2017-05-12 65 views
9

我想验证文档是否以任何方式不被修改。如何验证数字XML签名

从Java Web服务,我收到一个数字XML签名以下SOAP响应:

<?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> 

我发现一个有用的例子来验证MSDN的xml文件:

' 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 

不幸的是,验证总是返回false。我很难理解为什么它不起作用。


UPDATE 1根据从Henk HoltermanSimon Mourier的提案:通过调用函数CheckSignature

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 

CryptographicException消息 “格式不正确的参考元件” 导致( )


根据从@SimonMourier提案和@ 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 

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 

积分被盗@SimonMourier@CarlosLopez-MSFT

甲辅助类 UPDATE 2和SOLUTION
+1

'Dim signedXml As New SignedXml(xmlDocument)'部分选择整个文档作为签名部分。我想你需要选择那里的SOAP-ENV:Body。请注意id-7716709。但你需要确保文档。时间戳似乎也被签名,我不知道这是否可选。 –

+1

这是因为默认情况下,SignedXml没有看到任何公钥(它无法单独解码wsse:BinarySecurityToken(它是base64编码的X509证书)。但是,如果您创建的内容为wsse:BinarySecurityToken的X509Certificate解码为byte [],并将其传递给CheckSignature,它应该可以工作。但是,它仍然不适合我。也许是因为我没有所需的根证书(它来自瑞士的权威机构)...... –

+0

[link] https://msdn.microsoft.com/zh-cn/library/ms229950(v = vs.110).aspx在那里你可以看到一个密钥是如何生成的,并在你的算法中用它来查看是否缺少key作为'signedXml.CheckSignature(Key)'函数的参数与你的问题有关 – mago

回答

1

其实这是一个已知的和记录的问题:

.NET 3.5和.NET 4.0之间的规范化实施, 改变。

我不知道这是否适用于所有XML签名,但它可以在我所做的测试中使用。

添加以下C14N Transform类到您的项目:

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

并采用CryptoConfig.AddAlgorithm方法注册类,如下面所示:

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. 

这应该允许您正确的验证XML签名并解决您的问题。