web-dev-qa-db-ja.com

CryptoJSで暗号化し、PHP

クライアント側(モバイルデバイス)では、CryptoJSを使用してユーザーのパスワードを暗号化します。

var lib_crypt = require('aes');

$.loginButton.addEventListener('click', function(e){

var key = lib_crypt.CryptoJS.enc.Hex.parse('bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3');
var iv  = lib_crypt.CryptoJS.enc.Hex.parse('101112131415161718191a1b1c1d1e1f');

var encrypted = lib_crypt.CryptoJS.AES.encrypt($.passwordInput.value, key, { iv: iv });

var password_base64 = encrypted.ciphertext.toString(lib_crypt.CryptoJS.enc.Base64); 
return password_base64; 
});

サーバー側では、mcrypt_decryptで復号化したい:

function decryptPassword($password)
{
    $key = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");
    $ciphertext_dec = base64_decode($password);
    $iv_dec = "101112131415161718191a1b1c1d1e1f";

    $ciphertext_dec = substr($ciphertext_dec, 16);
    $decryptedPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);

    return trim($decryptedPassword);
}

同じキーとIVを使用していますが、どうすればよいですか?

7
Papauha

あなたは両方の側で同じことをしていません。

IV

CryptoJSでIVを解析しましたが、PHPで解析するのを忘れました。

_$iv_dec = pack('H*', "101112131415161718191a1b1c1d1e1f");
_

IVが間違っていることを修正するために、最初の16バイトが意味不明であることにおそらく気づいたでしょう。それはIVが間違っているときに起こります。 CryptoJSはデフォルトでCBCモードを使用するため、IVは復号化中の最初のブロックにのみ影響を与えることに注意してください。 削除これ:

_$ciphertext_dec = substr($ciphertext_dec, 16);
_

パディング

ほとんどの平文が正しく出てこないことにおそらく気づいたでしょう。彼らは最後にいくつかの奇妙な繰り返し文字で終わります。これは、CryptoJSでデフォルトで適用されるPKCS#7パディングです。 PHPでは自分でパディングを削除する必要があります。良いことは、Maarten Bodewesがこのための適切なコピーペーストソリューションを提供していることです ここ

trim()はZeroPaddingには適切かもしれませんが、PKCS#7で定義されているような適切なパディングスキームが使用されている場合には適切ではありません。 trim()呼び出しは、役に立たず、ゼロバイトと空白が最初と最後から削除されるため、予期しないプレーンテキストが生成される可能性があるため、完全に削除できます。

0
Artjom B.

こんにちは、

これを達成するためには、それぞれ32桁の16進数のキーとivを使用することを検討する必要があります。これを正確に解決する必要がありました。これが、その方法です。

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js">
</script>
<script type="text/javascript">
//The key and iv should be 32 hex digits each, any hex digits you want, but it needs to be 32 on length each
var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
var iv =  CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
/*
if you wish to have a more friendly key, you can convert letters to Hex this way:
var a = "D";
var hex_D = a.charCodeAt(0).toString(16);
just to mention,
if it were to binary, it would be:
var binary_D = a.charCodeAt(0).toString(2);
*/

var secret = "Hi, this will be seen uncrypted later on";

//crypted
var encrypted = CryptoJS.AES.encrypt(secret, key, {iv:iv});
//and the ciphertext put to base64
encrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64);    
//Assuming you have control on the server side, and know the key and iv hexes(we do),
//the encrypted var is all you need to pass through ajax,
//Let's follow with welcomed pure JS style, to reinforce one and other concept if needed
var xh = new XMLHttpRequest();
xh.open("POST", "decrypt_in_php.php", true);
xh.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xh.send("encrypted="+encodeURIComponent(encrypted));
</script>

そして今、PHPで受信して復号化しています

<?php
//Here we have the key and iv which we know, because we have just chosen them on the JS,
//the pack acts just like the parse Hex from JS

$key = pack("H*", "0123456789abcdef0123456789abcdef");
$iv =  pack("H*", "abcdef9876543210abcdef9876543210");

//Now we receive the encrypted from the post, we should decode it from base64,
$encrypted = base64_decode($_POST["encrypted"]);
$shown = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv);

echo $shown;
//Although the decrypted is shown, there may be needed to trim and str_replace some \r \n \x06 \x05, if there is not a better "trim" way to do it though
?>

これで、「こんにちは、これは後で暗号化されずに表示されます」に戻ります:)

17

これは、PHPのopenssl_decryptを使用した、 このコメント に基づくソリューションです。

JavaScriptの部分(ブラウザー用のNodeJSを使用した開発)—最初に CryptoJSnpm install crypto-jsでインストールし、次にJSをインストールしますコード:

import aes from 'crypto-js/aes'
import encHex from 'crypto-js/enc-hex'
import padZeroPadding from 'crypto-js/pad-zeropadding'

// message to encrypt
let msg = "Hello world";

// the key and iv should be 32 hex digits each, any hex digits you want, but it needs to be 32 on length each
let key = encHex.parse("0123456789abcdef0123456789abcdef");
let iv =  encHex.parse("abcdef9876543210abcdef9876543210");

// encrypt the message
let encrypted = aes.encrypt(msg, key, {iv:iv, padding:padZeroPadding}).toString();

// and finally, send this "encrypted" string to your server

[〜#〜] php [〜#〜]側では、コードは次のようになります。

// we use the same key and IV
$key = hex2bin("0123456789abcdef0123456789abcdef");
$iv =  hex2bin("abcdef9876543210abcdef9876543210");

// we receive the encrypted string from the post
$encrypted = $_POST['decrypt'];
$decrypted = openssl_decrypt($encrypted, 'AES-128-CBC', $key, OPENSSL_ZERO_PADDING, $iv);
// finally we trim to get our original string
$decrypted = trim($decrypted);
2
AymKdn