我们使用Thales nShield HSM存储私钥,相应的公钥存储在证书存储中。
我们编写了如下逻辑:
方法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;
}
}
}
答案 0 :(得分:1)
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.Pkcs11
类的构造函数中被调用,而C_Finalize
函数在处理HighLevelAPI.Pkcs11
类的实例时被调用。 至关重要的一点是,确保此类的两个实例与同一PKCS#11库一起使用不会相互重叠。我的猜测是,您使用了多个实例,并且在您仍在使用时将其处置尝试使用另一个。