public
Last active

AES-128-CBC-PKCS#5Padding and HMAC-SHA256 in PHP, Ruby Perl, Java

  • Download Gist
Cipher.java
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
public class Cipher {
 
public static void main(String[] args) throws Throwable
{
BASE64Decoder decoder = new BASE64Decoder();
BASE64Encoder encoder = new BASE64Encoder();
String message = "s_hostname:jonasnuts.blogs.sapo.pt OR s_hostname:jonasnuts.com;1350305502";
 
// data
 
// should be 16 byte len
final byte[] aes_key = "d2cb415e067c7b13".getBytes();
final byte[] iv = "e36dc751d0433f05".getBytes(); // should be random
final byte[] hmac_key = "d6cfaad283353507".getBytes();
 
// encrypt
Cipher encrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
encrypt.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(aes_key, "AES"), new IvParameterSpec(iv));
 
byte[] encMessage = encrypt.doFinal(message.getBytes("UTF-8"));
String encrypted_message_b64 = encoder.encode(encMessage);
System.out.printf("%s (%d chars)\n", encrypted_message_b64, encrypted_message_b64.length());
 
byte[] encryptedData = decoder.decodeBuffer(encrypted_message_b64);
Cipher decrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
decrypt.init(Cipher.DECRYPT_MODE, new SecretKeySpec(aes_key, "AES"), new IvParameterSpec(iv));
byte[] data = decrypt.doFinal(encryptedData);
String decryptedData = new String(data, "UTF-8");
 
// process data
System.out.println(decryptedData);
String[] dargs = decryptedData.split(";");
 
String filter = dargs[0];
Date ts = new Date(Long.parseLong(dargs[1]) * 1000);// ts comes in seconds?
 
System.out.println("> " + filter);
System.out.println("> " + ts);
 
// hmac
Mac hmac = Mac.getInstance("HmacSHA256");
hmac.init(new SecretKeySpec(hmac_key, "HmacSHA256"));
byte[] signature = hmac.doFinal(encryptedData);
 
// ruby hexdigest is 59...
System.out.println(Hex.encodeHexString(signature));
 
}
 
 
}
cipher.perl
Perl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 
use Crypt::CBC;
use MIME::Base64::Perl;
use Digest::HMAC;
use Digest::SHA qw(hmac_sha256_hex);
 
 
 
my $cipher = Crypt::CBC->new(
-key => 'd2cb415e067c7b13',
-iv => 'e36dc751d0433f05', #random 16chars!!!!!! shold NOT repeat between requests
-cipher => 'OpenSSL::AES', #this is same as Rijndael
-literal_key => 1,
-header => "none",
-keysize => 16
);
 
 
$encrypted = $cipher->encrypt( "s_hostname:jonasnuts.blogs.sapo.pt OR s_hostname:jonasnuts.com;1350305502");
$base64 = encode_base64($encrypted);
$digest = hmac_sha256_hex($encrypted, "d6cfaad283353507");
 
print("Ciphertext(b64): $base64\n");
print("Digest(hex) : $digest\n" );
cipher.php
PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
<?php
 
 
//PHP doesn't support PKCS5Padding
//http://us3.php.net/manual/en/ref.mcrypt.php#69782
function pkcs5_pad ($text, $blocksize)
{
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
 
//requires 'mcrypt'
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv_size = mcrypt_enc_get_iv_size($cipher);
 
// AES Key : 'd2cb415e067c7b13'
// AES IV : 'e36dc751d0433f05'
// HMAC KEY : 'd6cfaad283353507'
// Ciphertext(b64): 'Ru7RLA4o+iQZNJXBx0iXtgWSQuV8/uqj6R6M59egKfHhaBFuMTl9Mpsb4yx6\nkgokQAf1HUcLg32zGCPo8bH4Df5RUPXWSUfHNb3cR7Mf5I8=\n'
// HMACb16 : '0f291f903f6abc951084bedb2210f48b30c4eb3e1bcce99b0967c5fda99d72a6'
 
// How do you do 256-bit AES encryption in PHP vs. 128-bit AES encryption???
// The answer is: Give it a key that's 32 bytes long as opposed to 16 bytes long.
// We're using 16 bytes:
$key128 = "d2cb415e067c7b13";
$iv = "e36dc751d0433f05";
$hmac_key = "d6cfaad283353507";
 
// This is the plain-text to be encrypted:
$cleartext = "s_hostname:jonasnuts.blogs.sapo.pt OR s_hostname:jonasnuts.com;1350305502";
$cleartext = pkcs5_pad($cleartext, mcrypt_get_block_size('des','cbc'));
 
printf("plainText: %s\n\n",$cleartext);
// The mcrypt_generic_init function initializes the cipher by specifying both
// the key and the IV. The length of the key determines whether we're doing
// 128-bit, 192-bit, or 256-bit encryption.
// Let's do 256-bit encryption here:
// Now let's do 128-bit encryption:
if (mcrypt_generic_init($cipher, $key128, $iv) != -1) {
// PHP pads with NULL bytes if $cleartext is not a multiple of the block size..
printf("len: %d", strlen($cleartext));
$cipherText = mcrypt_generic($cipher, $cleartext );
mcrypt_generic_deinit($cipher);
// Display the result in hex.
$b64ciphertext = base64_encode($cipherText);
 
printf("Ciphertext(b64): %s (%d chars)\n", $b64ciphertext, strlen($b64ciphertext));
printf("hmac (hex) : %s\n\n", hash_hmac('sha256', $cipherText, $hmac_key));
}
?>
cipher.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
require 'openssl'
require 'digest/sha2'
require 'base64'
 
def digest(str)
Digest::MD5.hexdigest(str)[0...16]
end
 
#set up message to be encrypted
message = "s_hostname:jonasnuts.blogs.sapo.pt OR s_hostname:jonasnuts.com;1350305502"
cipher = OpenSSL::Cipher.new('aes-128-cbc')
 
# digest the key, iv and hmac_key so we have 16-byte length
# also, it looks more of a funky password
aes_key = digest 'my passphrase'
aes_iv = digest 'my random iv'
hmac_key= digest 'my hmac key'
 
# prepare cipher
cipher.encrypt
cipher.key = aes_key
cipher.iv = aes_iv
 
encrypted = cipher.update(message) << cipher.final()
#encrypted << cipher.final()
b64_encoded = Base64.strict_encode64(encrypted).encode('utf-8') #strict_encode64 guarantees no newlines, encode64 is default
hashb16 = OpenSSL::HMAC.hexdigest('sha256', hmac_key, encrypted)
 
puts "AES Key : #{aes_key}"
puts "AES IV : #{aes_iv}"
puts "HMAC KEY : #{hmac_key}"
puts "Ciphertext(b64): #{b64_encoded} (#{b64_encoded.length} chars)"
puts "HMACb16 : #{hashb16}"

in the call to Crypt::CBC->new in cipher.pl:

my $cipher = Crypt::CBC->new(
-key => 'd2cb415e067c7b13',
-iv => 'e36dc751d0433f05', #random 16chars!!!!!! shold NOT repeat between requests
-cipher => 'OpenSSL::AES', #this is same as Rijndael
-literal_key => 1,

-header => "none",
-keysize => 16
);

the values for key and iv are not going to work in the way many will read them. They should be truly random strings, and not a hex string. The hex string will not be interpreted as hex. By using hex you are reducing the effective key size by one half (4 bits of randomness per char instead of 8 bits).

There is no such thing as pkcs5_pad() in PHP. That needs to be a custom implemented function.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.