如何将存储在HSM中的私钥转换为C#中的SignedXml.SigningKey

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

我在这个链接中找到了一个有趣的例子: 使用X509Certificate2签署XML文档并使用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 template = new List(); 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 foundObjects = session.FindAllObjects(template); ObjectHandle privateKey = foundObjects[0]; signedXml.SigningKey = privateKey; //Here is where I stuck. 

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

 signedXml.SigningKey = cert.PrivateKey; 

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

您需要实现从System.Security.Cryptography.Xml.SignedXmlinheritance的自定义类

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

并且这段代码要validation

  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  node. signedXml.LoadXml((XmlElement)nodeList[0]); return signedXml.CheckSignature(certificate, true); } 

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

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