1

最初のステートメントを最初に処理する必要があると思います。Open SSL ライブラリを使用する場合、PHP が AES-128-CBC 暗号化を行う方法に矛盾がありますか?

AES のRFC3602を調べると、セクション 4 にいくつかのテスト ベクトルが見つかるため、これを尋ねています。最初に試したのは次のとおりです。

ケース #1: AES-CBC と 128 ビット キーを使用して 16 バイト (1 ブロック) を暗号化する

キー: 0x06a9214036b8a15b512e03d534120006

IV : 0x3dafba429d9eb430b422da802c9fac41

Painttext : "単一ブロック メッセージ"

Cphertext: 0xe353779c1079aeb82708942dbe77181a

次のコードでオープン SSL ライブラリを使用します。

echo bin2hex(openssl_encrypt($str, 'aes-128-cbc', $key, OPENSSL_RAW_DATA, $iv));

あなたは実際に戻ってきます:

e353779c1079aeb82708942dbe77181ab97c825e1c785146542d396941bce55d

これで問題なく復号化でき、最初の 32 バイトは予想される暗号文「e353779c1079aeb82708942dbe77181a」です。ただし、Open SSL ライブラリから返された暗号文の末尾には、さらに 32 バイトあるようです。どうしてこれなの?この質問は次に関連します。

私が楽しみのために行っていることの一部として、AES CBC モードの暗号化をゼロから実装しようとしています。これを行うために現在持っているコードは次のとおりです(以前に参照したRFCと、 「ブロック暗号操作モード」WikipediaページのCBC図に基づいています):

public function cbcEncrypt($str, $iv, $key) {
    $bl = 16;
    
    $bs = str_split($str,$bl);
    $pv = null;
    $r = '';
    
    foreach($bs as $b) {
        if($pv === null) $pv = $iv;
        if(strlen($b)<$bl) $b = $this->pkcs7Pad($b, $bl);
        $pv = openssl_encrypt($b^$pv, 'aes-128-ecb', $key, OPENSSL_RAW_DATA);
        $r .= $pv;
    }
    
    return $r;
}

現時点では、このコードが現在のテスト用の 16 バイト文字列よりも長い文字列では機能しないと確信していますが、このコードを Open SSL 実装と並べて実行して比較しました。CBC 実装による戻り値は次のとおりです。

e353779c1079aeb82708942dbe77181a8b1ccc6f8cd525ffe22d6327d891a063

次の方法で呼び出された場合:

$crypto = new CryptoTools();
$enc = $crypto->cbcEncrypt('Single block msg',hex2bin("3dafba429d9eb430b422da802c9fac41"),hex2bin("06a9214036b8a15b512e03d534120006"));

ここでも、最初の 32 バイトは正しいですが、2 番目の 32 バイトが追加されていますが、CBC の Open SSL 実装とは完全に異なります。理由の手がかりはありますか?

また、ECB モードの Open SSL 実装は 32 バイトの文字列を返します。これは、現在長さが 16 バイトを超える CBC 暗号化文字列を意味します。$pv の新しい値は長さが 32 バイトになり、2 番目の実際には $pv の長さは常に 16 バイトであってはならないのに、プレーン テキスト ブロックですか? 通常、$pv を 32 バイトから 16 バイトに切り詰めることは受け入れられますか?

念のため、上記のコードの pkcs7Pad メソッドは次のようになります。

public function pkcs7Pad($str, $b = 8) {
    if(trim($str) == '') return false;
    if((int)$b < 1)  return false;
    $p = $b - (strlen($str) % $b);
    return $str . str_repeat(chr($p),$p);
}

どんな助けでも大歓迎です。言うまでもなく、人々は通常、確かにPHPで車輪を再発明しようとするほど愚かではないため、これに関するドキュメントはあまりありません...

4

2 に答える 2

4

これはパディングと関係があります。テスト ベクトルはパディングをまったく使用しませんが、openssl_encrypt 関数と関数は PKCS#7 パディングを適用します。

すべての場合にパディングが追加されるように、PKCS#7 パディングが指定されています。平文の長さがブロック サイズの倍数である場合、パディングの完全なブロックが追加されます。これが、平文が 16 バイトの場合に暗号文の長さが 32 バイトになる理由です。

関数に問題があることはすぐにはわかりませんが (私は PHP に詳しくありません)、$str各ブロックにパディングが必要かどうかを確認する代わりに、分割する前にパディングを試すことができます。

于 2013-06-28T11:06:43.467 に答える
1

あはは!とった!ntoskrnl's answerとこの Stackoverflow questionのおかげで、解決策を思いつくことができました! もしあなたがそうするなら:

echo bin2hex(base64_decode(openssl_encrypt($str, 'aes-128-cbc', $key, OPENSSL_ZERO_PADDING, $iv)));

RFC が提供するテスト ケースと一致します。OPENSSL_RAW_DATA を実行していないため、base64 でエンコードされた文字列が返されるため、テスト ケースに一致するように 16 進数に変換する前に base64 でデコードする必要があることを覚えておく必要があります。

TLDR: これはパディングの問題であり、明らかな問題ではありません!

于 2013-06-28T12:33:57.067 に答える