There are many questions around this topic, but I haven't found one that explains exactly my issue.

I am encrypting data with AES 128 CBC, but using python and pycryptodome (should have used the python openssl, but couldn't find it) and specifying the key and iv (which is generally random). So, the key is not salted.

When I try using openssl 1.1.0g with ... | openssl enc -aes128 -d -K <key> -iv <initialization vector>, the operation fails with the 'digital envelope' failure after decrypting most of the string. If I add `-nopad', then it works completely, without error. The output difference is 11 bytes.

The original input is padded with binary 0s to a multiple of 16 bytes before encrypting, the encrypted string is converted to hex, then the hex iv is prepended and everything is base64 encoded.

Can anyone shed any light on why I need the -nopad option? I thought that with the -K and -iv options, openssl wouldn't process the salt at all for the decryption.


According to man enc:

All the block ciphers normally use PKCS#5 padding also known as standard block padding

PKCS#5 padding (technically PKCS#7 here) is done by adding N bytes of value N to the plaintext so that it is evenly divisible by the block size. If you don't specify -nopad, enc will expect that, for example, a 30 byte plaintext would be encrypted to a 32 byte ciphertext, with the last 2 plaintext bytes each having the value 2.

PKCS#5 padding requires that there always be at least one padding byte, so in the worst case you'll have an entire block of padding, but the upside is that when decrypting the last byte can always be checked to see how much padding needs to be stripped. This prevents any ambiguity between padding and data.

In your case, you've padded the plaintext with bytes of value 0, which causes the decryption error. This is called zero padding or null padding, and isn't generally used as it's not possible to distinguish between padding and data if the data can contain null bytes. When using -nopad, enc will require the input to be evenly divisible by the block size when encrypting, and when decrypting it won't attempt to strip any padding, leaving you to deal with it yourself (which is why you're getting those 11 extra bytes).

As @dave_thompson_085 noted, the salt is only used by enc to derive the key and IV when using a password. When specifying the IV and key directly, there is no salt.

  • And to emphasize: padding applies to the symmetric encryption/decryption which is separate from the (password-based) key derivation which (usually) includes salt. – dave_thompson_085 Mar 12 at 3:34
  • I implemented this and it completely works. It decrypts properly without the '-nopad' option (Thank you!). But I am curious why enc can identify that the padding is actually padding. What is so special about having a N bytes each with a value of N? Although that would stand out, couldn't ordinary binary data have that pattern? Many years ago when I developed AES encryption code for media over RTP, we prefixed the encrypted data with a 1 byte field of the number of padding bytes. – Chuck Crisler Mar 12 at 21:10
  • @ChuckCrisler Does that help? Including either the plaintext length or the amount of padding at the front of the plaintext works as well (in general I mean, not with enc), but when encrypting a stream both length and amount of padding will be unknown until the stream ends. – AndrolGenhald Mar 12 at 21:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.