問題描述
我通過 Apache 和 Node.js 服務器之間的不安全連接發送數據.我需要在 PHP 中加密數據并在 Node.js 中解密.我花了 2 天的時間試圖讓它工作,但是我只設法讓消息簽名工作,沒有加密.我嘗試通過 AES128-CBC、AES256-CBC、DES、AES128、AES256 作為算法,但是沒有任何效果..
我在 PHP 中嘗試過:
$data = json_encode(Array('mk' => $_SESSION['key'], 'algorithm' => 'SHA1', 'username' => $_SESSION['userid'], 'expires' => $expires));$payload = openssl_encrypt($data, 'des', '716c26ef');返回 base64_encode($payload);
在 Node.js 中:
var enc_json = new Buffer(response[1], 'base64');var decipher = crypto.createDecipher('des', '716c26ef');var json = decipher.update(enc_json).toString('ascii');json += decipher.final('ascii');
除了錯誤的解密數據外,我還收到如下錯誤:
TypeError:錯誤:0606508A:數字信封例程:EVP_DecryptFinal_ex:數據不是塊長度的倍數類型錯誤:錯誤:0606506D:數字信封例程:EVP_DecryptFinal_ex:錯誤的最終塊長度
我需要一個簡單的加密,因為數據不太敏感(沒有密碼或用戶數據),但是數據只能由接收者讀取.密鑰長度可以任意,但加密/解密的過程必須盡可能簡單,請不要使用IV.
我這周遇到了同樣的問題,但以相反的方式(PHP 加密 -> NodeJS 解密)并設法使此代碼段正常工作:>
aes256cbc.js
var crypto = require('crypto');var encrypt = 函數(純文本、加密方法、秘密、iv){var encryptor = crypto.createCipheriv(encryptionMethod, secret, iv);返回 encryptor.update(plain_text, 'utf8', 'base64') + encryptor.final('base64');};var 解密 = 函數(encryptedMessage、encryptionMethod、secret、iv){var 解密器 = crypto.createDecipheriv(encryptionMethod, secret, iv);返回解密器.更新(加密消息,'base64','utf8')+decryptor.final('utf8');};var textToEncrypt = new Date().toISOString().substr(0,19) + '|我的超級機密信息.';var encryptionMethod = 'AES-256-CBC';var secret = "My32charPasswordAndInitVectorStr";//必須是32個字符長度var iv = secret.substr(0,16);var encryptedMessage = encrypt(textToEncrypt, encryptionMethod, secret, iv);變量解密消息 = 解密(加密消息,加密方法,秘密,iv);控制臺日志(加密消息);控制臺日志(解密消息);
aes256cbc.php
這里避免陷入密鑰/iv 大小/解密問題的秘訣是擁有恰好 32 個字符長度和 16 個字符長度的秘訣用于 IV.此外,在 NodeJS 中使用base64"和utf8"非常很重要,因為這些是 PHP 中的默認值.
以下是一些示例運行:
$ node aes256cbc.js &&php aes256cbc.phpzra3FX4iyCc7qPc1dZs+G3ZQ40f5bSw8P9n5OtWl1t86nV5Qfh4zNRPFbsciyyHyU3Qi4Ga1oTiTwzrPIZQXLw==2015-01-27T18:29:12|我的超級機密信息.zra3FX4iyCc7qPc1dZs+G3ZQ40f5bSw8P9n5OtWl1t86nV5Qfh4zNRPFbsciyyHyU3Qi4Ga1oTiTwzrPIZQXLw==2015-01-27T18:29:12|我的超級機密信息.$ 節點 aes256cbc.js &&php aes256cbc.phpzra3FX4iyCc7qPc1dZs+G6B6+8aavHNc/Ymv9L6Omod8Di3tMbvOa2B7O2Yiyoutm9fy9l0G+P5VJT9z2qNESA==2015-01-27T18:29:15|我的超級機密信息.zra3FX4iyCc7qPc1dZs+G6B6+8aavHNc/Ymv9L6Omod8Di3tMbvOa2B7O2Yiyoutm9fy9l0G+P5VJT9z2qNESA==2015-01-27T18:29:15|我的超級機密信息.$ 節點 aes256cbc.js &&php aes256cbc.phpzra3FX4iyCc7qPc1dZs+G4oD1Fr5yLByON6QDE56UOqP6kkfGJzpyH6TbwZYX2oGlh2JGv+aHYUMh0qQnAj/uw==2015-01-27T18:29:29|我的超級機密信息.zra3FX4iyCc7qPc1dZs+G4oD1Fr5yLByON6QDE56UOqP6kkfGJzpyH6TbwZYX2oGlh2JGv+aHYUMh0qQnAj/uw==2015-01-27T18:29:29|我的超級機密信息.$ 節點 aes256cbc.js &&php aes256cbc.phpzra3FX4iyCc7qPc1dZs+G5OVCbCaUy8a0LLF+Bn8UT4X3nYbtynO0Zt2mvXnnli9dRxrxMw43uWnkh8MIwVHXA==2015-01-27T18:29:31|我的超級機密信息.zra3FX4iyCc7qPc1dZs+G5OVCbCaUy8a0LLF+Bn8UT4X3nYbtynO0Zt2mvXnnli9dRxrxMw43uWnkh8MIwVHXA==2015-01-27T18:29:31|我的超級機密信息.$ 節點 aes256cbc.js &&php aes256cbc.phpfdsqSyHBJjlwD0jYfOUZM2FrONG6Fk5d7FOItYEdbnaZIhhmg/apa8/jPwKFkDXD9eNqWC3w0JzY5wjtZADiBA==2015-01-27T18:30:08|我的超級機密信息.fdsqSyHBJjlwD0jYfOUZM2FrONG6Fk5d7FOItYEdbnaZIhhmg/apa8/jPwKFkDXD9eNqWC3w0JzY5wjtZADiBA==2015-01-27T18:30:08|我的超級機密信息.$ 節點 aes256cbc.js &&php aes256cbc.phpfdsqSyHBJjlwD0jYfOUZM4SRfi6jG5EoDFEF6d9xCIyluXSiMaKlhd89ovpeOz/YyEIlPbYR4ly00gf6hWfKHw==2015-01-27T18:30:45|我的超級機密信息.fdsqSyHBJjlwD0jYfOUZM4SRfi6jG5EoDFEF6d9xCIyluXSiMaKlhd89ovpeOz/YyEIlPbYR4ly00gf6hWfKHw==2015-01-27T18:30:45|我的超級機密信息.
注意:
我使用時間戳|消息"格式來避免中間人攻擊.例如,如果加密的消息包含要驗證的 ID,則 MitM 可以捕獲該消息并在他每次想要重新驗證時重新發送.
因此,我可以檢查加密消息上的時間戳是否在一個很小的時間間隔內.這樣一來,由于時間戳的原因,同一條消息每秒都會以不同的方式加密,并且無法在此固定時間間隔之外使用.
這里我濫用了初始化向量 (IV).正如@ArtjomB. 所解釋的,IV 應該是加密消息的第一部分,并且它應該是一個隨機值.還建議在 HTTP 標頭 (x-hmac: *value*
) 中使用 hmac
值以驗證消息是否來自有效來源(但這并沒有解決之前描述的重新發送"消息問題).
這是改進的版本,包括用于 php 和 node 的 hmac
以及作為加密消息一部分的 IV:
aes256cbc.js (v2)
var crypto = require('crypto');var encrypt = 函數(消息、方法、秘密、hmac){//var iv = crypto.randomBytes(16).toString('hex').substr(0,16);//在生產中使用它var iv = secret.substr(0,16);//將此用于測試目的(在 PHP 和 Node 加密器中具有相同的加密 IV)var encryptor = crypto.createCipheriv(method, secret, iv);var encrypted = new Buffer(iv).toString('base64') + encryptor.update(message, 'utf8', 'base64') + encryptor.final('base64');hmac.value = crypto.createHmac('md5', secret).update(encrypted).digest('hex');返回加密;};var 解密 = 函數(加密、方法、秘密、hmac){if (crypto.createHmac('md5', secret).update(encrypted).digest('hex') == hmac.value) {var iv = new Buffer(encrypted.substr(0, 24), 'base64').toString();var 解密器 = crypto.createDecipheriv(method, secret, iv);返回decryptor.update(encrypted.substr(24), 'base64', 'utf8') +decryptor.final('utf8');}};var encryptWithTSValidation = 函數(消息、方法、秘密、hmac){var messageTS = new Date().toISOString().substr(0,19) + message;返回加密(messageTS,方法,秘密,hmac);}vardecryptWithTSValidation = 函數(加密,方法,秘密,hmac,intervalThreshold){var 解密 = 解密(加密,方法,秘密,hmac);var now = new Date();var year = parseInt(decrypted.substr(0,4)), month = parseInt(decrypted.substr(5,2)) - 1,天 = parseInt(decrypted.substr(8,2)), 小時 = parseInt(decrypted.substr(11,2)),分鐘 = parseInt(decrypted.substr(14,2)), second = parseInt(decrypted.substr(17,2));var msgDate = new Date(Date.UTC(年、月、日、時、分、秒))if (Math.round((now - msgDate)/1000) <= intervalThreshold) {返回解密.substr(19);}}var message = '我的超級機密信息.';var 方法 = 'AES-256-CBC';var secret = "My32charPasswordAndInitVectorStr";//必須是32個字符長度var hmac = {};//var encrypted = encrypt(message, method, secret, hmac);//var 解密 = 解密(加密,方法,秘密,hmac);var encrypted = encryptWithTSValidation(message, method, secret, hmac);var解密=decryptWithTSValidation(加密,方法,秘密,hmac,60*60*12);//60*60m*12=12hconsole.log("使用 HTTP 標頭 'x-hmac:" + hmac.value + "' 來驗證中間人攻擊.");console.log("加密:" + 加密);console.log("解密:" + 解密);
請注意,crypto.createHmac(...).digest('hex')
是用 hex
消化的.這是 hmac
在 PHP 中的默認設置.
aes256cbc.php (v2)
getTimestamp() - $msgDate->getTimestamp()) <= $intervalThreshold) {返回 substr($decrypted,19);}}$message = "我的超級機密信息.";$method = "AES-256-CBC";$secret = "My32charPasswordAndInitVectorStr";//必須是32個字符長度//$encrypted = encrypt($message, $method, $secret, $hmac);//$decrypted =decrypt($encrypted, $method, $secret, $hmac);$encrypted = encryptWithTSValidation($message, $method, $secret, $hmac);$decrypted =decryptWithTSValidation($encrypted, $method, $secret, $hmac, 60*60*12);//60*60m*12=12hecho "使用 HTTP 標頭 'x-hmac: $hmac' 來驗證對抗中間人攻擊.
";echo "加密: $encrypted
";echo "解密: $decrypted
";?>
以下是一些示例運行:
$ node aes256cbc.js &&php aes256cbc.php使用 HTTP 標頭 'x-hmac: 6862972ef0f463bf48523fc9e334bb42' 來驗證對抗中間件攻擊.加密:YjE0ZzNyMHNwVm50MGswbQ==I6cAKeoxeSP5TGgtK59PotB/iG2BUSU8Y6NhAhVabN9UB+ZCTn7q2in4JyLwQiGN解密:我的超級秘密信息.使用 HTTP 標頭 'x-hmac: 6862972ef0f463bf48523fc9e334bb42' 來驗證對抗中間件攻擊.加密:YjE0ZzNyMHNwVm50MGswbQ==I6cAKeoxeSP5TGgtK59PotB/iG2BUSU8Y6NhAhVabN9UB+ZCTn7q2in4JyLwQiGN解密:我的超級秘密信息.$ 節點 aes256cbc.js &&php aes256cbc.php使用 HTTP 標頭 'x-hmac: b2e63f216acde938a82142220652cf59' 來驗證中間人攻擊.加密:YjE0ZzNyMHNwVm50MGswbQ==YsFRdKzCluCk7Yg+U+S1CSgYBBR8dkZytORm8xwEDmD9WB1mpqC3XnSrB+wR3/KW解密:我的超級秘密信息.使用 HTTP 標頭 'x-hmac: b2e63f216acde938a82142220652cf59' 來驗證中間人攻擊.加密:YjE0ZzNyMHNwVm50MGswbQ==YsFRdKzCluCk7Yg+U+S1CSgYBBR8dkZytORm8xwEDmD9WB1mpqC3XnSrB+wR3/KW解密:我的超級秘密信息.$ 節點 aes256cbc.js &&php aes256cbc.php使用 HTTP 標頭 'x-hmac: 73181744453d55eb6f81896ffd284cd8' 來驗證對抗中間件攻擊.加密:YjE0ZzNyMHNwVm50MGswbQ==YsFRdKzCLuCk7Yg+U+S1CTGik4Lv9PnWuEg5SiADJcdKX1to0LrNKmuCiYIweBAZ解密:我的超級秘密信息.使用 HTTP 標頭 'x-hmac: 73181744453d55eb6f81896ffd284cd8' 來驗證對抗中間件攻擊.加密:YjE0ZzNyMHNwVm50MGswbQ==YsFRdKzCLuCk7Yg+U+S1CTGik4Lv9PnWuEg5SiADJcdKX1to0LrNKmuCiYIweBAZ解密:我的超級秘密信息.$ 節點 aes256cbc.js &&php aes256cbc.php使用 HTTP 標頭 'x-hmac: 5372ecca442d65f582866cf3b24cb2b6' 來驗證對抗中間件攻擊.加密:YjE0ZzNyMHNwVm50MGswbQ==YsFRdKzCLuCk7Yg+U+S1CYEITF6aozBNp7bA54qY0Ugg9v6ktwoH6nqRyatkFqy8解密:我的超級秘密信息.使用 HTTP 標頭 'x-hmac: 5372ecca442d65f582866cf3b24cb2b6' 來驗證對抗中間件攻擊.加密:YjE0ZzNyMHNwVm50MGswbQ==YsFRdKzCLuCk7Yg+U+S1CYEITF6aozBNp7bA54qY0Ugg9v6ktwoH6nqRyatkFqy8解密:我的超級秘密信息.
最后但并非最不重要的是,如果您沒有在 php 中安裝 openssl mod,您可以使用 mcrypt
代替 rijndael128
和 pkcs7
像這樣填充(source):
aes256cbc-mcrypt.php (v2)
getTimestamp() - $msgDate->getTimestamp()) <= $intervalThreshold) {返回 substr($decrypted,19);}}$message = "我的超級機密信息.";$method = MCRYPT_RIJNDAEL_128;$secret = "My32charPasswordAndInitVectorStr";//必須是32個字符長度//$encrypted = encrypt($message, $method, $secret, $hmac);//$decrypted =decrypt($encrypted, $method, $secret, $hmac);$encrypted = encryptWithTSValidation($message, $method, $secret, $hmac);$decrypted =decryptWithTSValidation($encrypted, $method, $secret, $hmac, 60*60*12);//60*60m*12=12hecho "使用 HTTP 標頭 'x-hmac: $hmac' 來驗證對抗中間人攻擊.
";echo "加密: $encrypted
";echo "解密: $decrypted
";?>
當然,接下來進行一些測試:
$ php aes256cbc-mcrypt.php &&節點 aes256cbc.js使用 HTTP 標頭 'x-hmac: 801282a9ed6b2d5bd2254140d7a17582' 來驗證中間人攻擊.加密:YjE0ZzNyMHNwVm50MGswbQ==ipQ+Yah8xoF0C6yjCJr8v9IyatyGeNT2yebrpJZ5xH73H5fFcV1zhqhRGwM0ToGU解密:我的超級秘密信息.使用 HTTP 標頭 'x-hmac: 801282a9ed6b2d5bd2254140d7a17582' 來驗證中間人攻擊.加密:YjE0ZzNyMHNwVm50MGswbQ==ipQ+Yah8xoF0C6yjCJr8v9IyatyGeNT2yebrpJZ5xH73H5fFcV1zhqhRGwM0ToGU解密:我的超級秘密信息.$ php aes256cbc-mcrypt.php &&節點 aes256cbc.js使用 HTTP 標頭 'x-hmac: 0ab2bc83108e1e250f6ecd483cd65329' 來驗證中間人攻擊.加密:YjE0ZzNyMHNwVm50MGswbQ==ipQ+Yah8xoF0C6yjCJr8v79P+j4YUl8ln8eu??7FDqEdbxMe1Z7BvW8iVUN1qFCiHM解密:我的超級秘密信息.使用 HTTP 標頭 'x-hmac: 0ab2bc83108e1e250f6ecd483cd65329' 來驗證中間人攻擊.加密:YjE0ZzNyMHNwVm50MGswbQ==ipQ+Yah8xoF0C6yjCJr8v79P+j4YUl8ln8eu??7FDqEdbxMe1Z7BvW8iVUN1qFCiHM解密:我的超級秘密信息.
I am sending data through insecure connection between Apache and Node.js servers. I need to encrypt data in PHP and decrypt in Node.js. I've spent 2 days trying to get it to work, however I only managed to get message signing to work, no encryption. I tried passing AES128-CBC, AES256-CBC, DES, AES128, AES256 as algorithms, however nothing worked well..
I tried this in PHP:
$data = json_encode(Array('mk' => $_SESSION['key'], 'algorithm' => 'SHA1', 'username' => $_SESSION['userid'], 'expires' => $expires));
$payload = openssl_encrypt($data, 'des', '716c26ef');
return base64_encode($payload);
And in Node.js:
var enc_json = new Buffer(response[1], 'base64');
var decipher = crypto.createDecipher('des', '716c26ef');
var json = decipher.update(enc_json).toString('ascii');
json += decipher.final('ascii');
And besides wrong decrypted data I get error such as these:
TypeError: error:0606508A:digital envelope routines:EVP_DecryptFinal_ex:data not multiple of block length
TypeError: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
I need a simple encryption as data is not too sensitive (no password or user data), however data should only be read by the recipient. Key length can be anything, but procedure to encrypt/decrypt has to be as simple as possible, please no IVs.
I was struggling with the same problem this week but in the opposite way (PHP encrypts -> NodeJS decrypts) and had managed to get this snippet working:
aes256cbc.js
var crypto = require('crypto');
var encrypt = function (plain_text, encryptionMethod, secret, iv) {
var encryptor = crypto.createCipheriv(encryptionMethod, secret, iv);
return encryptor.update(plain_text, 'utf8', 'base64') + encryptor.final('base64');
};
var decrypt = function (encryptedMessage, encryptionMethod, secret, iv) {
var decryptor = crypto.createDecipheriv(encryptionMethod, secret, iv);
return decryptor.update(encryptedMessage, 'base64', 'utf8') + decryptor.final('utf8');
};
var textToEncrypt = new Date().toISOString().substr(0,19) + '|My super secret information.';
var encryptionMethod = 'AES-256-CBC';
var secret = "My32charPasswordAndInitVectorStr"; //must be 32 char length
var iv = secret.substr(0,16);
var encryptedMessage = encrypt(textToEncrypt, encryptionMethod, secret, iv);
var decryptedMessage = decrypt(encryptedMessage, encryptionMethod, secret, iv);
console.log(encryptedMessage);
console.log(decryptedMessage);
aes256cbc.php
<?php
date_default_timezone_set('UTC');
$textToEncrypt = substr(date('c'),0,19) . "|My super secret information.";
$encryptionMethod = "AES-256-CBC";
$secret = "My32charPasswordAndInitVectorStr"; //must be 32 char length
$iv = substr($secret, 0, 16);
$encryptedMessage = openssl_encrypt($textToEncrypt, $encryptionMethod, $secret,0,$iv);
$decryptedMessage = openssl_decrypt($encryptedMessage, $encryptionMethod, $secret,0,$iv);
echo "$encryptedMessage
";
echo "$decryptedMessage
";
?>
The secret here to avoid falling in key/iv size/decryption problems is to have the secret of exactly 32 characters length and 16 for the IV. Also, it is VERY important to use 'base64' and 'utf8' in NodeJS since these are the defaults in PHP.
Here are some sample runs:
$ node aes256cbc.js && php aes256cbc.php
zra3FX4iyCc7qPc1dZs+G3ZQ40f5bSw8P9n5OtWl1t86nV5Qfh4zNRPFbsciyyHyU3Qi4Ga1oTiTwzrPIZQXLw==
2015-01-27T18:29:12|My super secret information.
zra3FX4iyCc7qPc1dZs+G3ZQ40f5bSw8P9n5OtWl1t86nV5Qfh4zNRPFbsciyyHyU3Qi4Ga1oTiTwzrPIZQXLw==
2015-01-27T18:29:12|My super secret information.
$ node aes256cbc.js && php aes256cbc.php
zra3FX4iyCc7qPc1dZs+G6B6+8aavHNc/Ymv9L6Omod8Di3tMbvOa2B7O2Yiyoutm9fy9l0G+P5VJT9z2qNESA==
2015-01-27T18:29:15|My super secret information.
zra3FX4iyCc7qPc1dZs+G6B6+8aavHNc/Ymv9L6Omod8Di3tMbvOa2B7O2Yiyoutm9fy9l0G+P5VJT9z2qNESA==
2015-01-27T18:29:15|My super secret information.
$ node aes256cbc.js && php aes256cbc.php
zra3FX4iyCc7qPc1dZs+G4oD1Fr5yLByON6QDE56UOqP6kkfGJzpyH6TbwZYX2oGlh2JGv+aHYUMh0qQnAj/uw==
2015-01-27T18:29:29|My super secret information.
zra3FX4iyCc7qPc1dZs+G4oD1Fr5yLByON6QDE56UOqP6kkfGJzpyH6TbwZYX2oGlh2JGv+aHYUMh0qQnAj/uw==
2015-01-27T18:29:29|My super secret information.
$ node aes256cbc.js && php aes256cbc.php
zra3FX4iyCc7qPc1dZs+G5OVCbCaUy8a0LLF+Bn8UT4X3nYbtynO0Zt2mvXnnli9dRxrxMw43uWnkh8MIwVHXA==
2015-01-27T18:29:31|My super secret information.
zra3FX4iyCc7qPc1dZs+G5OVCbCaUy8a0LLF+Bn8UT4X3nYbtynO0Zt2mvXnnli9dRxrxMw43uWnkh8MIwVHXA==
2015-01-27T18:29:31|My super secret information.
$ node aes256cbc.js && php aes256cbc.php
fdsqSyHBJjlwD0jYfOUZM2FrONG6Fk5d7FOItYEdbnaZIhhmg/apa8/jPwKFkDXD9eNqWC3w0JzY5wjtZADiBA==
2015-01-27T18:30:08|My super secret information.
fdsqSyHBJjlwD0jYfOUZM2FrONG6Fk5d7FOItYEdbnaZIhhmg/apa8/jPwKFkDXD9eNqWC3w0JzY5wjtZADiBA==
2015-01-27T18:30:08|My super secret information.
$ node aes256cbc.js && php aes256cbc.php
fdsqSyHBJjlwD0jYfOUZM4SRfi6jG5EoDFEF6d9xCIyluXSiMaKlhd89ovpeOz/YyEIlPbYR4ly00gf6hWfKHw==
2015-01-27T18:30:45|My super secret information.
fdsqSyHBJjlwD0jYfOUZM4SRfi6jG5EoDFEF6d9xCIyluXSiMaKlhd89ovpeOz/YyEIlPbYR4ly00gf6hWfKHw==
2015-01-27T18:30:45|My super secret information.
NOTE:
I use a "timestamp|message" format to avoid man in the middle attacks. For example, if the encrypted message contains an ID to be authenticated, the MitM could capture the message and re-send it every time he wants to re-authenticate.
Therefore, I could check the timestamp on the encrypted message to be within a little time interval. This way, the same message is encrypted differently each second because of the timestamp, and could not be used out of this fixed time interval.
EDIT:
Here I was misusing the Initialization Vector (IV).
As @ArtjomB. explained, the IV should be the first part of the encrypted message, and also it should be a random value.
It's also recommended to use a hmac
value in a HTTP Header (x-hmac: *value*
) in order to validate that the message was originated from a valid source (but this does not address the "re-send" message issue previously described).
Here's the improved version, including the hmac
for php and node and the IV as a part of the encrypted message:
aes256cbc.js (v2)
var crypto = require('crypto');
var encrypt = function (message, method, secret, hmac) {
//var iv = crypto.randomBytes(16).toString('hex').substr(0,16); //use this in production
var iv = secret.substr(0,16); //using this for testing purposes (to have the same encryption IV in PHP and Node encryptors)
var encryptor = crypto.createCipheriv(method, secret, iv);
var encrypted = new Buffer(iv).toString('base64') + encryptor.update(message, 'utf8', 'base64') + encryptor.final('base64');
hmac.value = crypto.createHmac('md5', secret).update(encrypted).digest('hex');
return encrypted;
};
var decrypt = function (encrypted, method, secret, hmac) {
if (crypto.createHmac('md5', secret).update(encrypted).digest('hex') == hmac.value) {
var iv = new Buffer(encrypted.substr(0, 24), 'base64').toString();
var decryptor = crypto.createDecipheriv(method, secret, iv);
return decryptor.update(encrypted.substr(24), 'base64', 'utf8') + decryptor.final('utf8');
}
};
var encryptWithTSValidation = function (message, method, secret, hmac) {
var messageTS = new Date().toISOString().substr(0,19) + message;
return encrypt(messageTS, method, secret, hmac);
}
var decryptWithTSValidation = function (encrypted, method, secret, hmac, intervalThreshold) {
var decrypted = decrypt(encrypted, method, secret, hmac);
var now = new Date();
var year = parseInt(decrypted.substr(0,4)), month = parseInt(decrypted.substr(5,2)) - 1,
day = parseInt(decrypted.substr(8,2)), hour = parseInt(decrypted.substr(11,2)),
minute = parseInt(decrypted.substr(14,2)), second = parseInt(decrypted.substr(17,2));
var msgDate = new Date(Date.UTC(year, month, day, hour, minute, second))
if (Math.round((now - msgDate) / 1000) <= intervalThreshold) {
return decrypted.substr(19);
}
}
var message = 'My super secret information.';
var method = 'AES-256-CBC';
var secret = "My32charPasswordAndInitVectorStr"; //must be 32 char length
var hmac = {};
//var encrypted = encrypt(message, method, secret, hmac);
//var decrypted = decrypt(encrypted, method, secret, hmac);
var encrypted = encryptWithTSValidation(message, method, secret, hmac);
var decrypted = decryptWithTSValidation(encrypted, method, secret, hmac, 60*60*12); //60*60m*12=12h
console.log("Use HTTP header 'x-hmac: " + hmac.value + "' for validating against MitM-attacks.");
console.log("Encrypted: " + encrypted);
console.log("Decrypted: " + decrypted);
Note that crypto.createHmac(...).digest('hex')
is digested with hex
. This is the default in PHP for hmac
.
aes256cbc.php (v2)
<?php
function encrypt ($message, $method, $secret, &$hmac) {
//$iv = substr(bin2hex(openssl_random_pseudo_bytes(16)),0,16); //use this in production
$iv = substr($secret, 0, 16); //using this for testing purposes (to have the same encryption IV in PHP and Node encryptors)
$encrypted = base64_encode($iv) . openssl_encrypt($message, $method, $secret, 0, $iv);
$hmac = hash_hmac('md5', $encrypted, $secret);
return $encrypted;
}
function decrypt ($encrypted, $method, $secret, $hmac) {
if (hash_hmac('md5', $encrypted, $secret) == $hmac) {
$iv = base64_decode(substr($encrypted, 0, 24));
return openssl_decrypt(substr($encrypted, 24), $method, $secret, 0, $iv);
}
}
function encryptWithTSValidation ($message, $method, $secret, &$hmac) {
date_default_timezone_set('UTC');
$message = substr(date('c'),0,19) . "$message";
return encrypt($message, $method, $secret, $hmac);
}
function decryptWithTSValidation ($encrypted, $method, $secret, $hmac, $intervalThreshold) {
$decrypted = decrypt($encrypted, $method, $secret, $hmac);
$now = new DateTime();
$msgDate = new DateTime(str_replace("T"," ",substr($decrypted,0,19)));
if (($now->getTimestamp() - $msgDate->getTimestamp()) <= $intervalThreshold) {
return substr($decrypted,19);
}
}
$message = "My super secret information.";
$method = "AES-256-CBC";
$secret = "My32charPasswordAndInitVectorStr"; //must be 32 char length
//$encrypted = encrypt($message, $method, $secret, $hmac);
//$decrypted = decrypt($encrypted, $method, $secret, $hmac);
$encrypted = encryptWithTSValidation($message, $method, $secret, $hmac);
$decrypted = decryptWithTSValidation($encrypted, $method, $secret, $hmac, 60*60*12); //60*60m*12=12h
echo "Use HTTP header 'x-hmac: $hmac' for validating against MitM-attacks.
";
echo "Encrypted: $encrypted
";
echo "Decrypted: $decrypted
";
?>
Here are some sample runs:
$ node aes256cbc.js && php aes256cbc.php
Use HTTP header 'x-hmac: 6862972ef0f463bf48523fc9e334bb42' for validating against MitM-attacks.
Encrypted: YjE0ZzNyMHNwVm50MGswbQ==I6cAKeoxeSP5TGgtK59PotB/iG2BUSU8Y6NhAhVabN9UB+ZCTn7q2in4JyLwQiGN
Decrypted: My super secret information.
Use HTTP header 'x-hmac: 6862972ef0f463bf48523fc9e334bb42' for validating against MitM-attacks.
Encrypted: YjE0ZzNyMHNwVm50MGswbQ==I6cAKeoxeSP5TGgtK59PotB/iG2BUSU8Y6NhAhVabN9UB+ZCTn7q2in4JyLwQiGN
Decrypted: My super secret information.
$ node aes256cbc.js && php aes256cbc.php
Use HTTP header 'x-hmac: b2e63f216acde938a82142220652cf59' for validating against MitM-attacks.
Encrypted: YjE0ZzNyMHNwVm50MGswbQ==YsFRdKzCLuCk7Yg+U+S1CSgYBBR8dkZytORm8xwEDmD9WB1mpqC3XnSrB+wR3/KW
Decrypted: My super secret information.
Use HTTP header 'x-hmac: b2e63f216acde938a82142220652cf59' for validating against MitM-attacks.
Encrypted: YjE0ZzNyMHNwVm50MGswbQ==YsFRdKzCLuCk7Yg+U+S1CSgYBBR8dkZytORm8xwEDmD9WB1mpqC3XnSrB+wR3/KW
Decrypted: My super secret information.
$ node aes256cbc.js && php aes256cbc.php
Use HTTP header 'x-hmac: 73181744453d55eb6f81896ffd284cd8' for validating against MitM-attacks.
Encrypted: YjE0ZzNyMHNwVm50MGswbQ==YsFRdKzCLuCk7Yg+U+S1CTGik4Lv9PnWuEg5SiADJcdKX1to0LrNKmuCiYIweBAZ
Decrypted: My super secret information.
Use HTTP header 'x-hmac: 73181744453d55eb6f81896ffd284cd8' for validating against MitM-attacks.
Encrypted: YjE0ZzNyMHNwVm50MGswbQ==YsFRdKzCLuCk7Yg+U+S1CTGik4Lv9PnWuEg5SiADJcdKX1to0LrNKmuCiYIweBAZ
Decrypted: My super secret information.
$ node aes256cbc.js && php aes256cbc.php
Use HTTP header 'x-hmac: 5372ecca442d65f582866cf3b24cb2b6' for validating against MitM-attacks.
Encrypted: YjE0ZzNyMHNwVm50MGswbQ==YsFRdKzCLuCk7Yg+U+S1CYEITF6aozBNp7bA54qY0Ugg9v6ktwoH6nqRyatkFqy8
Decrypted: My super secret information.
Use HTTP header 'x-hmac: 5372ecca442d65f582866cf3b24cb2b6' for validating against MitM-attacks.
Encrypted: YjE0ZzNyMHNwVm50MGswbQ==YsFRdKzCLuCk7Yg+U+S1CYEITF6aozBNp7bA54qY0Ugg9v6ktwoH6nqRyatkFqy8
Decrypted: My super secret information.
Last but not least, if you don't have openssl mod installed in php, you can use mcrypt
instead with rijndael128
and pkcs7
padding (source) like this:
aes256cbc-mcrypt.php (v2)
<?php
function pkcs7pad($message) {
$padding = 16 - (strlen($message) % 16);
return $message . str_repeat(chr($padding), $padding);
}
function pkcs7unpad($message) {
$padding = ord(substr($message, -1)); //get last char and transform it to Int
return substr($message, 0, -$padding); //remove the last 'padding' string
}
function encrypt ($message, $method, $secret, &$hmac) {
//$iv = substr(bin2hex(mcrypt_create_iv(mcrypt_get_iv_size($method, MCRYPT_MODE_CBC), MCRYPT_DEV_URANDOM)),0,16); //use this in production
$iv = substr($secret, 0, 16); //using this for testing purposes (to have the same encryption IV in PHP and Node encryptors)
$message = pkcs7pad($message);
$encrypted = base64_encode($iv) . base64_encode(mcrypt_encrypt($method, $secret, $message, MCRYPT_MODE_CBC, $iv));
$hmac = hash_hmac('md5', $encrypted, $secret);
return $encrypted;
}
function decrypt ($encrypted, $method, $secret, $hmac) {
if (hash_hmac('md5', $encrypted, $secret) == $hmac) {
$iv = base64_decode(substr($encrypted, 0, 24));
return pkcs7unpad(mcrypt_decrypt($method, $secret , base64_decode(substr($encrypted, 24)) , MCRYPT_MODE_CBC, $iv));
}
}
function encryptWithTSValidation ($message, $method, $secret, &$hmac) {
date_default_timezone_set('UTC');
$message = substr(date('c'),0,19) . "$message";
return encrypt($message, $method, $secret, $hmac);
}
function decryptWithTSValidation ($encrypted, $method, $secret, $hmac, $intervalThreshold) {
$decrypted = decrypt($encrypted, $method, $secret, $hmac);
$now = new DateTime();
//echo "Decrypted: $decrypted
";
$msgDate = new DateTime(str_replace("T"," ",substr($decrypted,0,19)));
if (($now->getTimestamp() - $msgDate->getTimestamp()) <= $intervalThreshold) {
return substr($decrypted,19);
}
}
$message = "My super secret information.";
$method = MCRYPT_RIJNDAEL_128;
$secret = "My32charPasswordAndInitVectorStr"; //must be 32 char length
//$encrypted = encrypt($message, $method, $secret, $hmac);
//$decrypted = decrypt($encrypted, $method, $secret, $hmac);
$encrypted = encryptWithTSValidation($message, $method, $secret, $hmac);
$decrypted = decryptWithTSValidation($encrypted, $method, $secret, $hmac, 60*60*12); //60*60m*12=12h
echo "Use HTTP header 'x-hmac: $hmac' for validating against MitM-attacks.
";
echo "Encrypted: $encrypted
";
echo "Decrypted: $decrypted
";
?>
Ofcourse, some tests next:
$ php aes256cbc-mcrypt.php && node aes256cbc.js
Use HTTP header 'x-hmac: 801282a9ed6b2d5bd2254140d7a17582' for validating against MitM-attacks.
Encrypted: YjE0ZzNyMHNwVm50MGswbQ==ipQ+Yah8xoF0C6yjCJr8v9IyatyGeNT2yebrpJZ5xH73H5fFcV1zhqhRGwM0ToGU
Decrypted: My super secret information.
Use HTTP header 'x-hmac: 801282a9ed6b2d5bd2254140d7a17582' for validating against MitM-attacks.
Encrypted: YjE0ZzNyMHNwVm50MGswbQ==ipQ+Yah8xoF0C6yjCJr8v9IyatyGeNT2yebrpJZ5xH73H5fFcV1zhqhRGwM0ToGU
Decrypted: My super secret information.
$ php aes256cbc-mcrypt.php && node aes256cbc.js
Use HTTP header 'x-hmac: 0ab2bc83108e1e250f6ecd483cd65329' for validating against MitM-attacks.
Encrypted: YjE0ZzNyMHNwVm50MGswbQ==ipQ+Yah8xoF0C6yjCJr8v79P+j4YUl8ln8eu7FDqEdbxMe1Z7BvW8iVUN1qFCiHM
Decrypted: My super secret information.
Use HTTP header 'x-hmac: 0ab2bc83108e1e250f6ecd483cd65329' for validating against MitM-attacks.
Encrypted: YjE0ZzNyMHNwVm50MGswbQ==ipQ+Yah8xoF0C6yjCJr8v79P+j4YUl8ln8eu7FDqEdbxMe1Z7BvW8iVUN1qFCiHM
Decrypted: My super secret information.
這篇關于在 PHP 中加密字符串并在 Node.js 中解密的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!