-
Notifications
You must be signed in to change notification settings - Fork 0
OpenSSL Standards
I plan to keep this page up to date with OpenSSL standards for interoperability.
OpenSSL pads its AES 16 byte blocks, with a value equal to the number of padded bytes (this approach is known as PKCS7 padding, see for example http://www.cryptostuff.com/crypto/index.php?title=blockpad).
For example, the following unencrypted 16 Byte block
| ff | ff | ff | ff | |:---|:---|:---|:---| | ff | ff | ff | 03 | | ff | ff | ff | 03 | | ff | ff | ff | 03 |
has a final byte value of 0x03, which means that the 3 last bytes are padding and should be removed. This rule also means that when presented with a final block of exactly 16 bytes, one would need to add a block filled with the byte value of 16 or 0x10.
Example
| ee | aa | bb | aa | |:---|:---|:---|:---| | ff | bb | aa | bb | | aa | cc | cc | cc | | bb | dd | dd | dd |
| 10 | 10 | 10 | 10 | |:---|:---|:---|:---| | 10 | 10 | 10 | 10 | | 10 | 10 | 10 | 10 | | 10 | 10 | 10 | 10 |
http://www.rsa.com/rsalabs/node.asp?id=2127
OpenSSL uses PKCS 5 v1.5 to generate a key and iv from a password string and randon 8 byte salt. OpenSSL only used a MD5 hash iteration of 1, where as most PKCS 5 docs call for iterations of 2000 or more in order to make dictionary attacks much more expensive. I'm not sure why this is the default, or if it's possible to change it(doesn't look like it).
The following code is my JS translated from the C# code at: http://jensign.com/JavaScience/dotnet/DeriveKeyM/index.html
// Takes a password and salt as a byte
array openSSLKey: function(passwordArr, saltArr) {
// Number of rounds depends on the size of the AES in use
// 3 rounds for 256 // 2 rounds for the key, 1 for the IV
// 2 rounds for 128 // 1 round for the key, 1 round for the IV
// 3 rounds for 192 since it's not evenly divided by 128 bits
var rounds = this.Nr >= 12 ? 3: 2;
var key = [];
var iv = [];
var md5_hash = [];
var result = [];
data00 = passwordArr.concat(saltArr);
md5_hash[0] = GibberishAES.Hash.MD5(data00);
result = md5_hash[0];
for (var i = 1; i < rounds; i++) {
md5_hash[i] = GibberishAES.Hash.MD5(md5_hash[i - 1].concat(data00));
result = result.concat(md5_hash[i]);
}
key = result.slice(0, 4 * this.Nk);
iv = result.slice(4 * this.Nk, 4 * this.Nk + 16);
return {
key: key,
iv: iv
};
},
This code example relies on a MD5 hash digest that accepts and returns byte arrays. It can be found in the GibberishAES open source library at http://github.com/markpercival/gibberish-aes.
Basically what we are doing here is:
md5_hash[0] = MD5Digest(password | salt)
md5_hash[1] = MD5Digest(md5_hash[0] | password | salt)
md5_hash[2] = MD5Digest(md5_hash[1] | password | salt)
key = md5_hash[0] | md5_hash[1]
iv = md5_hash[2] // where '|' denotes concatenation.
If you need to compare it to the output of openssl use the following command line echo -n "test" | openssl enc -aes-256-cbc -a -p -k password
. The '-p' flag will give you back the salt, key, and iv generated by openssl.
In order to remain compatible with OpenSSL, Base64 lines should break every 64 characters. If the Base64 encoded string is less than 64 characters, a break should be included at the end automatically. If it more than 1 line, breaks only need to be included every 64 characters, and not at the end of the last line.