2017-09-27 7 views
2

J'essaie d'implémenter une démo de signature XML avec un certificat stocké dans le HSM.Comment puis-je convertir la clé privée stockée dans HSM en SignedXml.SigningKey en C#

J'ai trouvé un exemple intéressant à partir de ce lien: Sign XML Document with X509Certificate2 et l'ai modifié pour utiliser le certificat et la clé dans le module HSM avec l'encapsuleur PKCS11Interop.

Mais quelqu'un pouvait me donner une suggestion ou par exemple pour convertir ObjectHandle privateKey de 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. 

Dans l'exemple ci-dessus lien externe. Ils utilisent un certificat qui combine la clé privée. Ensuite, ils peuvent utiliser comme ça. Mais le certificat que j'utilise n'a pas contenu de clé privée à l'intérieur. S'il vous plaît donnez-moi quelques suggestions.

Répondre

0

Vous devez implémenter la classe personnalisée héritée de la classe System.Security.Cryptography.RSA, utiliser Pkcs11Interop dans son implémentation, puis utiliser l'instance de votre classe personnalisée en tant que SigningKey.

Vous pouvez implémenter vous-même ou vous pouvez utiliser la bibliothèque Pkcs11Interop.X509Store qui fournit facile à utiliser PKCS # 11 à base de magasin de certificats X.509 et contient Pkcs11RsaProvider classe héritée de System.Security.Cryptography.RSA classe. Il y a aussi un code sample disponible qui démontre son utilisation avec la classe SignedXml.

+0

Salut Jariq, Vous voulez dire que je dois créer une nouvelle classe héritée avec toutes les propriétés et méthodes comme droit de classe RSA? Où puis-je mettre la clé privée? Pourriez-vous s'il vous plaît me guider plus ou donner un bref exemple de code. – ktikar

+0

@ktikar Oui, vous devez créer une classe héritée - la partie la plus importante consiste à remplacer la méthode 'SignHash' et à l'implémenter avec Pkcs11Interop. Je prévois de publier une bibliothèque autonome avec une telle mise en œuvre, mais je suis actuellement occupé avec d'autres projets rémunérés. – jariq

+0

Nous vous remercions de votre suggestion. Je vais l'essayer. – ktikar

1

Vous devez mettre en œuvre classe personnalisée héritée de System.Security.Cryptography.Xml.SignedXml comme celui-ci

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

et vous devez créer une interface comme celui-ci

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

puis la mettre en œuvre par Pkcs11Interop comme celui-ci

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

      } 

     } 
    } 

} 

puis appelez cette méthode pour signer 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)); 
    } 

et ce code pour vérifier

 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

Salut Gholamreza, J'apprécie vraiment votre aimable aide. Je vais l'essayer. – ktikar