3

我必须在 SOAP 响应中验证 xml 签名。我用Java得到它,但我必须用.Net来做,但我无法解决。这是完成这项工作的Java代码:

public class SigVal {
  private PublicKey publicKey;

  public XMLSignatureValidator(PublicKey publicKey) {
    this.publicKey = publicKey;
}
    public boolean isValid(Node dsSignature)
    {
        DOMValidateContext context = new DOMValidateContext(publicKey, dsSignature);
        String providerName = System.getProperty "jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");

        XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM", (Provider) Class
                 .forName(providerName).newInstance());
              XMLSignature signature = factory.unmarshalXMLSignature(context);
              boolean coreValidity = signature.validate(context);
    }
}

公钥来自从 wsse:BinarySecurityToken 生成的 X509Certificate。dsSignature 节点是 ds:Signature 元素。

这就是 SOAP 响应:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
    <SOAP-ENV:Header>
        <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 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-33637684">MII..==</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-23582192">
                        <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>VvfpzVGZkr/AQ3krGcdklXujj3w=</ds:DigestValue>
                    </ds:Reference>
                    <ds:Reference URI="#id-961374">
                        <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>KGBEkW1e0fkV3ooAptSIldv9ftQ=</ds:DigestValue>
                    </ds:Reference>
                </ds:SignedInfo>
                <ds:SignatureValue>
StF...=
                </ds:SignatureValue>
                <ds:KeyInfo Id="KeyId-12282214">
                    <wsse:SecurityTokenReference xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="STRId-314846">
                        <wsse:Reference URI="#CertId-33637684" 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-23582192">
                <wsu:Created>2013-06-06T07:08:19.323Z</wsu:Created>
                <wsu:Expires>2013-06-06T07:13:19.323Z</wsu:Expires>
            </wsu:Timestamp>
        </wsse:Security>
        <wsa:MessageID SOAP-ENV:mustUnderstand="0">uuid:dddca070-ce77-11e2-b9ac-d6977a2ed2bf</wsa:MessageID>
        <wsa:To SOAP-ENV:mustUnderstand="0">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
        <wsa:Action SOAP-ENV:mustUnderstand="0">...</wsa:Action>
        <From xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing" SOAP-ENV:mustUnderstand="0">
            <Address>...</Address>
        </From>
        <wsa:RelatesTo RelationshipType="wsa:Reply" SOAP-ENV:mustUnderstand="0">urn:uuid:3f4f070b-9e28-410d-b10b-a21a7d62ee86</wsa:RelatesTo>
    </SOAP-ENV:Header>
    <SOAP-ENV:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-961374">
        ...
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

好的,我在 C# 中尝试过

            XmlNodeList xmlNodeList = xmlDocument.GetElementsByTagName("wsse:BinarySecurityToken");
            string binarySecurityToken = xmlNodeList[0].InnerText;
            X509Certificate2 x509Certificate2 = new X509Certificate2(Convert.FromBase64String(binarySecurityToken));

            xmlNodeList = xmlDocument.GetElementsByTagName("Signature", "http://www.w3.org/2000/09/xmldsig#");
            XmlElement signature = (XmlElement) xmlNodeList[0];
            SignedXml signedXml = new SignedXml(xmlDocument);
            signedXml.LoadXml(signature);

            signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;

bool isOk = signedXml.CheckSignature();

但是 CheckSignature 总是返回 False。我覆盖了 SignedXml.GetIdElement 以获取参考元素。但这不能胜任。

我是否理解正确:我必须从 Soap 消息 (ds:Signature) 中获取签名元素,从 Soap 消息 (wsse:BinarySecurityToken) 中获取证书并验证它们。

4

1 回答 1

1

在处理 Java Web 服务时,这对我有用:

XmlDocument xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = true;
xmlDocument.LoadXml(xmlResponse);

XmlNodeList xmlNodeList = xmlDocument.GetElementsByTagName("wsse:BinarySecurityToken");
string binarySecurityToken = xmlNodeList[0].InnerText;
X509Certificate2 x509Certificate2 = new X509Certificate2(Convert.FromBase64String(binarySecurityToken));

xmlNodeList = xmlDocument.GetElementsByTagName("Signature", "http://www.w3.org/2000/09/xmldsig#");
XmlElement signature = (XmlElement)xmlNodeList[0];
SignedXmlWithId signedXml = new SignedXmlWithId(xmlDocument);
signedXml.LoadXml(signature);

bool isOk = signedXml.CheckSignature(x509Certificate2, true);

首先,您需要在将 xml 加载到对象之前设置PreserveWhitespace为。trueXmlDocument

如果签名元素的 id 具有命名空间前缀(如wsu:Id本例所示),SignedXml则失败,因此替代方法是对其进行子类化并覆盖该GetIdElement方法。

SignedXmlWithId课程来自这个答案

public class SignedXmlWithId : SignedXml
{
    public SignedXmlWithId(XmlDocument xml) : base(xml)
    {
    }

    public SignedXmlWithId(XmlElement xmlElement) 
        : base(xmlElement)
    {       
    }

    public override XmlElement GetIdElement(XmlDocument doc, string id)
    {
        // check to see if it's a standard ID reference
        XmlElement idElem = base.GetIdElement(doc, id);

        if (idElem == null)
        {
            XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
            nsManager.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");

            idElem = doc.SelectSingleNode("//*[@wsu:Id=\"" + id + "\"]", nsManager) as XmlElement;
        }

        return idElem;
    }
}    

最后,您需要将证书传递给该CheckSignature方法。

此外,您不需要设置CanonicalizationMethodSignature 的参数或其他参数 - 所有内容都已写入 XML。

由于 .NET 3.5 和 .NET 4.0 之间的规范化实现发生变化,有些人遇到了问题(请参阅此处获取解决方案),但我没有这个问题。

此外,请注意不要在检查签名之前对响应 XML 进行任何格式化。(起初,我使用编辑器将响应保存到 XML 文件中,并在测试时使用该 XML。这不起作用,但是当我直接从 Web 服务测试响应时,它起作用了。)

于 2018-04-24T18:15:45.753 回答