validar online firmar firma con certificado .net vb.net soap xml-signature

.net - online - Cómo verificar la firma XML digital



validar firma xml java (1)

Quiero verificar que el documento no se haya modificado de ninguna manera.

Desde un servicio web Java, recibo la siguiente respuesta de Soap con una firma XML digital:

<?xml version="1.0" encoding="UTF-8"?><Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" SOAP-ENV:mustUnderstand="1"> <wsse:BinarySecurityToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="CertId-38670273">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</wsse:BinarySecurityToken><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="#id-32516734"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <ds:DigestValue>Rx5L4j8kF5RVYnC+spUCdvhh5N0=</ds:DigestValue> </ds:Reference> <ds:Reference URI="#id-7716709"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <ds:DigestValue>fFr2j5DoKTgpEvX1Se7gTC55bWM=</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue> jG1BaGgNbbk9JCc3R6JsJKY56p++f0+8RM2aL6TGOXS34NAGv48Sp3iAHEAuUt9+JV6w3VDAcFct no3nCEISa0P4dVWTlPQJue3GVTWnnlcXao95tjukh9o8lIU7vZGgYHBUZLU+jgS6ZcaUlNW4KFUl AdrPxR5DmJcFyGEtRY2yclqYhnJdnUc+ZBu5eWbRZgbJzR4MgtGsEQcgtftFe2i0CvRbOSe4mt3T JQzbGY81ssFCnB44vitgjhVLfPd/08amSa5Xn8KRptbNatp2uq1iGXAifJLVup8T0yS0RzaqhCJg CaHRPFVKFN3WaJcqPZex75KBwSZMZaaJDZW7lQ== </ds:SignatureValue> <ds:KeyInfo Id="KeyId-16708261"> <wsse:SecurityTokenReference xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="STRId-32258670"><wsse:Reference URI="#CertId-38670273" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/></wsse:SecurityTokenReference> </ds:KeyInfo> </ds:Signature> <wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-32516734"><wsu:Created>2017-03-31T09:04:40.352Z</wsu:Created><wsu:Expires>2017-03-31T09:09:40.352Z</wsu:Expires></wsu:Timestamp></wsse:Security><wsa:MessageID xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" SOAP-ENV:mustUnderstand="0">uuid:12b93a00-15f1-11e7-af6b-f16d80f418ae</wsa:MessageID><wsa:To xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" SOAP-ENV:mustUnderstand="0">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To></SOAP-ENV:Header> <SOAP-ENV:Body xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-7716709"> ... </SOAP-ENV:Body></Envelope>

Encontré un ejemplo útil para verificar el archivo xml en MSDN :

'' Verify the signature of an XML file and return the result. Function VerifyXmlFile(ByVal Name As String) As [Boolean] '' Create a new XML document. Dim xmlDocument As New XmlDocument() '' Format using white spaces. xmlDocument.PreserveWhitespace = True '' Load the passed XML file into the document. xmlDocument.Load(Name) '' Create a new SignedXml object and pass it '' the XML document class. Dim signedXml As New SignedXml(xmlDocument) '' Find the "Signature" node and create a new '' XmlNodeList object. Dim nodeList As XmlNodeList = xmlDocument.GetElementsByTagName("Signature", "http://www.w3.org/2000/09/xmldsig#") '' Load the signature node. signedXml.LoadXml(CType(nodeList(0), XmlElement)) '' Check the signature and return the result. Return signedXml.CheckSignature() End Function

Lamentablemente, la verificación siempre devuelve falso. Me cuesta entender por qué no funciona.

ACTUALIZA 1 de acuerdo con las propuestas de Henk Holterman y Simon Mourier :

Function VerifyXmlFile(ByVal Name As String) As Boolean '' <SOAP-ENV:Envelope ... > ... </SOAP-ENV:Envelope> Dim xDoc = XDocument.Load(Name) '' <wsse:binarySecurityToken ... > ... </wsse:binarySecurityToken> Dim xBinarySecurityToken = xDoc.Root.Descendants().Skip(2).FirstOrDefault '' <SOAP-ENV:Body ... > ... </SOAP-ENV:Body> Dim xBody = xDoc.Root.Elements().Skip(1).FirstOrDefault Dim signedXml = New SignedXml(ToXmlElement(xBody)) Dim xmlDocument As New XmlDocument() xmlDocument.PreserveWhitespace = True xmlDocument.Load(Name) Dim nodeList = xmlDocument.GetElementsByTagName("Signature", "http://www.w3.org/2000/09/xmldsig#") signedXml.LoadXml(CType(nodeList(0), XmlElement)) Dim byteCert = Encoding.UTF8.GetBytes(xBinarySecurityToken.Value) Dim cert = New X509Certificates.X509Certificate2(byteCert) Return signedXml.CheckSignature(cert, True) End Function Public Shared Function ToXmlElement(xElement As XElement) As XmlElement Dim xmlDoc = New XmlDocument() With {.PreserveWhitespace = True} xmlDoc.Load(xElement.CreateReader()) Return xmlDoc.DocumentElement End Function

Resultó en una excepción CryptographicException con el mensaje " elemento de referencia mal formado " llamando a la función CheckSignature () .

ACTUALIZACIÓN 2 y SOLUCIÓN según las propuestas de @SimonMourier y @ lax1089

Private Function VerifyXmlfile(Name As String) As Boolean CryptoConfig.AddAlgorithm(GetType(MyXmlDsigC14NTransform), "http://www.w3.org/TR/2001/REC-xml-c14n-20010315") Dim xmlDocument As New XmlDocument() xmlDocument.PreserveWhitespace = True xmlDocument.Load(Name) MyXmlDsigC14NTransform.document = xmlDocument Dim soapBody As XmlElement = xmlDocument.GetElementsByTagName("SOAP-ENV:Body")(0) Dim securityToken = xmlDocument.GetElementsByTagName("SOAP-ENV:Header")(0).FirstChild.NextSibling.FirstChild.NextSibling.InnerText Dim signedXml = New SignedXmlWithId(soapBody) Dim nodeList = xmlDocument.GetElementsByTagName("Signature", "http://www.w3.org/2000/09/xmldsig#") signedXml.LoadXml(CType(nodeList(0), XmlElement)) Dim byteCert = Convert.FromBase64String(securityToken) Dim cert = New X509Certificates.X509Certificate2(byteCert) Return signedXml.CheckSignature(cert, True) End Function

Una clase de ayuda robada de Dog Ears

Public Class SignedXmlWithId Inherits SignedXml Public Sub New(xml As XmlDocument) MyBase.New(xml) End Sub Public Sub New(xmlElement As XmlElement) MyBase.New(xmlElement) End Sub Public Overrides Function GetIdElement(doc As XmlDocument, id As String) As XmlElement '' check to see if it''s a standard ID reference Dim idElem As XmlElement = MyBase.GetIdElement(doc, id) If idElem Is Nothing Then Dim nsManager As New XmlNamespaceManager(doc.NameTable) nsManager.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd") idElem = TryCast(doc.SelectSingleNode((Convert.ToString("//*[@wsu:Id=""") & id) + """]", nsManager), XmlElement) End If Return idElem End Function End Class

Créditos a @SimonMourier y @ CarlosLopez-MSFT


En realidad, este es un problema conocido y documentado:

La implementación de Canonicalization entre .NET 3.5 y .NET 4.0 ha cambiado.

No sé si esto funciona en todas las firmas XML, pero funciona a partir de las pruebas que he hecho.

Agregue la siguiente clase de transformación C14N a su proyecto:

public class MyXmlDsigC14NTransform: XmlDsigC14NTransform { static XmlDocument _document; public static XmlDocument document { set { _document = value; } } public MyXmlDsigC14NTransform() {} public override Object GetOutput() { return base.GetOutput(); } public override void LoadInnerXml(XmlNodeList nodeList) { base.LoadInnerXml(nodeList); } protected override XmlNodeList GetInnerXml() { XmlNodeList nodeList = base.GetInnerXml(); return nodeList; } public XmlElement GetXml() { return base.GetXml(); } public override void LoadInput(Object obj) { int n; bool fDefaultNS = true; XmlElement element = ((XmlDocument) obj).DocumentElement; if (element.Name.Contains("SignedInfo")) { XmlNodeList DigestValue = element.GetElementsByTagName("DigestValue", element.NamespaceURI); string strHash = DigestValue[0].InnerText; XmlNodeList nodeList = _document.GetElementsByTagName(element.Name); for (n = 0; n < nodeList.Count; n++) { XmlNodeList DigestValue2 = ((XmlElement) nodeList[n]).GetElementsByTagName("DigestValue", ((XmlElement) nodeList[n]).NamespaceURI); string strHash2 = DigestValue2[0].InnerText; if (strHash == strHash2) break; } XmlNode node = nodeList[n]; while (node.ParentNode != null) { XmlAttributeCollection attrColl = node.ParentNode.Attributes; if (attrColl != null) { for (n = 0; n < attrColl.Count; n++) { XmlAttribute attr = attrColl[n]; if (attr.Prefix == "xmlns") { element.SetAttribute(attr.Name, attr.Value); } else if (attr.Name == "xmlns") { if (fDefaultNS) { element.SetAttribute(attr.Name, attr.Value); fDefaultNS = false; } } } } node = node.ParentNode; } } base.LoadInput(obj); } }

Y registre la clase usando el método CryptoConfig.AddAlgorithm como se muestra a continuación:

CryptoConfig.AddAlgorithm(typeof(MyXmlDsigC14NTransform), "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"); var message = new XmlDocument(); message.PreserveWhitespace = true; message.Load("XmlSig.xml"); MyXmlDsigC14NTransform.document = message; // The transform class needs the xml document // Validate signature as normal.

Esto debería permitirle verificar correctamente una firma XML y resolver su problema.