by Enrico Zimuel / @ezimuel
Senior Software Engineer
Zend a Rogue Wave Company
Developer since 1996. Senior Software Engineer at Zend a Rogue Wave Company. I did research in computer science at the Informatics Institute of Amsterdam University. Open source contributor of Apigility and Zend Framework. Author of articles and books about web programming and applied cryptography. Co-founder of PUG Torino (Italy).
P'2 = I2 ⊕ C'1 ⇒ I2 = P'2 ⊕ C'1
We know C'1[i] = x ⇒ we know I2[i]
I2 is the same as in the real ciphertext ⇒ P2[i] = C1[i] ⊕ I2[i]
function AES_encrypt($text, $key) {
$ivsize = openssl_cipher_iv_length('aes-256-cbc');
$iv = openssl_random_pseudo_bytes($ivsize);
// Encryption key generated by PBKDF2 (since PHP 5.5)
$keys = hash_pbkdf2('sha256', $key, $iv, 10000, 64, true);
$encKey = substr($keys, 0, 32); // 256 bit encryption key
$hmacKey = substr($keys, 32); // 256 bit hmac key
$ciphertext = openssl_encrypt(
$text,
'aes-256-cbc',
$encKey,
OPENSSL_RAW_DATA,
$iv
);
$hmac = hash_hmac('sha256', $iv . $ciphertext, $hmacKey);
return $hmac . $iv . $ciphertext;
}
function AES_decrypt($text, $key) {
$hmac = substr($text, 0, 64);
$ivsize = openssl_cipher_iv_length('aes-256-cbc');
$iv = substr($text, 64, $ivsize);
$ciphertext = substr($text, $ivsize + 64);
// Generate the encryption and hmac keys
$keys = hash_pbkdf2('sha256', $key, $iv, 10000, 64, true);
$encKey = substr($keys, 0, 32); // 256 bit encryption key
$hmacNew = hash_hmac('sha256', $iv . $ciphertext, substr($keys, 32));
if (!compareStrings($hmac, $hmacNew)) { // to prevent timing attacks
return false;
}
return openssl_decrypt(
$ciphertext,
'aes-256-cbc',
$encKey,
OPENSSL_RAW_DATA,
$iv
);
}
Note: hash_equals() requires PHP5.6+
A timing attack is a side channel attack in which the attacker attempts to compromise a cryptosystem by analyzing the time taken to execute cryptographic algorithms
From Wikipedia
Use hash_equals() to prevent timing attacks (PHP 5.6+)
Public key algorithms are too slow to encrypt a full text message
We need a different approach (Hybrid cryptosystem):
// Generate public and private keys
$keys = openssl_pkey_new(array(
"private_key_bits" => 4096,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
));
// Store the private key in a file
$passphrase = 'test';
openssl_pkey_export_to_file($keys, 'private.key', $passphrase);
// Store the public key in a file
$details = openssl_pkey_get_details($keys);
$publicKey = $details['key'];
file_put_contents('public.key', $publicKey);
$message = 'This is the secret message';
$passphrase = 'test'; // to read the private key
// Encryption
$key = openssl_random_pseudo_bytes(32);
openssl_public_encrypt($key, $encryptedKey, $publicKey);
$ciphertext = AES_encrypt($message, $key);
file_put_contents('encrypted.msg', $encryptedKey . $ciphertext);
// Decryption
$ciphertext = file_get_contents('encrypted.msg');
$encKey = substr($ciphertext, 0, 512);
$ciphertext = substr($ciphertext, 512);
$privateKey = openssl_pkey_get_private('file:///path/to/private.key',
$passphrase);
openssl_private_decrypt($encKey, $key, $privateKey);
$result = AES_decrypt($ciphertext, $key); // equal to $message
// Compute the signature
$passphrase = 'test';
$privateKey = openssl_pkey_get_private('file:///path/to/private.key',
$passphrase);
$data = file_get_contents('/path/to/file_to_sign');
openssl_sign($data, $signature, $privateKey, "sha256");
printf("Signature : %s\n", base64_encode($signature));
// Verify the signature
$publicKey = openssl_pkey_get_public('file:///path/to/public.key');
$result = openssl_verify($data, $signature, $publicKey, "sha256");
echo $result === 1 ? 'Signature verified' : 'Signature not valid';
zend-crypt is the cryptographic component of ZF2
use Zend\Crypt\BlockCipher;
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey('encryption key');
$ciphertext = $blockCipher->encrypt('this is a secret message');
use Zend\Crypt\PublicKey\RsaOptions;
use Zend\Crypt\PublicKey\Rsa;
// Generate public and private key
$rsaOptions = new RsaOptions(array( 'pass_phrase' => 'test' ));
$rsaOptions->generateKeys(array( 'private_key_bits' => 2048 ));
file_put_contents('private_key.pem', $rsaOptions->getPrivateKey());
file_put_contents('public_key.pub', $rsaOptions->getPublicKey());
// Sign and verify
$rsa = Rsa::factory(array(
'private_key' => 'path/to/private_key',
'pass_phrase' => 'passphrase of the private key'
));
$file = file_get_contents('path/file/to/sign');
$sign = $rsa->sign($file, $rsa->getOptions()->getPrivateKey());
$verify = $rsa->verify($file, $sign, $rsa->getOptions()->getPublicKey());
Rate this talk at joind.in/talk/8ea10
Contact me: enrico [at] zend.com
Follow me: @ezimuel
This work is licensed under a
Creative Commons Attribution-ShareAlike 3.0 Unported License.
I used reveal.js to make this presentation.