python-zeep icon indicating copy to clipboard operation
python-zeep copied to clipboard

Encrypt the body and other issues

Open yurj opened this issue 3 years ago • 6 comments

Hi!

zeep 4.0.0.

I'm testing zeep against http://www.sigecweb.beniculturali.it/webServiceBrowsingCards?wsdl. It has a security policy. Note: download the wdsl locally because of a wrong address inside, replace 10.96.1.85:8180 with sigecweb.beniculturali.it (so you've:

    <port binding="tns:WSBrowsingItemsBinding" name="WSBrowsingItemsPort">
      <soap:address location="http://sigecweb.beniculturali.it/webServiceBrowsingCards"/>
    </port>

This is the code I'm using:

from zeep import Client
from zeep.cache import SqliteCache
from zeep.transports import Transport
from zeep.wsse.username import UsernameToken
from zeep.wsse.signature import Signature
import datetime
from zeep.plugins import HistoryPlugin
from zeep.wsse.utils import WSU

import logging.config

logging.config.dictConfig({
    'version': 1,
    'formatters': {
        'verbose': {
            'format': '%(name)s: %(message)s'
        }
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
    },
    'loggers': {
        'zeep.transports': {
            'level': 'DEBUG',
            'propagate': True,
            'handlers': ['console'],
        },
    }
})

# zeep don't follow the docs https://docs.python-zeep.org/en/master/wsse.html
# "To use UsernameToken and Signature together, then you can pass both together to the client in a list" -> *** AttributeError: 'list' object has no attribute 'verify'
class CustomSignature(object):
    """Sign given SOAP envelope with WSSE sig using given key and cert."""
    def __init__(self, wsse_list):
        self.wsse_list = wsse_list
    def apply(self, envelope, headers):
        for wsse in self.wsse_list:
            envelope, headers = wsse.apply(envelope, headers)
        return envelope, headers
    def verify(self, envelope):
        pass

# transport = Transport(cache=SqliteCache())
user_name_token = UsernameToken('xxxx', '')
private_key_filename='mykey.pem'
public_key_filename='mycert.pem'
optional_password='xxxxx'
signature = Signature(private_key_filename, public_key_filename,
    optional_password)


timestamp_token = WSU.Timestamp()
today_datetime = datetime.datetime.today()
expires_datetime = today_datetime + datetime.timedelta(minutes=10)
timestamp_elements = [
        WSU.Created(today_datetime.strftime("%Y-%m-%dT%H:%M:%SZ")),
        WSU.Expires(expires_datetime.strftime("%Y-%m-%dT%H:%M:%SZ"))
]
timestamp_token.extend(timestamp_elements)
# here I don't have a user, do I need it to have a timestamp?
user_name_token = UsernameToken('user', '', timestamp_token=timestamp_token)

client = Client(
    'webServiceBrowsingCards.wsdl',
    wsse=CustomSignature([user_name_token, signature]))
#    wsse=signature,
#    wsse=[user_name_token, signature],

request_type = client.get_type('ns0:getAvailableSources')
bean_type = client.get_type('ns0:accessBean')
beanr = bean_type(systemIP='xxx.xxx.xxx.xxx', systemID='xxxxxxx', systemName='xxxxx')
br_type = client.get_type('ns0:baseRequest')
br = br_type(accessBean=beanr, distinct=0)
request = request_type(arg0=br)
breakpoint()
client.service.getAvailableSources(request)

I get this error:

zeep.transports: HTTP Response from http://sigecweb.beniculturali.it/webServiceBrowsingCards (status: 500):
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><soap:Fault><faultcode xmlns:ns1="http://ws.apache.org/wss4j">ns1:SecurityError</faultcode><faultstring>A security error was encountered when verifying the message</faultstring></soap:Fault></soap:Body></soap:Envelope>
zeep.exceptions.Fault: A security error was encountered when verifying the message

I've a java jar wich works and produce a header and body encrypted (capture from LoggingInInterceptor/LoggingOutInterceptor):

  <soap: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" soap: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#X509v3" wsu:Id="X509-1D06AFF1B26A357FF616217897768921">binary stuff</wsse:BinarySecurityToken>
      <wsu:Timestamp wsu:Id="TS-1">
        <wsu:Created>2021-05-23T17:09:36.869Z</wsu:Created>
        <wsu:Expires>2021-05-23T17:14:36.869Z</wsu:Expires>
      </wsu:Timestamp>
      <xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="EK-1D06AFF1B26A357FF616217897769814">
        <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>
            <ds:X509Data>
              <ds:X509IssuerSerial>
                <ds:X509IssuerName>CN=sigec,OU=Unknown,O=ICCD,L=Roma,ST=Roma,C=IT</ds:X509IssuerName>
                <ds:X509SerialNumber>2134296824</ds:X509SerialNumber>
              </ds:X509IssuerSerial>
            </ds:X509Data>
          </wsse:SecurityTokenReference>
        </ds:KeyInfo>
        <xenc:CipherData>
          <xenc:CipherValue>binary stuff</xenc:CipherValue>
        </xenc:CipherData>
        <xenc:ReferenceList>
          <xenc:DataReference URI="#ED-3"/>
          <xenc:DataReference URI="#ED-4"/>
        </xenc:ReferenceList>
      </xenc:EncryptedKey>
      <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="ED-4" Type="http://www.w3.org/2001/04/xmlenc#Element">
        <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
        <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
          <wsse:SecurityTokenReference xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey">
            <wsse:Reference URI="#EK-1D06AFF1B26A357FF616217897769814"/>
          </wsse:SecurityTokenReference>
        </ds:KeyInfo>
        <xenc:CipherData>
          <xenc:CipherValue>binary stuff</xenc:CipherValue>
        </xenc:CipherData>
      </xenc:EncryptedData>
    </wsse:Security>
  </soap:Header>
  <soap:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Id-103118806">
    <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="ED-3" Type="http://www.w3.org/2001/04/xmlenc#Content">
      <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <wsse:SecurityTokenReference 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" wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey">
          <wsse:Reference URI="#EK-1D06AFF1B26A357FF616217897769814"/>
        </wsse:SecurityTokenReference>
      </ds:KeyInfo>
      <xenc:CipherData>
        <xenc:CipherValue>binary stuff</xenc:CipherValue>
      </xenc:CipherData>
    </xenc:EncryptedData>
  </soap:Body>
</soap:Envelope>

As you can see, everything is encrypted and there's a timestamp.

As I said before, I don't understand where Timestamp has to appear.

My code produce this:

<?xml version='1.0' encoding='utf-8'?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"><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-94f34d28-7f58-42f1-93c8-134c53c752c2">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>TDyLKPG7fCYemJCgHcwi6K4iDA8=</DigestValue>
</Reference>
<Reference URI="#id-a1a1072e-4379-4e63-9332-d8d16087c214">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>He7d8iTrVAmSlg2d35Siq1PMFzY=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>binary stuff</SignatureValue>
<KeyInfo>
<wsse:SecurityTokenReference><X509Data>
<X509IssuerSerial>
<X509IssuerName>issuers info</X509IssuerName>
<X509SerialNumber>929749877</X509SerialNumber>
</X509IssuerSerial>
<X509Certificate>binary stuff</X509Certificate>
</X509Data>
</wsse:SecurityTokenReference></KeyInfo>
</Signature>
<wsse:UsernameToken>
<wsse:Username>user</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"></wsse:Password></wsse:UsernameToken>
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-a1a1072e-4379-4e63-9332-d8d16087c214">
<wsu:Created>2021-05-23T19:34:54Z</wsu:Created>
<wsu:Expires>2021-05-23T19:44:54Z</wsu:Expires>
</wsu:Timestamp></wsse:Security>
</soap-env:Header>
<soap-env:Body xmlns:ns1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" ns1:Id="id-94f34d28-7f58-42f1-93c8-134c53c752c2">
<ns0:getAvailableSources xmlns:ns0="http://webservice.sigec.iccd.it/"><arg0 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns0:getAvailableSources">
<arg0>
<accessBean>
<systemID>xxx</systemID>
<systemIP>xxx.xxx.xxx.xxx</systemIP>
<systemName>xxxxx</systemName>
</accessBean>
<distinct>false</distinct>
</arg0>
</arg0>
</ns0:getAvailableSources>
</soap-env:Body>
</soap-env:Envelope>

Any idea/example/hint? Certs are ok.

Thanks very much for any info!

yurj avatar May 23 '21 17:05 yurj

The Timestamp is on the BinarySecurityToken, so I think I've to move Timestamp from UserToken to BinarySecurityToken. Still unclear how to encrypt the body and header.

yurj avatar May 24 '21 08:05 yurj

Maybe I have to use sign_envelope?

def sign_envelope(
    envelope,
    keyfile,
    certfile,
    password=None,
    signature_method=None,
    digest_method=None,
):
    """Sign given SOAP envelope with WSSE sig using given key and cert.

    Sign the wsu:Timestamp node in the wsse:Security header and the soap:Body;
    both must be present.

    Add a ds:Signature node in the wsse:Security header containing the
    signature.

Looking at the comment, it seems exacly what I need, but maybe the body is not encrypted. Can someone confirm this?

The problem is that there should be a way to encrypt subelement of soap:body tag, but I cannot find any info in zeep docs.

yurj avatar May 24 '21 10:05 yurj

I've solved the timestamp following #996.

Now lets go with the next.

The server now answer:

{http://schemas.xmlsoap.org/ws/2005/07/securitypolicy}AsymmetricBinding: The signature is not protected. Here the full error:

*** zeep.exceptions.Fault: These policy alternatives can not be satisfied: 
{http://schemas.xmlsoap.org/ws/2005/07/securitypolicy}AsymmetricBinding: The signature is not protected
{http://schemas.xmlsoap.org/ws/2005/07/securitypolicy}InitiatorToken
{http://schemas.xmlsoap.org/ws/2005/07/securitypolicy}RecipientToken
{http://schemas.xmlsoap.org/ws/2005/07/securitypolicy}EncryptSignature
{http://schemas.xmlsoap.org/ws/2005/07/securitypolicy}EncryptedParts: Soap Body is not ENCRYPTED

The server uses cxf, the error happen here (https://github.com/apache/cxf/blob/master/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/policyvalidators/AbstractBindingPolicyValidator.java#L146):

        // Check whether the signatures were encrypted or not
                if (binding.isEncryptSignature() &amp;&amp; !isSignatureEncrypted(results.getResults())) {
                    ai.setNotAsserted("The signature is not protected");
                    return false;
                }

I think that "AsymmetricBinding: The signature is not protected" and "EncryptedParts: Soap Body is not ENCRYPTED" have the same cause: the body is not encrypted. Adding encryption should also solving the other parts:

{http://schemas.xmlsoap.org/ws/2005/07/securitypolicy}InitiatorToken
{http://schemas.xmlsoap.org/ws/2005/07/securitypolicy}RecipientToken
{http://schemas.xmlsoap.org/ws/2005/07/securitypolicy}EncryptSignature

So the main question is: how do I encrypt the body, adding the initiator/recipient token? Thanks for any pointer.

yurj avatar May 24 '21 15:05 yurj

Is this use case unsupported?

yurj avatar May 28 '21 09:05 yurj

Hey yurj, i'm having a similar problem. Did you find a solution? thanks

conradogarciaberrotaran avatar Dec 15 '21 19:12 conradogarciaberrotaran

Unfortunately, no. I think it is not supported, it has to be done at low level but xmlsecurity is quite complex.

yurj avatar Dec 16 '21 07:12 yurj