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?

  • Since this is for a CTF, I've replaced the actual values with working placeholders. ;)
    – Lery
    Jul 4, 2017 at 22:24

3 Answers 3


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.

  • 2
    Googled it and got one of my own answers :) Jul 4, 2017 at 16:38
  • 2
    The 5Fs appear in the decrypted message.
    – robert
    Jul 4, 2017 at 16:53
  • 1
    Yes, padding is performed before encryption with the block cipher / mode of operation and therefore after decryption. Jul 4, 2017 at 16:57
  • 2
    OK, I understand now. Using -nopad there are no more error messages, but the output is wrong, some garbage.
    – robert
    Jul 4, 2017 at 17:07
  • 1
    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. Jul 4, 2017 at 18:22

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.


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).


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.


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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .