A while back I wrote a post on how to Read OpenSSL files in PHP, and a reader asked how would you write a file.

Well I thought that’s easy you just do the same thing but use the encrypt function. Well I should have known better. Two hours later and a whole lot of research yield that PHP doesn’t do PKCS#5 padding when using PHP’s mcrypt API. But luckly duerra had a simple solution. So after padding my input everything worked fine.

So just a quick review. I use openssl in the command line to encrypt files with AES cipher 128 block size and 256 key length with salt. For example to encrypt myfile.txt I would use something like this:

openssl enc -e -aes-256-cbc -salt -in myfile.txt -out myfile.txt.crypt

If on the other hand you want to decrypt the file you would use something like this

openssl enc -d -aes-256-cbc -salt -in myfile.txt.crypt -out myfile.txt

I thought about using encrypted files on a web server since I currently share the space with other people. This way I can keep some files confidential. One way was to call openssl through a shell command but it turns out that it is not installed on the webserver. So I checked the PHP manual and it does have support for encryption/decryption (that is if you compiled with mcrypt module). So that’s what I was set on using.

The problem arose when I read the documentation and saw that there was no function to write a file to be read by openssl directly. Further more the encryption looked kinda complicated since all I had was a password for the file and the functions need a KEY and IV.

But I finally figured it out. So here it is:

First to get the KEY and IV you need a password and some salt. The password is of course kept secret but the salt is a random eight character string to be stored in the file created.

Again nothing special about the salt just come up with some random eight character string.

We take the password append it to the salt hash it a couple times with MD5 and we get the KEY and IV. Once we have the KEY and IV we can use the PHP functions to encrypt the file.

So here’s the code. First create some plain text to be encrypted an store it in a string:

$plain_text = "Super secret message";

Come up with a good password:

$password = "S3cr3tP@ssw0rd";

Then generate the salt, here I am using a constant but you really need to use something random to improve security.

$salt = "abcd1234";

Then here’s the critical part we need to pad our message to comply with PKCS#5 otherwise we won’t be able to decrypt the file with OpenSSL. So we need to get the block size and pad the plain text:

$block_size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$padded_plain_text = pkcs5_pad($plain_text, $block_size);

Here’s the function to do the PKCS#5 padding:

function pkcs5_pad($text, $block_size) {
    $pad = $block_size - (strlen($text) % $block_size);
    return $text . str_repeat(chr($pad), $pad);
}

Next we create the cipher resource used to encrypt the file, and we also get the size of the key and the size of the IV (we could hard-code constants but we want to be explicit). Note that AES cipher is the same as RIJNDAEL. You might also be asking why we used -aes-256-cbc but we are using MCRYPT_RIJNDAEL_128 here (well I did ask my self). Turns out 256 is the key size, while 128 is the block size so this combination works.

$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, "", MCRYPT_MODE_CBC, "");
$key_len = mcrypt_enc_get_key_size($td);
$iv_len  = mcrypt_enc_get_iv_size($td);

then we generate the key and IV with the password and salt.

list($key, $iv) = salted_key_and_iv($key_len, $iv_len, $password, $salt);

salted_key_and_iv is a simple function that does the password and salt hashing and it looks like this:

function salted_key_and_iv($key_len, $iv_len, $pass, $salt) {
    $desired_len = $key_len + $iv_len;
    $data = "";
    $dx   = "";
    while ( strlen($data) < $desired_len ) {
        $dx = md5($dx . $pass . $salt, true);
        $data .= $dx;
    }
    return array(substr($data,0,$key_len), substr($data,$key_len,$iv_len));
}

Lastly we init the cipher, encrypt the data, denit the cypher and close it. NOTE: that we are using the $padded_plain_text variable, otherwise it will not work!

mcrypt_generic_init($td, $key, $iv);
$encrypted_data = mcrypt_generic($td, $padded_plain_text);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);

Lastly we write the file, it is good to use the binary flag just in case your code gets ported to win32 machine.

$file = fopen('myfile.txt.crypt', 'wb');

Then we write the magic token, so openssl can recognize it:

fwrite($file, "Salted__");

Next we write the salt itself:

fwrite($file, $salt, strlen($salt));

Then we write the encrypted data:

fwrite($file, $encrypted_data, strlen($encrypted_data));

Finally we close the file:

fclose($file);

All done, hope this helps.