如何调用.Net Core需要签名的XML SOAP服务?

2024-04-13

我意识到这个问题指的是旧技术。我正在呼叫供应商系统,并且无法更改服务。我们需要调用 XML/SOAP WS,然后签署请求。 10 年前,我会使用 Web 服务增强 (WSE) 3.0 之类的东西,然后继续前进。就像今天一样,我陷入了在 .Net Core(.Net Standard 2.0)应用程序中该做什么的困境。

我愿意使用多种解决方案,包括商业解决方案。我看着奇尔卡特,但似乎我们要放弃太多才能使用它。

不过,他们确实有一个很好的例子来说明我所指的内容。

给出如下请求:

<?xml version="1.0" encoding="UTF8"?>
<SOAP-ENV:Envelope xmlns:SOAPENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
 <wsse:Security xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
            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"
            xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" SOAP-ENV:mustUnderstand="1">
 <wsse:BinarySecurityToken
           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#X509"
           wsu:Id="x509cert00">BASE64_CERT</wsse:BinarySecurityToken>
 </wsse:Security>
</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="TheBody">
 <getVersion xmlns="http://msgsec.wssecfvt.ws.ibm.com"/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

我们希望能够使用证书并对其进行签名,如下所示:

<?xml version="1.0" encoding="UTF8" ?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header>
        <wsse:Security xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 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" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" SOAP-ENV:mustUnderstand="1">
            <wsse:BinarySecurityToken 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#X509" wsu:Id="x509cert00">MIIDgzCCAmugAwIBAgIBADANBgkqhkiG9w0BAQUFADBcMRUwEwYDVQQDDAxUZXN0
IENvbXBhbnkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD
VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTcwOTEzMDA1NTM1WhcN
MTgwOTEzMDA1NTM1WjBcMRUwEwYDVQQDDAxUZXN0IENvbXBhbnkxCzAJBgNVBAYT
AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn
aXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiWRKl
x+88u4SKZnfCMraqMsfJCs6tcz3EjMYTWmRKhhUOE9pDkvZfv0mgF7pNHsTKvFRt
oVnEVQaZC5TlHNOGa2QWit9YuruWjW8VSaU4s9gR1/Cg9/Zc8Z0yUEDpsaVnwuoA
RpVzvzoRzPmTNpMNEcQ07aBjHP7OJrwyvcdqQA1BbfDVMmRmw1d+/i8tyR3cTyzl
/3TismN5nrmhGh/ZF75FFA/xDN7PbVYDPowiFnEVHiBrYh2mFTabRUnb7K4oLx+d
1L5x3Az299F/HYZlBenXpJLtnCL3+HY6qsGXVbzKjlKNqbXsmlzVkChu093weN/q
UvWO2883cEiXmdqxAgMBAAGjUDBOMB0GA1UdDgQWBBRsMy2bxsCKYyUYtTYz/zZb
z7Le0zAfBgNVHSMEGDAWgBRsMy2bxsCKYyUYtTYz/zZbz7Le0zAMBgNVHRMEBTAD
AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBnFQ+Sc3s8y79DTsA7CvvAFeG/zvWQiu8y
UM5LO1QcWeQQj29GMThqrY21dNfkynl7mZUMEeXKvwwzweFCc2odiUPHxoV1G4FE
tzNaZ8Ap9jye78YQ8SB8NPQwC7ovecfSqNflT4NMAThSuxpGp8Ugf7a24LXozLzL
bCRvG9sLGyRneZbfU8B43ELRLCkjzWR32N7D2pmKk4CEMiW0ScphU1JEHaimneMa
TFc63hNzKpuj7+BGv4ZuvB1j/Mbmz53PGgFKnGQHPb2TIvMxyB+lML5vE0Bm8YWt
P8DNyx11CCCdBdMWfeta6MjmmqcV5/YEq92c5O2Ql94tWFNLR6wQ</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#">
                        <InclusiveNamespaces xmlns="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="wsse SOAP-ENV" />
                    </ds:CanonicalizationMethod>
                    <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
                    <ds:Reference URI="#TheBody">
                        <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>VhsSnaEAFsY0OYegKQh99v9csXg=</ds:DigestValue>
                    </ds:Reference>
                </ds:SignedInfo>
                <ds:SignatureValue>Ynp3H4rtzpXIh4TaVxkpEkS1bMCCu672aeCzUOzheNNfnpmLsCZz3+zQjMBbchPggCayC5ihpEdhRe3XvPXjPXXAgxDP4mic091QPmjHlmUcu8yqRKfxnPtD35nqaxDtCYw+jGIzj+ch094vA4RPCfY8JQnb1mpy1ZjjsMW8741CIh1epbsd/0bZt6tfINUQ37seg07yvLbCJZ/Zf+h8FlFryQk6lHTTeZl/GfQ9NlDBcShby3x8Hc1KwW++zFqEA7G783R9AYPYn3fWTOBhYk5gkgFc+HaPRLR/L0Bp7ZPbmOR/iZQ+HK4W672tTdN/R2GdN7/deV7QTp2DYK1Z8w==</ds:SignatureValue>
                <ds:KeyInfo>
                    <wsse:SecurityTokenReference>
                        <wsse:Reference URI="#x509cert00" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509" />
                    </wsse:SecurityTokenReference>
                </ds:KeyInfo>
            </ds:Signature>
        </wsse:Security>
    </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="TheBody">
        <getVersion xmlns="http://msgsec.wssecfvt.ws.ibm.com" />
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

我通过滚动我们自己的肥皂信封、对其进行签名并通过 HttpClient 进行管道传输来解决此问题。 .NET Core 中的 WCF 无法为我们提供与第三方服务的各种怪癖兼容的结果。

这是代码,应该很容易根据您的要求进行更改:

// ...
private static HttpClient Client = new HttpClient(); // https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
// ...

Uri uri = new Uri("https://thirdparty.com/service.svc");
X509Certificate2 cert = // from some store etc
var envelope = BuildEnvelope(cert);

using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri))
{
    request.Content = new StringContent(envelope, Encoding.UTF8, "application/soap+xml");
    using (HttpResponseMessage response = Client.SendAsync(request).Result)
    {
        if (response.IsSuccessStatusCode)
        {
            response.Content.ReadAsStringAsync().ContinueWith(task =>
            {
                string thirdparty_envelope = task.Result;
                XElement thirdparty_root = XElement.Parse(thirdparty_envelope);
                // etc
            }, TaskContinuationOptions.ExecuteSynchronously);
        }
    }
}


private string BuildEnvelope(X509Certificate2 certificate)
{
    string envelope = null;
    // note - lots of bits here specific to my thirdparty
    string cert_id = string.Format("uuid-{0}-1", Guid.NewGuid().ToString());
    using (var stream = new MemoryStream())
    {
        Encoding utf8 = new UTF8Encoding(false); // omit BOM
        using (var writer = new XmlTextWriter(stream, utf8))
        {
            // timestamp
            DateTime dt = DateTime.UtcNow;
            string now = dt.ToString("o").Substring(0, 23) + "Z";
            string plus5 = dt.AddMinutes(5).ToString("o").Substring(0, 23) + "Z";

            // soap envelope
            // <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
            writer.WriteStartDocument();
            writer.WriteStartElement("s", "Envelope", "http://www.w3.org/2003/05/soap-envelope");
            writer.WriteAttributeString("xmlns", "a", null, "http://www.w3.org/2005/08/addressing");
            writer.WriteAttributeString("xmlns", "u", null, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");

            writer.WriteStartElement("s", "Header", null);

            /////////////////
            //  saml guts  //
            /////////////////

            //<a:Action s:mustUnderstand="1">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</a:Action>
            writer.WriteStartElement("a", "Action", null);
            writer.WriteAttributeString("s", "mustUnderstand", null, "1");
            writer.WriteString("http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue");
            writer.WriteEndElement(); //Action

            //<a:MessageID>urn:uuid:0cc426dd-35bf-4c8b-a737-7e2ae94bd44d</a:MessageID>
            string msg_id = string.Format("urn:uuid:{0}", Guid.NewGuid().ToString());
            writer.WriteStartElement("a", "MessageID", null);
            writer.WriteString(msg_id);
            writer.WriteEndElement(); //MessageID

            //<a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo>
            writer.WriteStartElement("a", "ReplyTo", null);
            writer.WriteStartElement("a", "Address", null);
            writer.WriteString("http://www.w3.org/2005/08/addressing/anonymous");
            writer.WriteEndElement(); //Address
            writer.WriteEndElement(); //ReplyTo

            writer.WriteStartElement("a", "To", "http://www.w3.org/2005/08/addressing");
            writer.WriteAttributeString("s", "mustUnderstand", null, "1");
            writer.WriteAttributeString("u", "Id", null, "_1");
            writer.WriteString("https://thirdparty.com/service.svc");
            writer.WriteEndElement(); //To

            //<o:Security xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
            writer.WriteStartElement("o", "Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
            writer.WriteAttributeString("s", "mustUnderstand", null, "1");

            //<u:Timestamp u:Id="_0">
            writer.WriteStartElement("u", "Timestamp", null);
            writer.WriteAttributeString("u", "Id", null, "_0");

            //<u:Created>2018-02-08T15:03:13.115Z</u:Created>
            writer.WriteElementString("u", "Created", null, now);

            //<u:Expires>2018-02-08T15:08:13.115Z</u:Expires>
            writer.WriteElementString("u", "Expires", null, plus5);

            writer.WriteEndElement(); //Timestamp

            writer.WriteStartElement("o", "BinarySecurityToken", null);
            writer.WriteAttributeString("u", "Id", null, cert_id);
            writer.WriteAttributeString("ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3");
            writer.WriteAttributeString("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
            byte[] rawData = certificate.GetRawCertData();
            writer.WriteBase64(rawData, 0, rawData.Length);
            writer.WriteEndElement(); //BinarySecurityToken

            writer.WriteEndElement(); //Security
            writer.WriteEndElement(); //Header

            //<s:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            writer.WriteStartElement("s", "Body", "http://www.w3.org/2003/05/soap-envelope");
            writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");
            writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");

            // your 3rd-party soap payload goes here
            writer.WriteStartElement("???", "http://docs.oasis-open.org/ws-sx/ws-trust/200512");
            // ...                
            writer.WriteEndElement(); // 
            writer.WriteEndElement(); // Body


            writer.WriteEndElement(); //Envelope
        }

        // signing pass
        var signable = Encoding.UTF8.GetString(stream.ToArray());
                            XmlDocument doc = new XmlDocument();
        doc.LoadXml(signable);

        // see https://stackoverflow.com/a/6467877
        var signedXml = new SignedXmlWithId(doc);

        var key = certificate.GetRSAPrivateKey();
        signedXml.SigningKey = key;
        // these values may not be supported by your 3rd party - they may use e.g. SHA256 miniumum
        signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
        signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA1Url;

        // 
        KeyInfo keyInfo = new KeyInfo();
        KeyInfoX509Data x509data = new KeyInfoX509Data(certificate);
        keyInfo.AddClause(x509data);
        signedXml.KeyInfo = keyInfo;

        // 3rd party wants us to only sign the timestamp fragment- ymmv
        Reference reference0 = new Reference();
        reference0.Uri = "#_0";
        var t0 = new XmlDsigExcC14NTransform();
        reference0.AddTransform(t0);
        reference0.DigestMethod = SignedXml.XmlDsigSHA1Url;
        signedXml.AddReference(reference0);
        // etc

        // get the sig fragment
        signedXml.ComputeSignature();
        XmlElement xmlDigitalSignature = signedXml.GetXml();

        // modify the fragment so it points at BinarySecurityToken instead
        XmlNode info = null;
        for (int i = 0; i < xmlDigitalSignature.ChildNodes.Count; i++)
        {
            var node = xmlDigitalSignature.ChildNodes[i];
            if (node.Name == "KeyInfo")
            {
                info = node;
                break;
            }
        }
        info.RemoveAll();

        // 
        XmlElement securityTokenReference = doc.CreateElement("o", "SecurityTokenReference", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        XmlElement reference = doc.CreateElement("o", "Reference", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        reference.SetAttribute("ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3");
        // cert id                
        reference.SetAttribute("URI", "#" + cert_id);
        securityTokenReference.AppendChild(reference);
        info.AppendChild(securityTokenReference);

        var nsmgr = new XmlNamespaceManager(doc.NameTable);
        nsmgr.AddNamespace("o", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        nsmgr.AddNamespace("s", "http://www.w3.org/2003/05/soap-envelope");
        var security_node = doc.SelectSingleNode("/s:Envelope/s:Header/o:Security", nsmgr);
        security_node.AppendChild(xmlDigitalSignature);

        envelope = doc.OuterXml;
    }

    return envelope;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何调用.Net Core需要签名的XML SOAP服务? 的相关文章

随机推荐