Due to GDPR I'm trying to find a good method to encrypt/decrypt data. But as my application has both Delphi and PHP endpoints I need functions that have strong encryption and work on both.
Searching on SO I've found out the following one, but I don't know how to do the same functions on Delphi:
function encryptString($plaintext, $password, $encoding = null) {
$iv = openssl_random_pseudo_bytes(16);
$ciphertext = openssl_encrypt($plaintext, "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext.$iv, hash('sha256', $password, true), true);
return $encoding == "hex" ? bin2hex($iv.$hmac.$ciphertext) : ($encoding == "base64" ? base64_encode($iv.$hmac.$ciphertext) : $iv.$hmac.$ciphertext);
}
function decryptString($ciphertext, $password, $encoding = null) {
$ciphertext = $encoding == "hex" ? hex2bin($ciphertext) : ($encoding == "base64" ? base64_decode($ciphertext) : $ciphertext);
if (!hash_equals(hash_hmac('sha256', substr($ciphertext, 48).substr($ciphertext, 0, 16), hash('sha256', $password, true), true), substr($ciphertext, 16, 32))) return null;
return openssl_decrypt(substr($ciphertext, 48), "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, substr($ciphertext, 0, 16));
}
CodePudding user response:
The code you found is not optimal. Once identifying what is repeating and enriching it with the principle of using constants over literals the following PHP code emerges:
define( 'ALGO_SECRET', 'sha256' ); // SHA-256 = 32 bytes; https://en.wikipedia.org/wiki/SHA-2
define( 'ALGO_MSGAUTH', 'sha256' );
define( 'ALGO_SSL', 'aes-256-cbc' );
define( 'SALT', 'Bedrock Hordes Staples' ); // https://en.wikipedia.org/wiki/Salt_(cryptography)
function encrypt( $plainbytes, $secretkey ) {
// Preparations
$hash= hash( ALGO_SECRET, $secretkey. SALT, true ); // true = return bytes
$iv= openssl_random_pseudo_bytes( 16 ); // 16 bytes; https://en.wikipedia.org/wiki/Initialization_vector
// Actual encryption of the payload
$cipherbytes= openssl_encrypt( $plainbytes, ALGO_SSL, $hash, OPENSSL_RAW_DATA, $iv );
// Adding a modification detection to the encrypted data in front of it; https://en.wikipedia.org/wiki/HMAC
$hmac= hash_hmac( ALGO_MSGAUTH, $cipherbytes. $iv, $hash, true ); // true = return bytes
return $iv. $hmac. $cipherbytes; // 16 32 ? = 48 bytes minimum
}
function decrypt( $allbytes, $secretkey ) {
// Preparations
$hash= hash( ALGO_SECRET, $secretkey. SALT, true ); // Same as in encryption
$iv= substr( $allbytes, 0, 16 ); // Cut into 3 pieces again
$hmac= substr( $allbytes, 16, 32 );
$cipherbytes= substr( $allbytes, 48 );
// Both extracted and recalculated modification detection values must match
$hmac_recalc= hash_hmac( ALGO_MSGAUTH, $cipherbytes. $iv, $hash, true ); // Same as in encryption
if( !hash_equals( $hmac_recalc, $hmac ) ) return null; // Not equal? Payload must be considered tampered/modified/corrupted.
// Actual decryption of the payload
return openssl_decrypt( $cipherbytes, ALGO_SSL, $hash, OPENSSL_RAW_DATA, $iv );
}
I consider this to ease telling apart all the single items and how much of the encryption and decryption process is the same. It shouldn't be that difficult to do the same steps in Delphi or another language - it would also work without OpenSSL on both ends.
For ready-to-use examples but also explanations on why to never handle texts but instead only bytes have a read on: