pythonsoapmtomwssexop

Unable to decrypt MTOM/XOP attachment using SUDS (python)


I have a SOAP client that downloads a file from a server. The request has its body and attachment (the file) encrypted using two separate keys. Both keys are included in respective <xenc:EncryptedKey> tags. I can decrypt the body no problem using one of the keys, however the attachment is giving me issues.

My code:

from Crypto.Cipher import AES
from Crypto import Random

class AESCipher:
    def __init__( self, key, BS = 16):
        self.key = key
        self.BS = BS
    def _pad(self, s):
        return s + (self.BS - len(s) % self.BS) * chr(self.BS - len(s) % self.BS) 
    def _unpad(self, s):
        return s[:-ord(s[len(s)-1:])]
    def decrypt( self, enc ):
        enc = base64.b64decode(enc)
        iv = enc[:self.BS]
        cipher = AES.new(self.key, AES.MODE_CBC, iv )
        return self._unpad(cipher.decrypt( enc[self.BS:]))

with open('test/resp-down-file','rb') as f:
    encFile = f.read()

#...the key is extracted elsewhere...

cryptor = AESCipher(key)
cryptor.decrypt(encFile)

At best i'm getting garbled result, but usually it just says Error while decrypting in CBC mode. Question: has anyone come across this issue? I'm open to suggetions in Python, Java, PHP, Perl, C, pretty much anything that runs on linux. Is there something special about the way MTOM/XOP attachments are encrypted?

I saw this question already, but it doesn't have a correct answer. The octet/stream refers to the content type, not delivery mechanism, so that answer is incorrect.

Edit: the server spec says they encrypt the message using AES128-CBC algorithm with PKCS5 padding. It doesn't make sense to me that they use DES padding for AES encryption, but they're adamant about that.

Edit2: sometimes the attached message doesn't have the correct length for AES128 decryption (e.g. 6023 bytes or 4071 bytes).

For reference the message comes in in this format:

--MIMEBoundaryurn_uuid_641B9D88B371C8A80C1501095406237
Content-Type: text/xml; charset=UTF-8
Content-Transfer-Encoding: binary
Content-ID: <0.urn:uuid:641B9D88B371C8A80C1501095406238@apache.org>

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
...
</soapenv:Envelope>

--MIMEBoundaryurn_uuid_641B9D88B371C8A80C1501095406237
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <urn:uuid:641B9D88B371C8A80C1501095406240@apache.org>

­8eJ¨%• }\  Œ“\ò½<( nË%¸£käö0   ‡XW�5ìR Ë�¾p�Áëş3Âå'¹5¥#=Zg¡øø{I~FP�n ×aµR^Föž¤¥EÍf«Îê�0qÊMö²È€]®PÌ>A@‡ Cş®±9>Áf7P’#ã  …fç~yxÔ.å–×v›±Cô„Ê
...
--MIMEBoundaryurn_uuid_641B9D88B371C8A80C1501095406237--

Solution

  • I figured out the problem. Turns out, the way I received the data (using result = requests.post(....) trimmed some non-printable characters because I was using result.text.

    Now I've switched to result.raw.read() and the issue is resolved.