Well I have spent the last few days trying to get a string to encrypt in php and decrypt in ruby. I am not a crypto expert and the documentation in the various libraries is certainly lacking, especially for those of us that don't live and breathe crytpo, so I thought I would spare the rest of you some pain and give a working example
Before I post a working example, I tried just about every library for php and for ruby before I found one that works, Im sure others can get them all to work, but these work for me, given a very special set of constraints that I will show. I chose AES128 (Also called Rijndael) as the standard as it seems to be a balance of descent encryption and performance and is the standard supported by the US Government. AES allows several choices of Block Cipher modes, I think we are using ECB
Let me also say I do not guarantee that this is very secure, in fact I can say its not very secure at all, but is certainly better than plain text. If you know of a way to improve this, let me know and I will update the example. For one thing there is no IV in this setup, AES can use IV, but these 2 libraries don't seem to expose IV. I tried other approaches to get IV to work but gave up.
I am using base64 encoding to encode the bytes to pass between the programs (so if you want to use a cookie or other text based protocol, it also makes it easier for printing), the encoding takes the 16 bytes and makes them a 24 character string.
PHP seems to offer 3 crypto options for AES, the most popular being mcrypt (the one we will use in the example), but also PHPAes and AES128 are out there.
Ruby seems to offer OpenSSL, EzCrypto (a wrapper on OpenSSL) and Crypt (pure ruby)
Now for the working example, in PHP, we are going to use mcrypt:
<?php
$input = "joelis ";
$key = "abcdefgh01234567";
$cipher_alg = MCRYPT_RIJNDAEL_128;
print "Original string: $input <p>";
$encrypted_string = mcrypt_encrypt($cipher_alg, $key, $input, MCRYPT_MODE_CBC);
$b64Encoded = base64_encode($encrypted_string);
print "Encrypted string: ".$b64Encoded."<p>";
$decrypted_string = mcrypt_decrypt($cipher_alg, $key, $encrypted_string, MCRYPT_MODE_CBC);
print "Decrypted string: $decrypted_string";
print "Each byte=";
for ($i=0,$j = strlen($encrypted_string);$i < $j; $i++) {
print ord($encrypted_string[$i])." ";
}
print "<p>";
?>
Output:
Original string: joelis
Encrypted string: rPxA+dEROY6x3Qhb9Lx4BQ==
Decrypted string: joelis
Each byte=172 252 64 249 209 17 57 142 177 221 8 91 244 188 120 5
* Now for 2 key things
- replace the key with your key, but make sure it is exactly 16 valid ascii characters, no more and no less.
- replace the string with your string, make sure it is exactly 16 ascii characters long, if it is shorter, right pad the string with spaces. If you have a longer string, do this process once for each 16 char string. Then at the end, combine the b64 encoded strings together.
(these 2 things are critical, because of the padding differences in the algorithms and the way they work)
Ruby Decrypt:
require 'crypt/rijndael'
b64in = "rPxA+dEROY6x3Qhb9Lx4BQ=="
bbyte = Base64.decode64(b64in)
bbyte.each_byte {|c| logger.error c.to_s + " " }
decryptedBlock = rijndael.decrypt_block(bbyte)
If you encrypted multiple 16byte blocks, you will need to split bbyte up into 16 byte blocks before decrypting.
goodl luck!
"I chose AES128 (Also called Rijndael)"
That's (at the very least) misleading.
AES has a fixed block length of 128 bits. The key length, on the other hand, can be 128, 192, or 256. "AES-128" means "AES with a key length of 128 bits".
Meanwhile, neither the block length nor the key length of Rijndael is fixed. "AES" means "Rijndael with a block length of 128 bits".
So "AES-128" means "Rijndael with a block length of 128 bits and a key length of 128 bits".
So, yes, in some sense it's true that "AES-128" is "Rijndael". But it's not true that "AES-128" MEANS "Rijndael".
For example, "AES-256" is also "Rijndael". And it also doesn't MEAN "Rijndael". It means "Rijndael with a block length of 128 bits and a key length of 256 bits".
Posted by: blonk | April 18, 2008 at 12:30 PM
The Ruby Decrypt code needs the following two lines:
key = 'abcdefgh01234567'
rijndael = Crypt::Rijndael.new( key, 128, 128 )
Posted by: Barry Cornelius | May 28, 2008 at 04:15 AM
One other thing. You have:
If you encrypted multiple 16byte blocks, you will need to split bbyte up
into 16 byte blocks before decrypting.
I think this should be:
If you encrypted multiple 16byte blocks, you will need to split b64in up
into 24 character chunks and apply decode64 to each chunk and then
apply decrypt_block to the result of the decode64.
For example, for me, the result of combining the b64 encoded strings is in params[:id]. I then do:
b64in = params[:id][ 0..23]
bbyte = Base64.decode64(b64in)
decoded_parameter = rijndael.decrypt_block(bbyte)
b64in = params[:id][24..47]
bbyte = Base64.decode64(b64in)
decoded_parameter += rijndael.decrypt_block(bbyte)
The variable decoded_parameter finishes up with 32 characters.
By the way, thanks for blogging about this. Your code was very helpful: my PHP script encrypts some information and then redirects to a Rails application that successfully decrypts that information.
Posted by: Barry Cornelius | May 28, 2008 at 03:03 PM
I noticed you stated, "...at the end, combine the b64 encoded strings together.". What do you mean by that? How do I combine? Simply just append the strings together?
Posted by: AC | June 22, 2009 at 01:57 PM