mvantellingen / python-zeep

A Python SOAP client
http://docs.python-zeep.org
Other
1.87k stars 578 forks source link

WSSE signature plugin for SecurityTokenReference #1419

Open jaspercram opened 1 month ago

jaspercram commented 1 month ago

I need to connect to a SOAP server that utilizes a WSSE authentication method different from the one available in Zeep. Unfortunately, I lack the knowledge and time to create a pull request for this plugin . However, I will share the code here in case it is useful to someone else. I apologize for not being able to provide more detailed information on how to use this plugin. Please feel free to ignore it or close this issue if necessary.

class SignaturePlugin(zeep.Plugin):
    def egress(self, envelope, http_headers, operation, binding_options):
        # Insert filename here
        key = xmlsec.Key.from_file(XXXXXXX, xmlsec.constants.KeyDataFormatPem)
        soap_env = zeep.utils.detect_soap_env(envelope)

        # Create the Signature node.
        signature = xmlsec.template.create(envelope, xmlsec.Transform.EXCL_C14N, xmlsec.Transform.RSA_SHA256)

        # Insert the Signature node in the wsse:Security header.
        security = get_security_header(envelope)
        security.insert(0, signature)

        # Perform the actual signing.
        ctx = xmlsec.SignatureContext()
        ctx.key = key
        _sign_node(ctx, signature, envelope.find(QName(soap_env, 'Body')))
        timestamp = security.find(QName(zeep.ns.WSU, 'Timestamp'))
        if timestamp is not None:
            _sign_node(ctx, signature, timestamp)
        ctx.sign(signature)

        # Place the X509 data inside a WSSE SecurityTokenReference within
        # KeyInfo. The recipient expects this structure, but we can't rearrange
        # like this until after signing, because otherwise xmlsec won't populate
        # the X509 data (because it doesn't understand WSSE).
        key_info = xmlsec.template.ensure_key_info(signature)
        sec_token_ref = etree.SubElement(key_info, QName(zeep.ns.WSSE, 'SecurityTokenReference'))
        # Create the KeyIdentifier node.
        key_identifier = etree.SubElement(
            sec_token_ref,
            etree.QName(zeep.ns.WSSE, 'KeyIdentifier'),
            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#X509SubjectKeyIdentifier',
        )
        # Insert Key Identifier here
        key_identifier.text = 'XXXXXXX'
        sec_token_ref.append(key_identifier)
        return envelope, http_headers