简体   繁体   中英

Signing SOAP Message with XML Signature and WCF

Currently I'm implementing a client in .NET to consume a SOAP service written in JAVA (I'm not sure). The SOAP envelope has to be signed in a very specific way, and it has been a nightmare trying to do so.

This is an example of the envelope they are expecting:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Header>
        <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
            <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:CanonicalizationMethod>
                    <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod>
                    <ds:Reference URI="#id-319644606">
                        <ds:Transforms>
                            <ds:Transform Algorithm="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:DigestMethod>
                        <ds:DigestValue>uQpjnTTrxxI7ck9FpeEI6YRVHBA=</ds:DigestValue>
                    </ds:Reference>
                </ds:SignedInfo>
                <ds:SignatureValue>
paNzJWyYDBNTj8Ay4z0/wOKKQUCe/z0q/LRcDgB9CP1fn/FZW8uyLdASHxs2HHlpYG7daFvbtpsQ
ZoiIUSzUuUBUAjjaqLWRZyUeCqc48f8X6vxR52mLARnJdPC9MWPZg8FTsoqJ2nvyH28chqf8Svql
KxgtCtBiL9wPnkzG5m4=
</ds:SignatureValue>
                <ds:KeyInfo Id="KeyId-1827725498">
                    <wsse:SecurityTokenReference xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="STRId-131635550">
                        <ds:X509IssuerSerial>
                            <ds:X509IssuerName>CN=DESARROLLO,OU=INGENIERIA,O=ASOBANCARIA,L=BOGOTA,ST=D.C,C=CO</ds:X509IssuerName>
                            <ds:X509SerialNumber>1141316577</ds:X509SerialNumber>
                        </ds:X509IssuerSerial>
                    </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:Created>2014-10-29T22:14:01Z</wsu:Created>
                <wsu:Expires>2014-10-29T22:19:01Z</wsu:Expires>
            </wsu:Timestamp>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-319644606">
        <ns1:evaluarCuestionario xmlns:ns1="http://ws.confrontaultra.cifin.asobancaria.com" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
            <p_parametrosSeguridad href="#id0"></p_parametrosSeguridad>
            <p_respuestaCuestionario href="#id1"></p_respuestaCuestionario>
        </ns1:evaluarCuestionario>
        <multiRef xmlns:ns2="http://ultras.dto.confrontaultra.cifin.co" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id1" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns2:RespuestaCuestionarioULTRADTO">
            <secuenciaCuestionario href="#id2"></secuenciaCuestionario>
            <codigoCuestionario href="#id3"></codigoCuestionario>
            <respuestas xmlns:ns3="http://ultra.dto.confrontaultra.cifin.co" soapenc:arrayType="ns3:RespuestaPreguntaULTRADTO[5]" xsi:type="soapenc:Array">
                <respuestas href="#id4"></respuestas>
                <respuestas href="#id5"></respuestas>
                <respuestas href="#id6"></respuestas>
                <respuestas href="#id7"></respuestas>
                <respuestas href="#id8"></respuestas>
            </respuestas>
        </multiRef>
        <multiRef xmlns:ns4="http://ultra.dto.confrontaultra.cifin.co" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns4:ParametrosSeguridadULTRADTO">
            <claveCIFIN xsi:type="soapenc:string">90681</claveCIFIN>
            <password xsi:type="soapenc:string">H32std</password>
        </multiRef>
        <multiRef xmlns:ns5="http://ultra.dto.confrontaultra.cifin.co" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id7" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns5:RespuestaPreguntaULTRADTO">
            <secuenciaPregunta href="#id9"></secuenciaPregunta>
            <secuenciaRespuesta href="#id10"></secuenciaRespuesta>
        </multiRef>
        <multiRef xmlns:ns6="http://ultra.dto.confrontaultra.cifin.co" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id4" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns6:RespuestaPreguntaULTRADTO">
            <secuenciaPregunta href="#id11"></secuenciaPregunta>
            <secuenciaRespuesta href="#id12"></secuenciaRespuesta>
        </multiRef>
        <multiRef xmlns:ns7="http://ultra.dto.confrontaultra.cifin.co" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id6" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns7:RespuestaPreguntaULTRADTO">
            <secuenciaPregunta href="#id13"></secuenciaPregunta>
            <secuenciaRespuesta href="#id14"></secuenciaRespuesta>
        </multiRef>
        <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id2" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:long">8846263</multiRef>
        <multiRef xmlns:ns8="http://ultra.dto.confrontaultra.cifin.co" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id5" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns8:RespuestaPreguntaULTRADTO">
            <secuenciaPregunta href="#id15"></secuenciaPregunta>
            <secuenciaRespuesta href="#id16"></secuenciaRespuesta>
        </multiRef>
        <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id3" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int">7062</multiRef>
        <multiRef xmlns:ns9="http://ultra.dto.confrontaultra.cifin.co" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id8" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns9:RespuestaPreguntaULTRADTO">
            <secuenciaPregunta href="#id17"></secuenciaPregunta>
            <secuenciaRespuesta href="#id18"></secuenciaRespuesta>
        </multiRef>
        <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id12" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int">150136315</multiRef>
        <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id14" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int">150136266</multiRef>
        <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id17" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int">7</multiRef>
        <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id10" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int">150136259</multiRef>
        <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id16" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int">150136277</multiRef>
        <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id9" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int">20</multiRef>
        <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id18" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int">150136247</multiRef>
        <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id15" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int">35</multiRef>
        <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id13" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int">9</multiRef>
        <multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" id="id11" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int">92</multiRef>
    </soapenv:Body>
</soapenv:Envelope>

So basically I have 2 problems:

  1. I haven't been able to set the proper namespaces to the signature header. (the ds namespace)
  2. Also, notice the wsse:SecutiryTokenReference tag inside the KeyInfo tag. The SignedXml class generates this xml by default inside the KeyInfo element:

     <KeyInfo> <X509Data> <X509IssuerSerial> <X509IssuerName>CN=RootCATest</X509IssuerName> <X509SerialNumber>228801528337358580231830876343013017805</X509SerialNumber> </X509IssuerSerial> </X509Data> </KeyInfo>

So, how can I replace this <X509Data> element with <wsse:SecurityTokenReference> element.

So far I made an implementation following lots of posts and blogs I've found in order to sign the xml document. This is my implementation:

 public class Signer
    {
        public XmlDocument SignMessage(XmlDocument xmlDoc, X509Certificate2 certificate)
        {
            var ns = new XmlNamespaceManager(xmlDoc.NameTable);
            ns.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");

            var body = xmlDoc.DocumentElement.SelectSingleNode(@"//soapenv:Body", ns) as XmlElement;
            if (body == null)
                throw new ApplicationException("No body tag found");
            body.SetAttribute("id", "Body");

            var signedXml = new SignedXml(xmlDoc);

            var keyInfo = new KeyInfo();
            signedXml.SigningKey = certificate.PrivateKey;                

            var keyInfoData = new KeyInfoX509Data();
            keyInfoData.AddIssuerSerial(certificate.Issuer, certificate.GetSerialNumberString());
            keyInfo.AddClause(keyInfoData);    

            signedXml.KeyInfo = keyInfo;

            signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
            var reference = new Reference { Uri = "#Body" };

            reference.AddTransform(new XmlDsigExcC14NTransform());
            signedXml.AddReference(reference);
            signedXml.ComputeSignature();

            var signedElement = signedXml.GetXml();

            var securityNode = xmlDoc.CreateElement(
                "wsse",
                "Security",
                "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");

            securityNode.AppendChild(signedElement);

            var timeStampNOde = xmlDoc.CreateElement("Timestamp");
            var createdNode = xmlDoc.CreateElement("Created");
            createdNode.InnerText = JsonConvert.SerializeObject(DateTime.UtcNow, new IsoDateTimeConverter());
            var expiresNode = xmlDoc.CreateElement("Created");
            expiresNode.InnerText = JsonConvert.SerializeObject(DateTime.UtcNow.AddMinutes(5), new IsoDateTimeConverter());
            timeStampNOde.AppendChild(createdNode);
            timeStampNOde.AppendChild(expiresNode);

            securityNode.AppendChild(timeStampNOde);

            var soapHeader = xmlDoc.DocumentElement.SelectSingleNode(@"//soapenv:Header", ns) as XmlElement;
            if (soapHeader == null)
            {
                soapHeader = xmlDoc.CreateElement("soapenv:Header", "");
                xmlDoc.DocumentElement.InsertBefore(soapHeader, xmlDoc.DocumentElement.ChildNodes[0]);
            }
            soapHeader.AppendChild(securityNode);

            return xmlDoc;
        }

Any comments, suggestions, posts, etc would be very helpful. Consider I have to send the the envelope as they require, since this organization won't change it just for me, I'm guessing they have tons of clients.

Thank you very much!

Your approach is valid but it does not use WCF security capabilities. I recommend to first try with WCF and then if it is not possible with custom signing as you did. First try with this WCF binding:

<customBinding>
        <binding name="NewBinding0">
            <textMessageEncoding messageVersion="Soap11" />
            <security authenticationMode="MutualCertificate" includeTimestamp="false"
                messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
                <secureConversationBootstrap />
            </security>
            <httpTransport />
        </binding>
</customBinding>

Also configure WCF to sign only:

c.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.Sign;

This will create a similar SOAP to what you need but the keyInfo will be as a reference to binarySecurityToken and not as in the sample. Still, I suggest to try with it, there is a good chance it will work.

If it fails you will need to create a code binding and play with the x509 params . Alternatively you could implement a custom WCF encoder to change the key reference.

I did not fully followed your code. The approach to sign yourself without WCF is valid but there is a good chance you will have errors.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM