Pkcs11Exception:方法 C_GetSessionInfo 返回 CKR_CRYPTOKI_NOT_INITIALIZED

Pkcs11Exception: Method C_GetSessionInfo returned CKR_CRYPTOKI_NOT_INITIALIZED

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

我们写的逻辑如下:

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

方法C_GetSessionInfo返回CKR_CRYPTOKI_NOT_INITIALIZED

请帮忙。提前致谢。

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

我们创建了一个 class,它封装了所有 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 将应用程序定义为具有单个地址 space 和一个或多个控制线程 运行 的单个进程。

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

PKCS#11 函数 C_InitializeHighLevelAPI.Pkcs11 class 的构造函数中被调用,C_Finalize 函数在 HighLevelAPI.Pkcs11 [=28= 的实例中被调用] 被处置。 确保使用同一个 PKCS#11 库的 class 的两个实例不会相互重叠是至关重要的。 我的猜测是您使用了多个实例并且当你还在尝试使用另一个时,你就把它处理掉了。