For the purpose of the tool I am currently writing, I need to perform some AES encryption/decryption on some input. It's like a pipeline: you feed it data, it runs the algorithm and pass it out to the next modifier.

I'm used to openssl enc -aes-256-cbc to encrypt and decrypt everything. My understanding of this is, it first derive the key you enter using some kind of pbkdf-sha1 with some 8 bytes generated salt, then derive the iv from the derived key + salt.

It obviously only works with OpenSSL since I wasn't able to find any standard regarding how to communicate – or write to file – the iv and salt parameters. I was expecting some kind of RFC (like for elliptic curve parameters) but no luck.

My idea is to have a 64 bytes header: salt + iv first in the file, then it is followed by the encrypted data. I will use the salt with pbkdf2-sha256 to derive the key.

The headers don't have a fixed length, adjusting the right parameters when you start the program will get the salt and iv set to the right values.

I don't want to make no mistake. I was amazed how it could be the weakest link and how easy it is to recover a password from a bad key derivation and/or IV generation.

I am pretty sure every aes implementation rolls their own protocol on parameters. And I think it is sad.

I will of course be using crypto/aes from Go and their pbkdf2, always taking the random source from crypto/rand and not math/rand.

To sum up:

  • Is there any standard on having AES parameters like salt and iv?

  • Should I generate the iv or derive it from the salt || dk?


I forgot to mention that I was going to use PKCS 5#2.0 which specify how to derive the key, but this question was more towards best practices on the iv and salt storing for broader compatibility.

2 Answers 2


First of all, the salt is not an AES parameter. You have two different primitives here:

You would have:

  • pbkdf(salt, password) returns aes_key

  • aes(iv, aes_key)

The IV size should match the block size you are using (typically 128-bit as that's the one standarised, although other block sizes are possible).

Generate a new, random, iv for each file you want to encrypt.

Every AES implementation must provide the same algorithm. The way you choose to save data on disk is what differs.

A common solution would be to simply append everything:

file_password_salt || file_iv || file_contents

However, a more future-proof solution would embed some parameter pointing to the size of the fields and the algorithm used. Or, at least, a version number (eg. v1 PBKDF1 with SHA1 and AES-128, in a few months you may want to use Argon, with Camellia…).

Then, you could decide to use a format already used by other tools, so that it is compatible with them. For example copying what OpenSSL does (see that "SALTED" string?), or making it output a zip wrapper over the aes content.

  • I see, yes you are right. I've looked at that "Salted" string but it appeared to just be a trick to be backward compatible with the --nosalt option. I also looked at PKCS 5#2.1. So I am going to append everything to file since it is secure. Also rework on the question. One more thing, is it better to derive the iv from the salt || dk? The only benefit I see is to save space, but realistically speaking, it's not that long. Thank you for the response though.
    – tehmoon
    Commented Nov 29, 2017 at 0:27
  • @tehmoon Generate a new, random iv. The iv must be unique. Given that you generate a proper random salt per file, it could be acceptable to derive it and hope that don't enable a related key attack somehow. But it's much easier to be on the safe side and simply generate a new iv.
    – Ángel
    Commented Nov 29, 2017 at 0:38
  • AES128,192,256 are key sizes; the data block size and thus IV size for most modes including CBC is always 128 bits = 16 octets. Rijndael does support other block sizes, but they are not standard and as a result are not widely implemented or interoperable. Commented Nov 29, 2017 at 1:04
  • @dave_thompson_085 thanks. Indeed only iv size matters , as the key size is derived through pbkdf. I have edited the answer to clarify this.
    – Ángel
    Commented Nov 29, 2017 at 1:29

Near cross-dupe https://crypto.stackexchange.com/questions/27444/standard-format-to-encode-aes-cipher-and-random-iv

openssl enc derives both key and IV from pw and salt, based on but modified from PKCS5 v1, see (my) https://crypto.stackexchange.com/questions/3298/is-there-a-standard-for-openssl-interoperable-aes-encryption -- but don't take that as an example to follow.

In addition to ASN.1-based CMS, which is several RFCs (see link above), OpenPGP format (using its own encoding) supports password-based encryption and is RFC 4880, and the standards for XML encryption and signature are published by w3.org in an open process similar to RFCs but not under IETF. Both PKCS7/CMS and PGP were originally designed for public-key encryption (really hybrid public-key plus symmetric) and public-key signature (ditto), but options for password-based (plus symmetric) encryption were added.

If you need pipeline/streaming operation it will be harder to add (or include) authentication, very likely leaving you open to active attacks.

  • Please, don't choose XML as your encryption format!
    – Ángel
    Commented Nov 29, 2017 at 2:00
  • @Ángel: I don't particularly recommend XMLenc/dsig, or indeed XML for anything, but the question is 'standard ... how to communicate [encryption] parameters'. OTOH according to Gutmann X.509 certs are also totally impossible to implement and no one in the world will ever be able to make an HTTPS connection. Do you know of anyone who has made an HTTPS connection? For example, has anybody connected to any Stack site in the last year or so? Commented Nov 29, 2017 at 6:40

You must log in to answer this question.

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