0

I have the following 2 values : AES key it is a Java array of bytes

64,67,-65,88,-19,-118,-16,-53,-81,-98,44,-83,82,-90,124,112,-120,42,92,67,104,23,44,8,2,-96,-106,-28,57,99,76,73

IV also a Java array of bytes

50,52,3,36,-90,81,109,-56,24,122,-3,27,-96,56,118,-60

The encryption mode is : AES/CBC/PKCS5PADDING

Now I need to use these 2 values to decrypt my cipher text using python. I am trying to use pycrypto but am open on exploring other options as well. Now because Java's array of bytes is signed and (it looks like) python (2.7) does not recognize signed array of bytes, I am having trouble doing this decryption.

I did find a bunch of SO discussions around similar topics, but most of them were generating the IV and the key, and not really using an already existing one, for eg.

So the above solutions have not worked for me, unless I did something fundamentally wrong while tweaking them for my need.

Here's one of my attempts (bits and pieces taken from the SO discussions mentioned above)

from Crypto import Random
from Crypto.Cipher import AES
import base64

key = 
[64,67,-65,88,-19,-118,-16,-53,-81,-98,44,-83,82,-90,124,112,-120,42,92,67,104,23,44,8,2,-96,-106,-28,57,99,76,73]

iv = [50,52,3,36,-90,81,109,-56,24,122,-3,27,-96,56,118,-60]

aes_key = ''.join(map(lambda x: chr(x % 256), key))
iv_data = ''.join(map(lambda x: chr(x % 256), iv))

def pad(s):
  return s + b"\0" * (AES.block_size - len(s) % AES.block_size)

def decrypt(ciphertext, key):
  ciphertext = base64.b64decode(ciphertext)
  iv = ciphertext[:AES.block_size]
  cipher = AES.new(key, AES.MODE_CBC, iv)
  plaintext = cipher.decrypt(ciphertext[AES.block_size:])
  return plaintext.rstrip(b"\0")  

And then calling decrypt() as :

decrypt('HaCiIld192v2QE3/zcuAxhpr39HRXvsHQYSJE/VT9FuWWK7envVDRaaDR6gpmh0zmw09/440O7UAtVb5qBLddZH37TASWUMXAN0Idy7B0VtpgPgMHOLGJG6axWCHgieaTleFDaRKP+z7WN+4fj5RGw==', aes_key)

gives the output as :

Out[9]: '\xdd\x99\xab\x8bI\xcb\xf0?\xcd\x1c\x1eb\x109\xe8c\xc0\x105\xa6\xdb\xe4\xdb\x08\xc7\xdbHT\x08.\x10\x9e\xe1g\xfe\xd1\xc2\x92\xdbH\x8e\x9bV\xae^\r:\x98%\x8d\x06\xdf\xaf\t\xb5m\xfb\xf3Q+s"CK\xf4\xc2\xe7\xd2\xfd\xfdq\xcc)\xcf\x99&\xffj\xc4\xc1\x1a$ML\xe9\xe7(\xef\x1c\x81vx\xf56'

I tried :

a = b'\xdd\x99\xab\x8bI\xcb\xf0?\xcd`\x1c\x1eb\x109\xe8c\xc0\x105\xa6\xdb\xe4\xdb\x08\xc7\xdbHT\x08.\x10\x9e\xe1g\xfe\xd1\xc2\x92\xdbH\x8e\x9bV\xae^\r:\x98%\x8d\x06\xdf\xaf\t\xb5m\xfb\xf3Q+`s"CK\xf4\xc2\xe7\xd2\xfd\xfdq\xcc)\xcf\x99&\xffj\xc4\xc1\x1a$ML\xe9\xe7(\xef\x1c\x81vx\xf56'

a.encode("hex")

and output is :

'dd99ab8b49cbf03fcd601c1e621039e863c01035a6dbe4db08c7db4854082e109ee167fed1c292db488e9b56ae5e0d3a98258d06dfaf09b56dfbf3512b607322434bf4c2e7d2fdfd71cc29cf9926ff6ac4c11a244d4ce9e728ef1c817678f536'

which is definitely not the plain text I was expecting. So I need help figuring this out, as to how can I decrypt (using Python 2.7) an AES encrypted cipher text given the key and IV as Java array of bytes?

As per suggestions in the comments below (@MaartenBodewes thanks) I tried this :

So, I checked the base64 of the bytearray, key, in java as :

byte arr[] = new byte[] {64,67,-65,88,-19,-118,-16,-53,-81,-98,44,-83,82,-90,124,112,-120,42,92,67,104,23,44,8,2,-96,-106,-28,57,99,76,73};
String base64 = new String(Base64.encodeBase64(arr)); 

and then to achieve the same in python did some changes to python code above to do the same as : (I realized that java treats a byte array as an array of signed 2s complement data, from an excellent discussion at Java byte array contains negative numbers, and so had to accommodate for that in python)

key = [64,67,-65,88,-19,-118,-16,-53,-81,-98,44,-83,82,-90,124,112,-120,42,92,67,104,23,44,8,2,-96,-106,-28,57,99,76,73]
b64encode(bytearray([(lambda x:int((bin(x & 0xff)),2))(x) for x in key]))

and both these resulted in the same output as :

QEO/WO2K8MuvniytUqZ8cIgqXENoFywIAqCW5DljTEk=

And I thought that not that I am sure that both these keys (Java and python) are the same, the decryption should work. And hence I tried once again:

decrypt('HaCiIld192v2QE3/zcuAxhpr39HRXvsHQYSJE/VT9FuWWK7envVDRaaDR6gpmh0zmw09/440O7UAtVb5qBLddZH37TASWUMXAN0Idy7B0VtpgPgMHOLGJG6axWCHgieaTleFDaRKP+z7WN+4fj5RGw==', str(aes_key))

And I got the exact same output as earlier:

'\xdd\x99\xab\x8bI\xcb\xf0?\xcd\x1c\x1eb\x109\xe8c\xc0\x105\xa6\xdb\xe4\xdb\x08\xc7\xdbHT\x08.\x10\x9e\xe1g\xfe\xd1\xc2\x92\xdbH\x8e\x9bV\xae^\r:\x98%\x8d\x06\xdf\xaf\t\xb5m\xfb\xf3Q+s"CK\xf4\xc2\xe7\xd2\xfd\xfdq\xcc)\xcf\x99&\xffj\xc4\xc1\x1a$ML\xe9\xe7(\xef\x1c\x81vx\xf56'

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
qre0ct
  • 5,680
  • 10
  • 50
  • 86
  • 2
    There are no negative values in a byte array. They are just an interpretation of the data, mostly when the first bit is `1`. So, you will have to check if the representation fits your conversion or in other word if the `% 256 properly reverts the array to right bytes. – Klaus D. Jun 26 '17 at 08:22
  • Thanks for the suggestion. Could you please explain how exactly can I verify this ? I mean I do not really know what the *right* bytes would be. – qre0ct Jun 26 '17 at 08:40
  • 1
    You're not doing PKCS#5 (or, for AES, PKCS#7 padding) you are zero padding your plaintext in PHP. Compare the key and IV and plaintext in hexadecimals or base 64 by using a print statement or suchlike for both PHP and Java. If there are differences you know what went wrong. Note that to print hexadecimals in Java you may need extra code. Java inexplicably doesn't contain a hex encoder for byte arrays. – Maarten Bodewes Jun 26 '17 at 08:40
  • @qre0ct Well, you have to know how the keys and IV got exported. You did not show that above. – Klaus D. Jun 26 '17 at 08:43
  • Sorry for the insult to Python here, I mean "Python", not "PHP". – Maarten Bodewes Jun 26 '17 at 09:52
  • @KlausD. java bytes are signed. – pvg Jun 26 '17 at 10:34
  • What is the plaintext you are expecting? Without the Java code or indeed the plaintext it is hard to determine what is wrong... – Maarten Bodewes Jun 26 '17 at 11:20
  • I agree, but the thing is I don't really know the plain text exactly, except that it is supposed to be some kinda HTTP request body (JSON would be my best guess). So this is basically part of a red team-blue team exercise I am involved in, me playing the red here. And I think I know what could possibly be going wrong. Let me just try those experiments and confirm if my doubts are correct . – qre0ct Jun 26 '17 at 11:23
  • So my guess was right. I was actually trying to decrypt the cipher text with a wrong AES key (that I had assumed was right). Now I am getting properly decrypted text as expected, except that the data is incomplete. Meaning when I am getting the decrypted text some part of the text is just missing. Not sure why ! – qre0ct Jun 26 '17 at 14:47
  • Ok. Finally got the exact answer. So the only change needed was `plaintext = pkcs5_unpad(cipher.decrypt(ciphertext[0:]))` in the *decrypt()* above. – qre0ct Jun 27 '17 at 02:01

0 Answers0