0

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

我们写的逻辑如下:

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

方法 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;
            }
        }
    }
4

1 回答 1

2

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

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

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

于 2018-06-20T19:06:52.620 回答