Unable to decrypt AES with OpenSSL

12,297

Solution 1

Because openssl uses PKCS#7 padding by default, and your plaintext doesn't contain PKCS#7 padding. If your plaintext has been padded then it has been padded with bytes of value 5F. Use the option -nopad instead, padding with value 5F isn't any padding scheme known to me; if it needs to be removed you will need to remove it yourself.

You currently show the input in hexadecimals. Hexadecimal is the representation of the bytes, not the byte values themselves. You need to either directly input the source material from a file using the < for your file or by hex decoding the input.

The output will also be binary; it will not represent any readable plaintext. So you may need to convert the output to hexadecimals before comparing it to the values in your question.

Solution 2

Well, you might want to consider using python or any other scripting/programming language to do such things.

The advantages of doing it programmatically are:

  • Whenever you'll need to redo something similar, you'll have your code ready.
  • You can write comments to explain what is going on, so if you once need to understand what you previously did, hopefully the code plus the comments will allow you to do so.
  • Many things such as encoding, byte handling, etc. are easier than in the console
  • Most languages are cross platform, so it'll work on Windows, Linux, Android easily, if you once switch your device.

Regarding your problem at hand, it may be solved using python to do everything:

# we import the function we need from common librairies
from base64 import b64decode
from Crypto.Cipher import AES
from binascii import hexlify, unhexlify


# First we decode the message and the key from base64 into bytes:
msg = b64decode("8LBUVZfDfI6wnggG1uUYuQsRoGd08pGwHCN++R5rabMW9PJmWHWcSrjy5Tfffj6L")
key = b64decode("3q1FxGhuZ5fQYbjzDxgQ35==")

# We then instantiate a cipher_suite using AES with the provided key, in ECB mode
cipher_suite = AES.new(key, AES.MODE_ECB)

# We can decrypt the message using our cipher_suite:
recovered = cipher_suite.decrypt(msg)

# We can print it:
print ("plaintext: ", recovered)

# There is some garbage at the end, but if we display it in hexadecimal form:
print ("in hex:", hexlify(recovered))
# We can see it's just padding using '5f', so let's create a function to remove such padding:
def unpad(padded):
  # we declare the value of our padding:
  paddingByte = unhexlify('5f')
  # we do a loop, while the last byte is padding
  while padded[-1:]==paddingByte:
    # we remove the last byte
    padded = padded[:-1]
  # once it's done, we return
  return padded

# We can now use our function to remove padding:
print ("unpadded: ", unpad(recovered))

Now, if you don't want to learn Python or any other language, and/or if you really want to do it all in your terminal, it is also possible: then you could do everything directly using pipes to pass the data from one command to the other, command substitution to feed the right key to openssl, and the commands base64 to handle base64 plusxxd to convert binary data to hex (for the key in openssl), and finally use sed to remove the 5f padding:

echo "8LBUVZfDfI6wnggG1uUYuQsRoGd08pGwHCN++R5rabMW9PJmWHWcSrjy5Tfffj6L" | base64 --decode | openssl enc -d -K $(echo "3q1FxGhuZ5fQYbjzDxgQ35==" | base64 --decode | xxd  -c 16 -ps) -aes-128-ecb -nosalt -nopad | sed 's/_*$//g'

I don't know why, but I personally find the python approach cleaner.


You also mentioned that you obtained garbage using the way Maarten Bodewes indicated you, this is coming from the fact that you are feeding hexadecimal values to OpenSSL, while you should provide directly the binary data (not the hexadecimal value) for the message, while you should provide the key in hexadecimal:

echo -n f0b0545597c37c8eb09e0806d6e518b90b11a06774f291b01c237ef91e6b69b316f4f26658759c4ab8f2e537df7e3e8b | xxd -r -p | openssl ...

PS: you should probably avoid posting the actual values you encounter in CTFs, since it may spoil the game for people whose first reflex is to google the values.

Solution 3

decode base64 by

echo 8LBUVZfDfI6wnggG1uUYuQsRoGd08pGwHCN++R5rabMW9PJmWHWcSrjy5Tfffj6L | base64 -D > aesdata.dat

(the -D is a mac OS quirk. Linux tends to use -d or --decode instead).

Similarly:

echo 3q1FxGhuZ5fQYbjzDxgQ35== | base64 -D > aeskey.dat

but openssl expects hex values in parameters (but binary in cipher files):

xxd -p < aeskey.dat gives dead45c4686e6797d061b8f30f1810df. Or piped from the previous command, if you want to avoid clutter.

Finally:

openssl enc -d -nopad -aes-128-ecb -K dead45c4686e6797d061b8f30f1810df -in aesdata.dat -out plain

will decrypt it. The -nopad avoids the decryption error, as non-standard padding is used.

Now hd plain to check the result, which is indeed what you were looking for.

Share:
12,297
robert
Author by

robert

Updated on September 18, 2022

Comments

  • robert
    robert over 1 year

    I am working on a ctf game:

    Encrypted with AES in ECB mode. All values base64 encoded

    ciphertext = 8LBUVZfDfI6wnggG1uUYuQsRoGd08pGwHCN++R5rabMW9PJmWHWcSrjy5Tfffj6L
    key = 3q1FxGhuZ5fQYbjzDxgQ35==
    

    I tried to decrypt it in my terminal leaving the cyphertext in base64 and using the -base64 flag, without luck. Then I went to http://extranet.cryptomathic.com/aescalc, where, after converting the values to hex I was able to decrypt:

    key: DEAD45C4686E6797D061B8F30F1810DF 
    text: F0B0545597C37C8EB09E0806D6E518B90B11A06774F291B01C237EF91E6B69B316F4F26658759C4AB8F2E537DF7E3E8B
    out: 7B796F755F73686F756C645F6E6F745F706F73745F7468655F61637475616C5F6374665F76616C75657D5F5F5F5F5F5F
    

    Then I returned to my terminal trying:

    echo -n F0B0545597C37C8EB09E0806D6E518B90B11A06774F291B01C237EF91E6B69B316F4F26658759C4AB8F2E537DF7E3E8B | openssl enc -d -K DEAD45C4686E6797D061B8F30F1810DF -aes-128-ecb -nosalt
    

    but I got the same error:

    bad decrypt
    140735124906848:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:531:
    

    I have tried this on an Ubuntu 17.04, and now on my MacOSX using OpenSSL 1.0.2l. Why can I not decrypt in my own terminal?

    • Lery
      Lery almost 7 years
      Since this is for a CTF, I've replaced the actual values with working placeholders. ;)
  • Maarten Bodewes
    Maarten Bodewes almost 7 years
    Googled it and got one of my own answers :)
  • robert
    robert almost 7 years
    The 5Fs appear in the decrypted message.
  • Maarten Bodewes
    Maarten Bodewes almost 7 years
    Yes, padding is performed before encryption with the block cipher / mode of operation and therefore after decryption.
  • robert
    robert almost 7 years
    OK, I understand now. Using -nopad there are no more error messages, but the output is wrong, some garbage.
  • Maarten Bodewes
    Maarten Bodewes almost 7 years
    AES is a 128 bit / 16 byte block cipher and it is therefore not possible to use PKCS#5 compatible padding, as it is only defined for 64 bit / 8 byte block ciphers. If the output looks like garbage then it is probably because the output is interpreted to be US-ASCII / UTF-8 (or any other character set on your system). Direct the output to file using > and then open it in a hex editor, then compare results, not before.
  • Lery
    Lery almost 7 years
    @robert Actually you obtain garbage because Openssl expects bytes as input, not a hexadecimal string like you feed it with. You may use echo -n DEADC0DE | xxd -r -p | openssl ... to get the actual data.
  • Maarten Bodewes
    Maarten Bodewes almost 7 years
    @Lery integrated this in my answer, forgot about the input. D'oh.