如何将存储在 HSM 中的私钥转换为 C# 中的 SignedXml.SigningKey [英] How can I convert the private key stored in HSM to SignedXml.SigningKey in C#

查看:58
本文介绍了如何将存储在 HSM 中的私钥转换为 C# 中的 SignedXml.SigningKey的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用存储在 HSM 中的证书来实现一些 XML 签名演示.

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

我从这个链接中找到了一些有趣的例子:使用 X509Certificate2 签署 XML 文档 并将其修改为使用带有 PKCS11Interop 包装器的 HSM 中的证书和密钥.

I found some interesting example from this link: Sign XML Document with X509Certificate2 and modified it to using certificate and key inside the HSM with PKCS11Interop wrapper.

但是任何人都可以给我一个建议或示例,以将 ObjectHandle privateKey 从 HSM 转换为 SignedXML.SigningKey

But anyone could give me a suggestion or example to convert ObjectHandle privateKey from HSM to SignedXML.SigningKey

private static void SignXmlWithCertificate(XmlDocument xmlDoc, X509Certificate2 cert, Session session, String alias)
        {
            SignedXml signedXml = new SignedXml(xmlDoc);

            List<ObjectAttribute> template = new List<ObjectAttribute>();
            template.Add(new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY));
            template.Add(new ObjectAttribute(CKA.CKA_KEY_TYPE, CKK.CKK_RSA));
            template.Add(new ObjectAttribute(CKA.CKA_LABEL, alias));
            List<ObjectHandle> foundObjects = session.FindAllObjects(template);
            ObjectHandle privateKey = foundObjects[0];

            signedXml.SigningKey = privateKey; //Here is where I stuck.

在上述外部链接的示例中.他们使用结合了私钥的证书.然后他们就可以这样使用了.

In the example from above external link. They using a certificate which combined private key. Then they can use like this.

signedXml.SigningKey = cert.PrivateKey;

但是我使用的证书里面没有私钥的内容.请给我一些建议.

But the certificate that I'm using haven't content of private key inside. Please give me some suggestion.

推荐答案

您需要实现从 System.Security.Cryptography.Xml.SignedXml 像这样

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);
}

然后像这样通过Pkcs11Interop实现它

    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);
                }

            }

        }
    }

}

然后调用这个方法对xml进行签名

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));
    }

和这个验证码

        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);
    }

这篇关于如何将存储在 HSM 中的私钥转换为 C# 中的 SignedXml.SigningKey的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆