2

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

我从这个链接中找到了一些有趣的例子: Sign XML Document with X509Certificate2并将其修改为在带有 PKCS11Interop 包装器的 HSM 中使用证书和密钥。

但是任何人都可以给我一个建议或示例,将 ObjectHandle privateKey 从 HSM 转换为 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.

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

signedXml.SigningKey = cert.PrivateKey;

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

4

2 回答 2

6

您需要像这样实现从System.Security.Cryptography.Xml.SignedXml继承的自定义类

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

然后你需要创建这样的界面

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进行签名

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);
    }
于 2017-10-17T11:57:40.900 回答
1

您需要实现从类继承的自定义类System.Security.Cryptography.RSA,在其实现中使用 Pkcs11Interop,然后将自定义类的实例用作SigningKey.

您可以自己实现它,也可以使用Pkcs11Interop.X509Store库,该库提供易于使用的基于 PKCS#11 的 X.509 证书存储并包含Pkcs11RsaProvider从类继承的System.Security.Cryptography.RSA类。还有一个可用的代码示例演示了它与SignedXml类的用法。

于 2017-09-27T06:17:18.453 回答