微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何配置 SignedXML 以对带有前缀和其他仪式的 XML 文档进行签名?

如何解决如何配置 SignedXML 以对带有前缀和其他仪式的 XML 文档进行签名?

我需要使用 .NET Core 和 SignedXml 使用证书对 XML 文档进行签名,但我不知道如何在签名标签中配置对方期望的一些元素。我检查了很多文章one of this,authored by Rick Stahl 非常相似,但我仍然验证了错误。不幸的是,我无法发送任何好的日志消息,我只有一个笼统的答案,即签名无效。

签名的预期内容如下所示。看起来所有细节 - 标签、前缀、标识符、架构 - 都是必需的。

        <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
            <ds:Signature Id="{{someValue}}" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <ds:SignedInfo>
                    <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                        <ec:InclusiveNamespaces PrefixList="soapenv urn urn1 urn2 urn3 urn4" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                    </ds:CanonicalizationMethod>
                    <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
                    <ds:Reference URI="#{{someValue}}">
                        <ds:Transforms>
                            <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                                <ec:InclusiveNamespaces PrefixList="urn urn1 urn2 urn3 urn4" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                            </ds:Transform>
                        </ds:Transforms>
                        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                        <ds:DigestValue>VALUEOFDIGIESTVALUE</ds:DigestValue>
                    </ds:Reference>
                </ds:SignedInfo>
                <ds:SignatureValue>VALUEOFSIGNATUREVALUE</ds:SignatureValue>
                <ds:KeyInfo Id="{{someValue}}">
                    <wsse:SecurityTokenReference wsu:Id="{{someValue}}">
                        <ds:X509Data>
                            <ds:X509IssuerSerial>
                                <ds:X509IssuerName>{{someValue}}</ds:X509IssuerName>
                                <ds:X509SerialNumber>{{someValue}}</ds:X509SerialNumber>
                            </ds:X509IssuerSerial>
                        </ds:X509Data>
                    </wsse:SecurityTokenReference>
                </ds:KeyInfo>
            </ds:Signature>
        </wsse:Security>

每个提示都有帮助,我不希望得到全面的答案,但我最感兴趣的是以下问题:

  • 如何定义前缀 dsec
  • 如何配置 wsse:SecurityTokenReference
  • 在哪里可以找到所有标识符,标记{{someValue}}

在与 SignedXml 战斗两周后,我需要在 2021 年确认相同的状态,即 described by Rick Stahl in 2008

难怪签名和证书加密的安全性在 Windows 上被如此轻易地使用。这是一个令人头疼的问题,有可怕的记录,需要大量工具。

虽然Java县的同事说这个在国外简单很多,但我认为问题是SOAP服务的规范过于复杂。

解决方法

这是基于提到的 Rick Stahl's article 的部分解决方案。假设我们有要签名的 XML 文档:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP:Envelope xmlns:r1="http://www.routeone.com/namespace.messaging.diag#"
               xmlns:star="http://www.starstandards.org/STAR"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:oa="http://www.openapplications.org/oagis">
  <SOAP:Body>
    <!-- data to be signed here -->
  </SOAP:Body>
</SOAP:Envelope>

使用该代码:

using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Xml;

namespace XmlSigner
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create a new XML document.
            var doc = new XmlDocument();

            // Format the document to ignore white spaces.
            doc.PreserveWhitespace = false;

            // Load the passed XML file using it's name.
            doc.Load(new XmlTextReader("C:\\Repos\\request.xml"));

            // Initialize the certificate.
            var certificate = new X509Certificate2("C:\\Repos\\certificate.pfx","youdontguessthispassword");

            // Sign the document
            var signedDoc = SignSoapBody(doc,certificate);

            // Write to the output file.
            File.WriteAllText("C:\\Repos\\signed.xml",signedDoc.OuterXml);

            // Validate the result.
            if (ValidateSoapBodySignature(signedDoc,certificate))
                Console.WriteLine("Everything looks good,keep going.");
            else
                Console.WriteLine("You've screwed,back to work.");
        }

        private const string STR_SOAP_NS = "http://schemas.xmlsoap.org/soap/envelope/";
        private const string STR_SOAPSEC_NS = "http://schemas.xmlsoap.org/soap/security/2000-12";

        /// <summary>
        /// Signs the SOAP document and adds a digital signature to it.
        /// 
        /// Note a lot of optional settings are applied against
        /// key and certificate info to match the required XML document
        /// structure the server requests.
        /// </summary>
        /// <param name="xmlDoc"></param>
        /// <param name="certFriendlyName">Friendly Name of Cert installed in the Certificate Store under CurrentUser | Personal</param>
        /// <returns></returns>
        static public XmlDocument SignSoapBody(XmlDocument xmlDoc,X509Certificate2 cert)
        {
            // Add search Namespaces references to ensure we can reliably work 
            // against any SOAP docs regardless of tag naming
            XmlNamespaceManager ns = new XmlNamespaceManager(xmlDoc.NameTable);
            ns.AddNamespace("SOAP",STR_SOAP_NS);
            ns.AddNamespace("SOAP-SEC",STR_SOAPSEC_NS);

            // Grab the body element - this is what we create the signature from
            XmlElement body = xmlDoc.DocumentElement.SelectSingleNode(@"//SOAP:Body",ns) as XmlElement;
            if (body == null)
                throw new ApplicationException("No body tag found");

            // We'll only encode the <SOAP:Body> - add id: Reference as #Body
            var bodyId = $"id-{Guid.NewGuid()}";
            body.SetAttribute("id",bodyId);

            // Signed XML will create Xml Signature - Xml fragment
            SignedXml signedXml = new SignedXml(xmlDoc);
            signedXml.Signature.Id = $"sid-{Guid.NewGuid()}";
            signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";

            // Create a KeyInfo structure
            KeyInfo keyInfo = new KeyInfo();
            keyInfo.Id = $"kid-{Guid.NewGuid()}";

            // The actual key for signing - MAKE SURE THIS ISN'T NULL!
            signedXml.SigningKey = cert.PrivateKey;

            // Specifically use the issuer and serial number for the data rather than the default
            KeyInfoX509Data keyInfoData = new KeyInfoX509Data();
            keyInfoData.AddIssuerSerial(cert.Issuer,cert.GetSerialNumberString());
            keyInfo.AddClause(keyInfoData);


            // provide the certficate info that gets embedded - note this is only
            // for specific formatting of the message to provide the cert info
            signedXml.KeyInfo = keyInfo;

            // Again unusual - meant to make the document match template
            signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;

            // Now create reference to sign: Point at the Body element
            Reference reference = new Reference();
            reference.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1";
            reference.Uri = $"#{bodyId}";  // reference id=body section in same doc
            reference.AddTransform(new XmlDsigExcC14NTransform());  // required to match doc
            signedXml.AddReference(reference);

            // Finally create the signature
            signedXml.ComputeSignature();

            // Result is an XML node with the signature detail below it
            // Now let's add the sucker into the SOAP-HEADER
            XmlElement signedElement = signedXml.GetXml();

            // Create SOAP-SEC:Signature element
            XmlElement soapSignature = xmlDoc.CreateElement("Security",STR_SOAPSEC_NS);
            soapSignature.Prefix = "wsse";
            //soapSignature.SetAttribute("MustUnderstand","","1");

            // And add our signature as content
            soapSignature.AppendChild(signedElement);

            // Now add the signature header into the master header
            XmlElement soapHeader = xmlDoc.DocumentElement.SelectSingleNode("//SOAP:Header",ns) as XmlElement;
            if (soapHeader == null)
            {
                soapHeader = xmlDoc.CreateElement("Header",STR_SOAP_NS);
                soapHeader.Prefix = "SOAP";
                xmlDoc.DocumentElement.InsertBefore(soapHeader,xmlDoc.DocumentElement.ChildNodes[0]);
            }
            soapHeader.AppendChild(soapSignature);

            return xmlDoc;
        }

        /// <summary>
        /// Validates the Xml Signature in a document.
        /// 
        /// This routine is significantly simpler because the key parameters
        /// are embedded into the signature itself. All that's needed is a
        /// certificate to provide the key - the rest can be read from the
        /// Signature itself.
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="publicCertFileName"></param>
        /// <returns></returns>
        static public bool ValidateSoapBodySignature(XmlDocument doc,X509Certificate2 cert)
        {
            // Load the doc this time
            SignedXml sdoc = new SignedXml(doc);

            // Find the signature and load it into SignedXml
            XmlNodeList nodeList = doc.GetElementsByTagName("Signature");
            sdoc.LoadXml((XmlElement)nodeList[0]);

            // Now read the actual signature and validate
            bool result = sdoc.CheckSignature(cert,true);

            return result;
        }
    }
}

我有这个结果:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP:Envelope xmlns:r1="http://www.routeone.com/namespace.messaging.diag#" xmlns:star="http://www.starstandards.org/STAR" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/" xmlns:oa="http://www.openapplications.org/oagis">
    <SOAP:Header>
        <wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/soap/security/2000-12">
            <Signature Id="sid-f3d74053-b47a-4740-b105-74ba1a550d38" 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="#id-9718651b-c7a4-47a0-bcd1-f89e1a5f8395">
                        <Transforms>
                            <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                        </Transforms>
                        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                        <DigestValue>SOME_SECRET_VALUES</DigestValue>
                    </Reference>
                </SignedInfo>
                <SignatureValue>SOME_SECRET_VALUES</SignatureValue>
                <KeyInfo Id="kid-626cac63-9f43-46a9-80e3-ed350ef6cea2">
                    <X509Data>
                        <X509IssuerSerial>
                            <X509IssuerName>CN=XX,OU=XX,O=XX,L=XX,C=XX</X509IssuerName>
                            <X509SerialNumber>SOME_SECRET_VALUES</X509SerialNumber>
                        </X509IssuerSerial>
                    </X509Data>
                </KeyInfo>
            </Signature>
        </wsse:Security>
    </SOAP:Header>
    <SOAP:Body id="id-9718651b-c7a4-47a0-bcd1-f89e1a5f8395"> <!-- data to be signed here -->
    </SOAP:Body>
</SOAP:Envelope>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?