How can I convert the private key stored in HSM to SignedXml.SigningKey in C#

前端 未结 2 1236
礼貌的吻别
礼貌的吻别 2021-01-06 23:12

I\'m trying to implement some demo of XML signing with a certificate which stored in the HSM.

I found some interesting example from this link: Sign XML Document with

相关标签:
2条回答
  • 2021-01-06 23:59

    You need to implement custom class inherited from System.Security.Cryptography.Xml.SignedXml like this

    public class CustomSignedXml: SignedXml
        {
        public CustomSignedXml(XmlDocument xmlDoc):base(xmlDoc)
        {
        }
        internal void ComputeSignature(ISignerProvider signerProvider)
        {
            var methodInfo = typeof (SignedXml).GetMethod("BuildDigestedReferences",
                BindingFlags.Instance | BindingFlags.NonPublic);
            methodInfo.Invoke(this, null);
            SignedInfo.SignatureMethod = XmlDsigRSASHA1Url;
            // See if there is a signature description class defined in the Config file
            SignatureDescription signatureDescription =
                CryptoConfig.CreateFromName(SignedInfo.SignatureMethod) as SignatureDescription;
            if (signatureDescription == null)
                throw new CryptographicException("Cryptography_Xml_SignatureDescriptionNotCreated");
    
            var hashAlg = signatureDescription.CreateDigest();
            if (hashAlg == null)
                throw new CryptographicException("Cryptography_Xml_CreateHashAlgorithmFailed");
            var methodInfo2 = typeof (SignedXml).GetMethod("GetC14NDigest", BindingFlags.Instance | BindingFlags.NonPublic);
            var hashvalue = (byte[]) methodInfo2.Invoke(this, new object[] {hashAlg});
    
            m_signature.SignatureValue = signerProvider.Sign(hashvalue);
        }
    }
    

    and then you need to create interface like this

    public interface ISignerProvider
    {
        byte[] Sign(byte[] data);
    }
    

    then implement it by Pkcs11Interop like this

        public class Pkcs11SignerProvider : ISignerProvider
    {
        private string _thumbprint;
        public string DllPath { get; set; }
        public string TokenSerial { get; set; }
        public string TokenPin { get; set; }
        public string PrivateKeyLabel { get; set; }
    
        public Pkcs11SignerProvider(string dllPath, string tokenSerial, string tokenPin, string privateKeyLabel)
        {
            DllPath = dllPath;
            TokenSerial = tokenSerial;
            TokenPin = tokenPin;
            PrivateKeyLabel = privateKeyLabel;
        }
    
        public byte[] Sign(byte[] data)
        {
            using (var pkcs11 = new Pkcs11(DllPath, AppType.SingleThreaded))
            {
    
                var slots = pkcs11.GetSlotList(SlotsType.WithTokenPresent);
                var slot = slots.FirstOrDefault(slot1 => slot1.GetTokenInfo().SerialNumber == TokenSerial);
                if (slot == null)
                    throw new Exception("there is no token with serial " + TokenSerial);
                using (var session = slot.OpenSession(SessionType.ReadOnly))
                {
                    session.Login(CKU.CKU_USER, TokenPin);
    
                    var searchTemplate = new List<ObjectAttribute>
                    {
                        new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY),
                        new ObjectAttribute(CKA.CKA_KEY_TYPE, CKK.CKK_RSA)
                    };
                    if (!string.IsNullOrEmpty(PrivateKeyLabel))
                        searchTemplate.Add(new ObjectAttribute(CKA.CKA_LABEL, PrivateKeyLabel));
    
                    var foundObjects = session.FindAllObjects(searchTemplate);
                    var privateKey = foundObjects.FirstOrDefault();
    
                    using (var mechanism = new Mechanism(CKM.CKM_RSA_PKCS))
                    {
                        return session.Sign(mechanism, privateKey, data);
                    }
    
                }
    
            }
        }
    
    }
    

    then call this method to sign xml

    public static void Sign(XmlDocument xmlDoc, ISignerProvider signerProvider)
        {
            if (xmlDoc == null)
                throw new ArgumentException("xmlDoc");
            if (xmlDoc.DocumentElement == null)
                throw new ArgumentException("xmlDoc.DocumentElement");
            var signedXml = new CustomSignedXml(xmlDoc);
            var reference = new Reference { Uri = "" };
            var env = new XmlDsigEnvelopedSignatureTransform();
            reference.AddTransform(env);
            signedXml.AddReference(reference);
            signedXml.ComputeSignature(signerProvider);
            var xmlDigitalSignature = signedXml.GetXml();
            xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));
        }
    

    and this code to verify

            public static bool Verify(XmlDocument document, X509Certificate2 certificate)
        {
            // Check arguments.
            if (document == null)
                throw new ArgumentException("Doc");
            if (certificate == null)
                throw new ArgumentException("Key");
    
            // Create a new SignedXml object and pass it
            // the XML document class.
            var signedXml = new SignedXml(document);
    
            // Find the "Signature" node and create a new
            // XmlNodeList object.
            var nodeList = document.GetElementsByTagName("Signature");
    
            // Throw an exception if no signature was found.
            if (nodeList.Count <= 0)
            {
                throw new CryptographicException("Verification failed: No Signature was found in the document.");
            }
    
            // This example only supports one signature for
            // the entire XML document.  Throw an exception 
            // if more than one signature was found.
            if (nodeList.Count >= 2)
            {
                throw new CryptographicException("Verification failed: More that one signature was found for the document.");
            }
    
            // Load the first <signature> node.  
            signedXml.LoadXml((XmlElement)nodeList[0]);
    
            return signedXml.CheckSignature(certificate, true);
        }
    
    0 讨论(0)
  • 2021-01-07 00:00

    You need to implement custom class inherited from System.Security.Cryptography.RSA class, use Pkcs11Interop in its implementation and then use instance of your custom class as a SigningKey.

    You can implement it yourself or you can use Pkcs11Interop.X509Store library which provides easy to use PKCS#11 based X.509 certificate store and contains Pkcs11RsaProvider class inherited from System.Security.Cryptography.RSA class. There's also a code sample available which demonstrates its usage with SignedXml class.

    0 讨论(0)
提交回复
热议问题