Pkcs11异常: 方法C_GetSessionInfo返回CKR_CRYPTOKI_NOT_INITIALIZED



我们使用Thales nShield HSM来存储私钥,相应的公钥存储在证书存储中。

我们编写了如下逻辑:

  1. 搜索有效的插槽并在第一个插槽上打开一个会话 调用,它可以处理多个请求。
  2. 半年后过期 小时。在它未过期的时间内,如果它收到任何请求 服务。
  3. 现在即使它没有过期,当我们尝试检查 会话信息它给出以下消息:

方法C_GetSessionInfo返回CKR_CRYPTOKI_NOT_INITIALIZED

请帮忙。提前谢谢。

以下是对上述查询的补充。

我们创建了一个类,它封装了所有 Pkcs11Interop 用法,并公开了一些方法,如下所示。

/// <summary>
/// Contains the information about Private key stored in HMS and Certificate to load from File System/Windows Certificates Store/HSM.
/// </summary>
public class HardwareSecureModule
{
/// <summary>
/// CryptoApi reference
/// </summary>
public string CryptoApiPath { get; set; }
/// <summary>
/// Idenfitier of the Private Key
/// </summary>
public string KeyLabel { get; set; }
/// <summary>
/// Idenfitier type of the Private Key
/// </summary>
public string KeyIdentifier { get; set; }
/// <summary>
/// Idenfitier of the Token
/// </summary>
public string TokenLabel { get; set; }
/// <summary>
/// Token Pin 
/// </summary>
public string TokenPin { get; set; }
/// <summary>
/// Idenfitier of the Certificate
/// </summary>
public string CertificateLabel { get; set; }        
}
public interface IHsmSession : IDisposable
{
/// <summary>
/// Find key encryption algorithm
/// </summary>
/// <returns></returns>
string GetEncryptionAlgorithm();
/// <summary>
/// sign the digest
/// </summary>
/// <param name="digest"></param>
/// <returns></returns>
byte[] Sign(byte[] digest, string encryptionAlgorithm, string hashAlgorithm);
/// <summary>
/// Indicates if thread within the pool is working
/// to avoid disposal of the same
/// </summary>
bool Locked { get; set; }
/// <summary>
/// Unique identifier of the HSM Session
/// </summary>
Guid Id { get; }
}
/// <summary>
/// Class for communicating with HSM
/// </summary>
public class Pkcs11HsmSession : IHsmSession
{
private Pkcs11 _pkcs11;
private Slot _slot;
private Session _session;
private readonly HardwareSecureModule _certificateInformation = null;
public bool Locked { get; set; }
public Guid Id { get; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="certificateInformation"></param>
public Pkcs11HsmSession(HardwareSecureModule certificateInformation)
{
Id = Guid.NewGuid();
_certificateInformation = certificateInformation;
if (_certificateInformation != null)
InitializeVariables();
}
private void InitializeVariables()
{
_pkcs11 = GetPkcs11Instance(_certificateInformation.CryptoApiPath);
if (_pkcs11 == null)
throw new Exception("Unable to create instance of Pkcs11");
_slot = FindSlot(_pkcs11, _certificateInformation.TokenLabel);
if (_slot == null)
throw new Exception("Specified token not found: " + _certificateInformation.TokenLabel);
_session = _slot.OpenSession(true);
if (_session == null)
throw new Exception("Unable to create session for the slot");
SessionLogin();            
}
private Pkcs11 GetPkcs11Instance(string hsmCryptoApi)
{
Pkcs11 pkcs11 = null;
try
{
pkcs11 = CreatePkcs11Instance(hsmCryptoApi, true);
}
catch (Pkcs11Exception ex)
{
if (ex.RV == CKR.CKR_CANT_LOCK)
pkcs11 = CreatePkcs11Instance(hsmCryptoApi, false);
else
throw ex;
}
return pkcs11;
}
private Pkcs11 CreatePkcs11Instance(string hsmCryptoApi, bool useOsLocking)
{
return new Pkcs11(hsmCryptoApi, useOsLocking);
}
private Slot FindSlot(Pkcs11 pkcs11, string tokenLabel)
{
if (string.IsNullOrEmpty(tokenLabel))
throw new Exception("Token label is not specified");
List<Slot> slots = pkcs11.GetSlotList(true);            
if (slots != null && slots.Count > 0)
{
foreach (Slot slot in slots)
{
TokenInfo tokenInfo = null;
try
{
tokenInfo = slot.GetTokenInfo();                        
}
catch (Pkcs11Exception ex)
{
if (ex.RV != CKR.CKR_TOKEN_NOT_RECOGNIZED && ex.RV != CKR.CKR_TOKEN_NOT_PRESENT)
throw;
}
if (tokenInfo == null)
continue;
if (!string.IsNullOrEmpty(tokenLabel))
if (0 !=
String.Compare(tokenLabel, tokenInfo.Label, StringComparison.InvariantCultureIgnoreCase))
continue;
return slot;
}
}
return null;
}
/// <summary>
/// HSM Signs the digest using private key 
/// </summary>
/// <param name="message"></param>
/// <param name="encryptionAlgorithm"></param>
/// <param name="hashAlgorithm"></param>
/// <returns></returns>
public virtual byte[] Sign(byte[] message, string encryptionAlgorithm, string hashAlgorithm)
{
hashAlgorithm = hashAlgorithm.Replace("-", string.Empty);
CKM signingMechanismType = GetSigningMechanismType(encryptionAlgorithm, hashAlgorithm);
SessionLogin();
ObjectHandle privateKeyHandle = GetPrivateKeyHandle();
if (signingMechanismType == CKM.CKM_ECDSA)
{
message = GetMessageDigest(message, hashAlgorithm);
}
using (Mechanism mechanism = new Mechanism(signingMechanismType))
{
byte[] signedHash = _session.Sign(mechanism, privateKeyHandle, message);
if (signingMechanismType == CKM.CKM_ECDSA)
{
return ConstructEcdsaSigValue(signedHash);
}
return signedHash;
}
}
private byte[] GetMessageDigest(byte[] message, string hashAlgorithm)
{
CKM hashMechanismType = (CKM)Enum.Parse(typeof(CKM), "CKM_" + hashAlgorithm.ToUpper());
using (Mechanism mechanism = new Mechanism(hashMechanismType))
{
return _session.Digest(mechanism, message);
}
}
/// <summary>
/// Construct ECDSA der sequence
/// </summary>
/// <param name="rs"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
public byte[] ConstructEcdsaSigValue(byte[] rs)
{
if (rs == null)
throw new ArgumentNullException("rs is null");
if (rs.Length < 2 || rs.Length % 2 != 0)
throw new ArgumentException("Invalid length of rs byte");
int halfLen = rs.Length / 2;
byte[] half1 = new byte[halfLen];
Array.Copy(rs, 0, half1, 0, halfLen);
var r = new BigInteger(1, half1);
byte[] half2 = new byte[halfLen];
Array.Copy(rs, halfLen, half2, 0, halfLen);
var s = new BigInteger(1, half2);
var derSequence = new Org.BouncyCastle.Asn1.DerSequence(
new Org.BouncyCastle.Asn1.DerInteger(r),
new Org.BouncyCastle.Asn1.DerInteger(s));
return derSequence.GetDerEncoded();
}
/// <summary>
/// GetEncryptionAlgorithm for Interface
/// </summary>
/// <returns></returns>
public string GetEncryptionAlgorithm()
{
SessionLogin();
string objectAttributeValue = GetObjectAttribute().ToString();            
switch ((CKK)Enum.Parse(typeof(CKK), objectAttributeValue))
{
case CKK.CKK_RSA:
return "RSA";
case CKK.CKK_ECDSA: //CKK.CKK_EC has same value as CKK.CKK_ECDSA:
return "ECDSA";
default:
throw new Exception("Unknown Encryption Algorithm");
}
}
/// <summary>
/// Get atrributes for object handle
/// </summary>
/// <returns></returns>
private ulong GetObjectAttribute()
{
ObjectHandle objectHandle = GetPrivateKeyHandle();
List<CKA> keyAttributes = new List<CKA>();
keyAttributes.Add(CKA.CKA_KEY_TYPE);
List<ObjectAttribute> keyObjectAttributes = _session.GetAttributeValue(objectHandle, keyAttributes);
return keyObjectAttributes[0].GetValueAsUlong();
}
/// <summary>
/// Extract private key handle from HSM
/// </summary>
/// <returns></returns>
private ObjectHandle GetPrivateKeyHandle()
{
_logger.WriteTrace("Inside GetPrivateKeyHandle()", LogCategory.General);
string keyLabel = _certificateInformation.KeyLabel;
string keyIdentifier = _certificateInformation.KeyIdentifier;
List<ObjectAttribute> searchTemplate = new List<ObjectAttribute>();
searchTemplate.Add(new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY));
CKA indentifierType;
bool parseResult = Enum.TryParse(keyIdentifier, out indentifierType);
if (!parseResult)
throw new Exception("Invalid Key Identifier '" + keyIdentifier + "'. Please provide a valid value (CKA_ID, CKA_LABEL etc).");
searchTemplate.Add(new ObjectAttribute(indentifierType, keyLabel));
List<ObjectHandle> foundObjects = _session.FindAllObjects(searchTemplate);
if (foundObjects.Count < 1)
{
throw new Exception(string.Format("Private key with {0} '{1}' was not found", keyIdentifier, keyLabel));
}
else if (foundObjects.Count > 1)
{
throw new Exception(string.Format("More than one private key with {0} '{1}' was found", keyIdentifier, keyLabel));
}
return foundObjects[0];
}
/// <summary>
/// Get MechanismType CKM for Ecdsa
/// </summary>
/// <param name="hashAlgorithm"></param>
/// <returns></returns>
private CKM GetEcdsaMechanismType(string hashAlgorithm)
{
switch (hashAlgorithm)
{
//Currently we don't have direct support for the below mechanism in HSM, however if supported this code can be uncommented and used
//case "SHA1":
//    return CKM.CKM_ECDSA_SHA1;
//case "SHA224":
//    return CKM.CKM_ECDSA_SHA224;
//case "SHA256":
//    return CKM.CKM_ECDSA_SHA256;
//case "SHA384":
//    return CKM.CKM_ECDSA_SHA384;
//case "SHA512":
//    return CKM.CKM_ECDSA_SHA512;
default:
return CKM.CKM_ECDSA;
}
}
/// <summary>
/// Get CKM based upon hash algorithm
/// </summary>
/// <param name="hashAlgorithm"></param>
/// <returns></returns>
private CKM GetRsaMechanismType(string hashAlgorithm)
{
switch (hashAlgorithm)
{
case "SHA512":
return CKM.CKM_SHA512_RSA_PKCS;
case "SHA256":
default:
return CKM.CKM_SHA256_RSA_PKCS;
}
}
/// <summary>
/// Get CKM based on encryption and hash algorithm
/// </summary>
/// <param name="encryptionAlgorithm"></param>
/// <param name="hashAlgorithm"></param>
/// <returns></returns>
private CKM GetSigningMechanismType(string encryptionAlgorithm, string hashAlgorithm)
{
switch (encryptionAlgorithm)
{
case "EC":
case "ECDSA":
return GetEcdsaMechanismType(hashAlgorithm);
case "RSA":
default:
return GetRsaMechanismType(hashAlgorithm);
}
}
private void CloseSession()
{
if (_session != null)
{
try
{
SessionLogout();
}
catch
{
// Any exceptions can be safely ignored here
}
_session.Dispose();
_session = null;
}
_slot = null;
if (_pkcs11 != null)
{
_pkcs11.Dispose();
_pkcs11 = null;
}
}
public void Dispose()
{
CloseSession();
}
private void SessionLogout()
{
if (_session != null && GetSessionState() == CKS.CKS_RO_USER_FUNCTIONS)
{
ulong sessionId = _session.SessionId;
_session.Logout();                
}
}
private void SessionLogin()
{
if (_session != null && GetSessionState() != CKS.CKS_RO_USER_FUNCTIONS)
{
_session.Login(CKU.CKU_USER, _certificateInformation.TokenPin);                
}
}
private CKS GetSessionState()
{
try
{
return _session.GetSessionInfo().State;
}
catch (Exception ex)
{
if (_certificateInformation != null)
InitializeVariables();
return _session.GetSessionInfo().State;
}
}
}

PKCS#11 将应用程序定义为具有单个地址空间和在其中运行的一个或多个控制线程的单个进程。

任何应用程序都通过调用C_Initialize函数在其一个线程中初始化PKCS#11库来成为"Cryptoki应用程序"。初始化库后,应用程序可以调用 PKCS#11 API 的其他函数。当应用程序使用 PKCS#11 API 完成时,它会通过调用C_Finalize函数来完成 PKCS#11 库,并且不再是"Cryptoki 应用程序"。从应用程序的角度来看,PKCS#11 库初始化和完成是全局事件,因此确保一个线程不会完成库而其他线程仍在使用它至关重要。

PKCS#11 函数C_Initialize在类的构造函数中调用HighLevelAPI.Pkcs11C_Finalize并且在释放HighLevelAPI.Pkcs11类的实例时调用该函数。确保使用同一 PKCS#11 库的此类的两个实例不会相互重叠至关重要。我的猜测是,您正在使用多个实例,并且在仍在尝试使用另一个实例时将其处理掉。

最新更新