Pkcs11Exception:方法 C_GetSessionInfo 返回 CKR_CRYPTOKI_NOT_INITIALIZED

问题描述 投票:0回答:2

我们使用 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;
            }
        }
    }
digital-signature pkcs#11 hsm pkcs11interop softhsm
2个回答
2
投票

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 库的此类的两个实例不会相互重叠,这一点至关重要。我的猜测是,您正在使用多个实例,并且在您仍在尝试使用另一个实例时将其处置掉.


0
投票

我更换了 USB 插槽而不是连接了令牌,问题就解决了。

© www.soinside.com 2019 - 2024. All rights reserved.