2012-08-31 17 views
5

¿Hay alguna manera de firmar un archivo XML con RSA y tener el prefijo del espacio de nombres "ds: Signature" en lugar de "Signature"? Pasé muchas horas intentando resolver esto y, por lo que veo, no hay solución.xml signature DS prefix?

Parece que está codificado en la clase System.Security.Cryptography.Xml.Signature.

XmlElement element = document.CreateElement("Signature", "http://www.w3.org/2000/09/xmldsig#"); 

Si alguien conoce una solución, necesito firmarlo así hacer que el software de importación se verifica con "DS: firma", por lo que con "DS" como prefijo el software verifica así:

public static bool VerifySignature(XmlDocument doc, RSA key, string prefix) 
    { 
     SignedXml xml = new SignedXml(doc); 
     string str = "Signature"; 
     if (!string.IsNullOrEmpty(prefix)) 
     { 
      str = string.Format("{0}:{1}", prefix, str); 
     } 
     XmlNodeList elementsByTagName = doc.GetElementsByTagName(str); 
     xml.LoadXml((XmlElement)elementsByTagName[0]); 
     return xml.CheckSignature(key); 
    } 

    VerifySignature(xmlDoc, rsa, "ds"); 

normalmente signos como este:

<kk>blabla<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /><DigestValue>rVL2nKjPTBhL9IDHYpu69OiE8gI=</DigestValue></Reference></SignedInfo><SignatureValue>CfXW9D/ErmHjzxIjy0/54/V3nst6j/XXcu7keR17LApfOZEpxjEvAlG3VnBZIi3jxQzU6t9RkmfDyngcRZccJByuuA6YDwFTQxZNRgu2GRoZxMKWnkm+MtQ0jH0Fo78GivCxV+iIewZvsrUQLzG01cXuZSH/k2eeMUaEooJaLQiYpO2aNVn5xbosTPtGlsACzFWz34E69/ZeeLZbXLc3jpDO+opxdYJ5e+Tnk/UM2Klt+N+m7Gh/sUNTPgkDiwP3q3y3O9tvCT0G2XmQaWBP4rw9TIoYHQtucm2b8R2JeggbeRKOetbRYV218RT8CK2Yuy0FIUlQXdabKyp9F96Yc55g8eNe10FGtgietH2iqquIVFLCA8fu3SZNLDPMoyHnVNKdBvI35+S8hrAaybEkMvo7iYnUSY5KrlGSfGGtfQXdaISutAzcnGPDFXgZXPNzNy7eL0u+Lt3yWWkj7wh6Zeh4fH2+nXDWYCWbLpegAEX4ZWSI5Ts6D1TplMJTGH1F0GyflehH4u+W4Lc3TvkB4dWjEuiKgnpl3hcvoj2CWFaeAxXMd/64tU/YMm8+1gSBjkVH6oV+QlI/m0z6M8FPVEVC2as0wLG2woVwmzVLcaQKyPi7NN4eO9ea7QNfaRHaofU4LQO/Y3FNJOP+uMfYlGJKWSr3qv29+BQjeNldNJY=</SignatureValue></Signature></kk> 

y necesito que lo haga de esta manera:

<kk>blabla<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /><ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /><ds:DigestValue>rVL2nKjPTBhL9IDHYpu69OiE8gI=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>CfXW9D/ErmHjzxIjy0/54/V3nst6j/XXcu7keR17LApfOZEpxjEvAlG3VnBZIi3jxQzU6t9RkmfDyngcRZccJByuuA6YDwFTQxZNRgu2GRoZxMKWnkm+MtQ0jH0Fo78GivCxV+iIewZvsrUQLzG01cXuZSH/k2eeMUaEooJaLQiYpO2aNVn5xbosTPtGlsACzFWz34E69/ZeeLZbXLc3jpDO+opxdYJ5e+Tnk/UM2Klt+N+m7Gh/sUNTPgkDiwP3q3y3O9tvCT0G2XmQaWBP4rw9TIoYHQtucm2b8R2JeggbeRKOetbRYV218RT8CK2Yuy0FIUlQXdabKyp9F96Yc55g8eNe10FGtgietH2iqquIVFLCA8fu3SZNLDPMoyHnVNKdBvI35+S8hrAaybEkMvo7iYnUSY5KrlGSfGGtfQXdaISutAzcnGPDFXgZXPNzNy7eL0u+Lt3yWWkj7wh6Zeh4fH2+nXDWYCWbLpegAEX4ZWSI5Ts6D1TplMJTGH1F0GyflehH4u+W4Lc3TvkB4dWjEuiKgnpl3hcvoj2CWFaeAxXMd/64tU/YMm8+1gSBjkVH6oV+QlI/m0z6M8FPVEVC2as0wLG2woVwmzVLcaQKyPi7NN4eO9ea7QNfaRHaofU4LQO/Y3FNJOP+uMfYlGJKWSr3qv29+BQjeNldNJY=</ds:SignatureValue></ds:Signature></kk> 
+0

¿No puedes modificar el contenido de DOM XML después de firmar todo y combinándolo en un árbol DOM? –

+1

si modifica el contenido xml después de firmar el valor de firma no coincidirá ... –

+0

el punto es modificar el nodo de la firma, no el firmado. –

Respuesta

8

si alguien sabe una solución, necesito firmarlo así hacer que el software de importación se verifica con "DS: firma", por lo que con "DS" prefijo

El prefijo debe carecer de importancia: todo lo que debería importar es en qué espacio de nombres está el elemento. No debería importar cómo se expresa ese espacio de nombre. Si lo hace, eso muestra un quebrantamiento en el código de verificación, diría yo.

Sin embargo, si realmente quieres hacer esto, ¿hay alguna razón por la que no quieras simplemente reemplazar el elemento por uno con el mismo contenido, pero usando el prefijo que deseas? No debería ser difícil hacer eso en LINQ to XML.

+2

edité la primera publicación para ver cómo lo verifica el software que recibe el XML ..., no puede reemplazar el prefijo de los elementos, porque entonces el valor de la firma sería diferente ... –

7

he encontrado la solución here

using System; 
using System.Reflection; 
using System.Security.Cryptography.Xml; 
using System.Security.Cryptography; 
using System.Collections.Generic; 
using System.Text; 
using System.Xml; 

namespace mysign 
{ 
public class PrefixedSignedXML : SignedXml 
{ 
    public PrefixedSignedXML(XmlDocument document) 
     : base(document) 
    { } 

    public PrefixedSignedXML(XmlElement element) 
     : base(element) 
    { } 

    public PrefixedSignedXML() 
     : base() 
    { } 

    public void ComputeSignature(string prefix) 
    { 
     this.BuildDigestedReferences(); 
     AsymmetricAlgorithm signingKey = this.SigningKey; 
     if (signingKey == null) 
     { 
      throw new CryptographicException("Cryptography_Xml_LoadKeyFailed"); 
     } 
     if (this.SignedInfo.SignatureMethod == null) 
     { 
      if (!(signingKey is DSA)) 
      { 
       if (!(signingKey is RSA)) 
       { 
        throw new CryptographicException("Cryptography_Xml_CreatedKeyFailed"); 
       } 
       if (this.SignedInfo.SignatureMethod == null) 
       { 
        this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; 
       } 
      } 
      else 
      { 
       this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; 
      } 
     } 
     SignatureDescription description = CryptoConfig.CreateFromName(this.SignedInfo.SignatureMethod) as SignatureDescription; 
     if (description == null) 
     { 
      throw new CryptographicException("Cryptography_Xml_SignatureDescriptionNotCreated"); 
     } 
     HashAlgorithm hash = description.CreateDigest(); 
     if (hash == null) 
     { 
      throw new CryptographicException("Cryptography_Xml_CreateHashAlgorithmFailed"); 
     } 
     this.GetC14NDigest(hash, prefix); 
     this.m_signature.SignatureValue = description.CreateFormatter(signingKey).CreateSignature(hash); 
    } 

    public XmlElement GetXml(string prefix) 
    { 
     XmlElement e = this.GetXml(); 
     SetPrefix(prefix, e); 
     return e; 
    } 

    //Invocar por reflexión al método privado SignedXml.BuildDigestedReferences 
    private void BuildDigestedReferences() 
    { 
     Type t = typeof(SignedXml); 
     MethodInfo m = t.GetMethod("BuildDigestedReferences", BindingFlags.NonPublic | BindingFlags.Instance); 
     m.Invoke(this, new object[] { }); 
    } 

    private byte[] GetC14NDigest(HashAlgorithm hash, string prefix) 
    { 
     //string securityUrl = (this.m_containingDocument == null) ? null : this.m_containingDocument.BaseURI; 
     //XmlResolver xmlResolver = new XmlSecureResolver(new XmlUrlResolver(), securityUrl); 
     XmlDocument document = new XmlDocument(); 
     document.PreserveWhitespace = true; 
     XmlElement e = this.SignedInfo.GetXml(); 
     document.AppendChild(document.ImportNode(e, true)); 
     //CanonicalXmlNodeList namespaces = (this.m_context == null) ? null : Utils.GetPropagatedAttributes(this.m_context); 
     //Utils.AddNamespaces(document.DocumentElement, namespaces); 

     Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject; 
     //canonicalizationMethodObject.Resolver = xmlResolver; 
     //canonicalizationMethodObject.BaseURI = securityUrl; 
     SetPrefix(prefix, document.DocumentElement); //establecemos el prefijo antes de se que calcule el hash (o de lo contrario la firma no será válida) 
     canonicalizationMethodObject.LoadInput(document); 
     return canonicalizationMethodObject.GetDigestedOutput(hash); 
    } 

    private void SetPrefix(string prefix, XmlNode node) 
    { 
     foreach (XmlNode n in node.ChildNodes) 
      SetPrefix(prefix, n); 
     node.Prefix = prefix; 
    } 
} 
} 
+0

esto no funcionó, el bloque de la firma terminó lo mismo sin el prefijo – tarrball

0

Puede ser correctamente SetPrefix código es el siguiente:

private void SetPrefix(String prefix, XmlNode node) { 
     foreach (XmlNode n in node.ChildNodes) 
     { 
      SetPrefix(prefix, n); 
      n.Prefix = prefix; 
     } 
    } 
+0

no entendiste la pregunta. Ya di sollution –

+1

@GeorgeDima Estoy jugando con esto, y la modificación del bloque de firma en el xml no parece afectar el signatureValue. Por lo tanto, debería ser seguro hacerlo después de que se haya generado la firma.¿Estás seguro de que no funcionó para ti? –

+1

Sí, solo mi solución publicada aquí funcionó para mí, quiero que se verifique. –

1

he probado todas estas soluciones, pero que no funcionó. Sin embargo, mirando el código fuente de .NET (http://referencesource.microsoft.com/) podemos ver que esto se puede llevar a cabo fácilmente al proporcionar una clase XmlDocument derivada a SignedXml, donde se puede agregar el espacio de nombres. Sin embargo, tener el prefijo "ds" dentro de "SignedInfo" y sus descendientes hará que la firma falle. Aquí es lo mejor que podría hacer sin romper la firma:


XmlDsigDocument.cs

using System; 
using System.Collections.Generic; 
using System.Security.Cryptography.Xml; 
using System.Text; 
using System.Xml; 

namespace CustomSecurity 
{ 
class XmlDsigDocument : XmlDocument 
{ 
    // Constants 
    public const string XmlDsigNamespacePrefix = "ds"; 

    /// <summary> 
    /// Override CreateElement function as it is extensively used by SignedXml 
    /// </summary> 
    /// <param name="prefix"></param> 
    /// <param name="localName"></param> 
    /// <param name="namespaceURI"></param> 
    /// <returns></returns> 
    public override XmlElement CreateElement(string prefix, string localName, string namespaceURI) 
    { 
     // CAntonio. If this is a Digital signature security element, add the prefix. 
     if (string.IsNullOrEmpty(prefix)) 
     { 
      // !!! Note: If you comment this line, you'll get a valid signed file! (but without ds prefix) 
      // !!! Note: If you uncomment this line, you'll get an invalid signed file! (with ds prefix within 'Signature' object) 
      //prefix = GetPrefix(namespaceURI); 

      // The only way to get a valid signed file is to prevent 'Prefix' on 'SignedInfo' and descendants. 
      List<string> SignedInfoAndDescendants = new List<string>(); 
      SignedInfoAndDescendants.Add("SignedInfo"); 
      SignedInfoAndDescendants.Add("CanonicalizationMethod"); 
      SignedInfoAndDescendants.Add("InclusiveNamespaces"); 
      SignedInfoAndDescendants.Add("SignatureMethod"); 
      SignedInfoAndDescendants.Add("Reference"); 
      SignedInfoAndDescendants.Add("Transforms"); 
      SignedInfoAndDescendants.Add("Transform"); 
      SignedInfoAndDescendants.Add("InclusiveNamespaces"); 
      SignedInfoAndDescendants.Add("DigestMethod"); 
      SignedInfoAndDescendants.Add("DigestValue"); 
      if (!SignedInfoAndDescendants.Contains(localName)) 
      { 
       prefix = GetPrefix(namespaceURI); 
      } 
     } 

     return base.CreateElement(prefix, localName, namespaceURI); 
    } 

    /// <summary> 
    /// Select the standar prefix for the namespaceURI provided 
    /// </summary> 
    /// <param name="namespaceURI"></param> 
    /// <returns></returns> 
    public static string GetPrefix(string namespaceURI) 
    { 
     if (namespaceURI == "http://www.w3.org/2001/10/xml-exc-c14n#") 
      return "ec"; 
     else if (namespaceURI == SignedXml.XmlDsigNamespaceUrl) 
      return "ds"; 

     return string.Empty; 
    } 
} 
} 

Esto se utiliza en la creación SignedXml:

// Create a new XML document. 
    XmlDsigDocument doc = new XmlDsigDocument(); 

    // Load the passed XML file using its name. 
    doc.Load(new XmlTextReader(FileName)); 

    // Create a SignedXml object. 
    SignedXml signedXml = new SignedXml(doc); 

Se puede ver archivos de código fuente completo en:

https://social.msdn.microsoft.com/Forums/en-US/cd595379-f66a-49c8-8ca2-62acdc58b252/add-prefixds-signedxml?forum=xmlandnetfx

0

Acepto que el prefijo no debe ser importante, pero ...

XML se convierte en mucho más fácil en C# si utiliza XPath:

var s = signedXml.GetXml(); 
XmlNodeList nodes = s.SelectNodes("descendant-or-self::*"); 
foreach (XmlNode childNode in nodes) 
{ 
    childNode.Prefix = "dsig"; 
} 
0

El código George Dima proporcionar obras.

Explicaré cómo funciona.

Cuando llame al método ComputeSignature esto generará el valor de la firma al digerir el valor del nodo SignedInfo.

El código proporcionado por George Dima agrega el prefijo al nodo SignedInfo y sus hijos ANTES de obtener el valor de resumen. Esto no va a añadir el prefijo a toda la estructura XML

este es el método que genera el valor de resumen del nodo SignedInfo

private byte[] GetC14NDigest(HashAlgorithm hash, string prefix) 
{   
    XmlDocument document = new XmlDocument(); 
    document.PreserveWhitespace = true; 
    XmlElement e = this.SignedInfo.GetXml(); 
    document.AppendChild(document.ImportNode(e, true));   
    Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject; 

    SetPrefix(prefix, document.DocumentElement); //HERE'S WHERE THE PREFIX IS ADDED TO GET THE DIGEST VALUE 
    canonicalizationMethodObject.LoadInput(document); 
    return canonicalizationMethodObject.GetDigestedOutput(hash); 
} 

Así que ahora tiene el valor de resumen del nodo SignedInfo con el prefijo , y este valor se puede utilizar para obtener el valor de firma, pero que todavía no tienen el xml con el prefijo sin embargo, así que si sólo hace esto

signedXml.GetXml(); 

obtendrá el XML sin el prefijo y de Por supuesto, porque el valor de la firma se calculó teniendo en cuenta el ds pre fijar tendrá una firma no válida por lo que lo que tiene que hacer es llamar al GetXml pasándole el valor del prefijo, en este caso, "DS" como esto

signedXml.GetXml("ds"); 
Cuestiones relacionadas