ChatGPT解决这个技术问题 Extra ChatGPT

Python sign SOAP request using BinarySecurityToken

I'm trying to sign a SOAP request with a certificate using python. I've tried python-zeep and its Signature methods and suds with py-wsse. Both don't give me the expected result.

Zeep gives me:

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
   <soap-env:Header>
      <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
         <Signature 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-2790286f-721f-4f62-88bf-7e6b1f160e09">
                  <Transforms>
                     <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                  </Transforms>
                  <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                  <DigestValue>DATA</DigestValue>
               </Reference>
               <Reference URI="#id-597e9b96-07e2-4ee8-9ba8-071d97851456">
                  <Transforms>
                     <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                  </Transforms>
                  <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                  <DigestValue>DATA</DigestValue>
               </Reference>
            </SignedInfo>
            <SignatureValue>DATA</SignatureValue>
            <KeyInfo>
               <wsse:SecurityTokenReference>
                  <X509Data>
                     <X509IssuerSerial>
                        <X509IssuerName>DATA</X509IssuerName>
                        <X509SerialNumber>DATA</X509SerialNumber>
                     </X509IssuerSerial>
                     <X509Certificate>DATA</X509Certificate>
                  </X509Data>
               </wsse:SecurityTokenReference>
            </KeyInfo>
         </Signature>
         <wsu:Timestamp wsu:Id="id-597e9b96-07e2-4ee8-9ba8-071d97851456">
            <wsu:Created>2017-10-27T09:41:01+00:00</wsu:Created>
            <wsu:Expires>2017-10-27T10:41:01+00:00</wsu:Expires>
         </wsu:Timestamp>
      </wsse:Security>
   </soap-env:Header>
   <soap-env:Body wsu:Id="id-2790286f-721f-4f62-88bf-7e6b1f160e09">
      <wst:RequestSecurityToken>
         <wst:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</wst:TokenType>
         <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>
      </wst:RequestSecurityToken>
   </soap-env:Body>
</soap-env:Envelope>

Whereas suds python-wsse gives:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
   <soapenv:Header>
      <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">
         <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#X509v3" wsu:Id="id-86d39619-2654-4e09-a1bc-40e2822bf1c9">DATA</wsse:BinarySecurityToken>
         <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
            <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" />
            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
               <wsse:SecurityTokenReference wsse:TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">
                  <wsse:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="#id-86d39619-2654-4e09-a1bc-40e2822bf1c9" />
               </wsse:SecurityTokenReference>
            </ds:KeyInfo>
            <xenc:CipherData>
               <xenc:CipherValue>DATA</xenc:CipherValue>
            </xenc:CipherData>
            <xenc:ReferenceList>
               <xenc:DataReference URI="#id-a14b401f-8353-46d6-a607-92ef23caca1e" />
            </xenc:ReferenceList>
         </xenc:EncryptedKey>
         <wsu:Timestamp>
            <wsu:Created>2017-10-27T11:20:16.301Z</wsu:Created>
            <wsu:Expires>2017-10-27T13:20:26.301Z</wsu:Expires>
         </wsu:Timestamp>
      </wsse:Security>
   </soapenv:Header>
   <soapenv:Body>
      <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" Type="http://www.w3.org/2001/04/xmlenc#Element" ns0:Id="id-a14b401f-8353-46d6-a607-92ef23caca1e">
         <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
         <xenc:CipherData>
            <xenc:CipherValue>DATA</xenc:CipherValue>
         </xenc:CipherData>
      </xenc:EncryptedData>
   </soapenv:Body>
</soapenv:Envelope>

However I need a request that looks more like a mix of the two:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
   <soapenv:Header>
      <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">
         <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#X509v3" wsu:Id="X509-B0D6288D1BAB6D839515090888163762">DATA</wsse:BinarySecurityToken>
         <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SIG-B0D6288D1BAB6D839515090888164186">
            <ds:SignedInfo>
               <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                  <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="soapenv wst" />
               </ds:CanonicalizationMethod>
               <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
               <ds:Reference URI="#TS-B0D6288D1BAB6D839515090888163021">
                  <ds:Transforms>
                     <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                        <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="wsse soapenv wst" />
                     </ds:Transform>
                  </ds:Transforms>
                  <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                  <ds:DigestValue>DATA</ds:DigestValue>
               </ds:Reference>
               <ds:Reference URI="#id-B0D6288D1BAB6D839515090888164135">
                  <ds:Transforms>
                     <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                        <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="wst" />
                     </ds:Transform>
                  </ds:Transforms>
                  <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                  <ds:DigestValue>DATA</ds:DigestValue>
               </ds:Reference>
               <ds:Reference URI="#X509-B0D6288D1BAB6D839515090888163762">
                  <ds:Transforms>
                     <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                        <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="" />
                     </ds:Transform>
                  </ds:Transforms>
                  <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                  <ds:DigestValue>DATA</ds:DigestValue>
               </ds:Reference>
            </ds:SignedInfo>
            <ds:SignatureValue>DATA</ds:SignatureValue>
            <ds:KeyInfo Id="KI-B0D6288D1BAB6D839515090888164053">
               <wsse:SecurityTokenReference wsu:Id="STR-B0D6288D1BAB6D839515090888164074">
                  <wsse:Reference URI="#X509-B0D6288D1BAB6D839515090888163762" 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 wsu:Id="TS-B0D6288D1BAB6D839515090888163021">
            <wsu:Created>2017-10-27T07:20:16.301Z</wsu:Created>
            <wsu:Expires>2017-10-27T07:20:26.301Z</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-B0D6288D1BAB6D839515090888164135">
      <wst:RequestSecurityToken>
         <wst:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</wst:TokenType>
         <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>
      </wst:RequestSecurityToken>
   </soapenv:Body>
</soapenv:Envelope>

Is there any easy way in python to sign SOAP envelopes with BinarySecurityToken? Is there even a proper difference between the first and the last envelope or would both be valid?

Could you pretty print these requests? It would be nicer for the eye. Example of pretty print: wiki.hippoedit.com/_media/plugins/xml_formatted.png
@pawni are you still interested in an answer to this question? if you are, to start, could you show where the differences are between zeep, suds, and your expected output?

F
Fabio Crispino

Chicklat with its API provides two examples (see at bottom) for solving this problem. The first signs a certificate using SecurityTokenReference and the second signs a certificate using BinaryTokenReference. You don't have to rely on this API as it's subjected to license costs, you can do it but you can use alternative libraries to do this (below in the post I posted alternatives). Those examples are good starting points to get to your expected results by understanding the way Chicklat API does it and using your custom methods.

In the first example:

(#1) A SOAP XML template is loaded, this is the template that will be signed with the pfx certificate and the BinarySecurityToken;

(#2-3) The pfx, a single file containing the password protected certificate along with its private key, is loaded, then its private key and its internal certificate are extracted by providing a password as input, which is the password used when the certificate was issued;

(#4) Once the certificate is extracted from the pfx file, it is BASE64-encoded. Inside the XML template, BASE64_CERT is replaced by this string as a value provided to wsse:BinarySecurityToken;

(#5) Build the wsse:SecurityTokenReference XML. This XML is a KeyInfo, a storage to contain your certificate private key, used to verify the signature;

(#6) Signing the XML using Chicklat XML Digital Signature Generator.

You can adapt the first example by using the second example. This can be done by following these changes:

Change the way the sbXml is constructed. Example 2, starting from chilkat.CkXml(), shows a way to do it and setup parameters. The output XML structure would be similar to this schema to use BinarySecurityToken:

<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:exc14n="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <S:Header>
      <To xmlns="http://www.w3.org/2005/08/addressing" wsu:Id="_5002">https://XXXXXXXXX</To>
      <Action xmlns="http://www.w3.org/2005/08/addressing" S:mustUnderstand="true">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</Action>
      <ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
         <Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
      </ReplyTo>
      <FaultTo xmlns="http://www.w3.org/2005/08/addressing">
         <Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
      </FaultTo>
      <MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:e9033251-4ff0-4618-8baf-4952ab5fd207</MessageID>
      <wsse:Security S:mustUnderstand="true">
         <wsu:Timestamp xmlns:ns16="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns17="http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512" wsu:Id="_1">
            <wsu:Created>2018-05-23T02:38:27Z</wsu:Created>
            <wsu:Expires>2018-05-23T02:43:27Z</wsu:Expires>
         </wsu:Timestamp>
         <wsse:BinarySecurityToken xmlns:ns16="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns17="http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" wsu:Id="uuid_43470044-78b4-4b23-926a-b7f590d24cb8">MIIEIjCCAwqgAwIBAgIDAmCRMA0GCSqGSIb3DQEBCwUAMIGFMQswCQYDVQQGEwJBVTElMCMGA1UEChMcQXVzdHJhbGlhbiBCdXNpbmVzcyBSZWdpc3RlcjEgMB4GA1UECxMXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxLTArBgNVBAMTJFRlc3QgQXVzdHJhbGlhbiBCdXNpbmVzcyBSZWdpc3RlciBDQTAeFw0xNzAzMDEwMzQyMTBaFw0xOTAzMDEwMzQyMTBaMFYxETAPBgNVBC4TCDIwMDgwMTYxMQswCQYDVQQGEwJBVTEUMBIGA1UEChMLOTYwODU1MjE2MDYxHjAcBgNVBAMTFUNPUlAgVEFYQVRJT04gTUFOQUdFUjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAs6zlEhQOOpPPXffUPgpmbPl6qtQ3QaYgYvSTGLOL2pxuwGlZc5oW93zTwh5yq0r5NAgouGx9Oo6pYBO/xEUst5g3VW5BmbuOyGk3UvKVLHTnWlt8KCaRwW/qOyya8ZYfJm8+OQHtmMZc0pjj/Pcn8lrN61BKyjAYwK3CTKa60gcCAwEAAaOCAUswggFHMAwGA1UdEwEB/wQCMAAwgewGA1UdIASB5DCB4TCB3gYJKiQBlzllAQcBMIHQMIGuBggrBgEFBQcCAjCBoRqBnlRoaXMgY2VydGlmaWNhdGUgbWF5IG9ubHkgYmUgdXNlZCBmb3IgdGhlIHB1cnBvc2UgcGVybWl0dGVkIGluIHRoZSBhcHBsaWNhYmxlIENlcnRpZmljYXRlIFBvbGljeS4gTGltaXRlZCBsaWFiaWxpdHkgYXBwbGllcyAtIHJlZmVyIHRvIHRoZSBDZXJ0aWZpY2F0ZSBQb2xpY3kuMB0GCCsGAQUFBwIBFhF3d3cudGVzdGFicmNhLmNvbTAXBgYqJAGCTQEEDRYLOTYwODU1MjE2MDYwDgYDVR0PAQH/BAQDAgTwMB8GA1UdIwQYMBaAFI0lJ7xfoJpx55N3+bAZYiyZdlr9MA0GCSqGSIb3DQEBCwUAA4IBAQBQoYVRGRf3QIkxFa4ecI2Kxph5vrUuTdzIaDm+mCnHyaamnAbBH7WCPymuK+ZFQo1lWcyMzQpf7N/6/e0sZH3OD0JIuBF3AKQbvwVLfJ5x/Xu2Cz9ZAkkonL0wIXXAmGIPNjZD1WwzPUYbUuZw+GqOIeSOYitIuz7N3y6vKIgLLzghVAcXBPPuMqqcL/PwSIQ9LRTqa4zhNy2Bn+CwR9QanS6jGTXfpOmetsbRckE+WgCwzMz5iqgTrP8AYwsDYxFBdmktrO+u1V0PrESeZFTbToRs2UUtEaYowIDEb/6KqKJxs7AZN+Nt5r1RaOfbsr6KJDOS5K+VRRtjAGwcXBXN</wsse:BinarySecurityToken>
      </wsse:Security>
   </S:Header>
   <S:Body>
      <RequestSecurityToken xmlns="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
         <RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</RequestType>
         <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
            <EndpointReference:EndpointReference xmlns:EndpointReference="http://www.w3.org/2005/08/addressing" xmlns="http://www.w3.org/2005/08/addressing">
               <Address>https://XXXXXXXXX/services</Address>
            </EndpointReference:EndpointReference>
         </wsp:AppliesTo>
         <TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</TokenType>
         <Claims xmlns:i="http://schemas.xmlsoap.org/ws/2005/05/identity" Dialect="http://schemas.xmlsoap.org/ws/2005/05/identity">
            <i:ClaimType Optional="false" Uri="http://XXXXXXXXX/2008/06/identity/claims/abn" />
            <i:ClaimType Optional="false" Uri="http://XXXXXXXXX/2008/06/identity/claims/commonname" />
            <i:ClaimType Optional="false" Uri="http://XXXXXXXXX/2008/06/identity/claims/credentialtype" />
            <i:ClaimType Optional="false" Uri="http://XXXXXXXXX/2008/06/identity/claims/samlsubjectid" />
            <i:ClaimType Optional="false" Uri="http://XXXXXXXXX/2008/06/identity/claims/fingerprint" />
            <i:ClaimType Optional="true" Uri="http://XXXXXXXXX/2008/06/identity/claims/sbr_personid" />
            <i:ClaimType Optional="true" Uri="http://XXXXXXXXX/2008/06/identity/claims/givennames" />
            <i:ClaimType Optional="true" Uri="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" />
            <i:ClaimType Optional="true" Uri="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" />
            <i:ClaimType Optional="true" Uri="http://XXXXXXXXX/2008/06/identity/claims/credentialadministrator" />
            <i:ClaimType Optional="true" Uri="http://XXXXXXXXX/2008/06/identity/claims/stalecrlminutes" />
            <i:ClaimType Optional="true" Uri="http://XXXXXXXXX/2008/06/identity/claims/subjectdn" />
            <i:ClaimType Optional="true" Uri="http://XXXXXXXXX/2008/06/identity/claims/issuerdn" />
            <i:ClaimType Optional="true" Uri="http://XXXXXXXXX/2008/06/identity/claims/notafterdate" />
            <i:ClaimType Optional="true" Uri="http://XXXXXXXXX/2008/06/identity/claims/certificateserialnumber" />
            <i:ClaimType Optional="true" Uri="http://XXXXXXXXX/2008/06/identity/claims/previoussubject" />
         </Claims>
         <Lifetime>
            <wsu:Created>2018-05-23T02:38:27.906Z</wsu:Created>
            <wsu:Expires>2018-05-23T03:08:27.906Z</wsu:Expires>
         </Lifetime>
         <KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</KeyType>
         <KeySize>512</KeySize>
      </RequestSecurityToken>
   </S:Body>
</S:Envelope>

You can use OpenSSL.crypto to manage the pfx file extracting the private key and the certificate;

You can use SignXML for the XML Digital Signature generation.

from lxml import etree
from signxml import XMLSigner, XMLVerifier

data_to_sign = "<Test/>" // Your XML
root = etree.fromstring(data_to_sign)
signed_root = XMLSigner().sign(root, key=PRIVATE_KEY, cert=CERTIFICATE)
verified_data = XMLVerifier().verify(signed_root).signed_xml

Reference examples:

Example 1: Sign SOAP XML using a wsse:SecurityTokenReference

Example 2: Sign with BinarySecurityToken