Erlang 和 PHP 之间的加密


Crypto between Erlang and PHP

Update 5

将问题切换到Security StackExchange - 得到了答案。https://security.stackexchange.com/questions/30168/aes-cfb-128-decryption-encryption-problem-between-erlang-and-php

更新 4

解决了问题后,我想 - 我试图继续实施。事实证明,这还不够。

一旦我们开始将真实的单词数据放入其中,一切都会下地狱。一个例子是将纯文本从1234567812345678增加到12345678123456781234567812345678

在这种情况下,cypertext 的第一个 128 位块是相同的,但第二个是不同的:

.PHP: 139 182 94 68 208 173 127 90 14 236 33 230 41 29 210 121 153 57 173 191 237 169 242 222 217 104 116 144 240 175 39 33

Erlang: <<139,182,94,68,208,173,127,90,14,236,33,230,41,29,210,121,147,172, 114,74,61,11,162,5,112,104,102,63,24,78,34,179>>

更新 3 - (我虽然是)解决了(但我错了)

这个答案提供了最后的线索:将 Crypto++ AES 加密移植到 PHP 的 mcrypt 时密钥大小不正确

问题是mycrypt中的'cfb'模式没有使用正确的反馈块大小来模拟aes cfb - 您需要使用模式nofb

更新 2

试图让 AES-CFB-128 在两者上工作 - 所以我已经确定使用具有 128 块大小和类型 'cfb'RINJDAEL_128 模式的 mycrypt 相当于 Erlang 中的 PHP crypto:aes_cfb_128/3

所以我开始用两种语言编写例程来证明这一点。

PHP版本:

<?php
// fugly because I don't know enough PHP to write it better
// big apologies to any PHP code poets out there, my bad :(
function dump($String, $Bin) {
echo $String . " is " . ord($Bin[0]) . " " . ord($Bin[1]) . " " . ord($Bin[2]) . " " . ord($Bin[3]) . " " . ord($Bin[4]) . " " . ord($Bin[5]) . " " . ord($Bin[6]) . " " . ord($Bin[7]) . " " . ord($Bin[8]) . " " . ord($Bin[9]) . " " . ord($Bin[10]) . " " . ord($Bin[11]) . " " . ord($Bin[12]) . " " . ord($Bin[13]) . " " . ord($Bin[14]) . " " . ord($Bin[15]) . "'n";
}
$Key  = "abcdefghabcdefgh";
$IV   = "12345678abcdefgh";
$Text = "1234567812345678";
$KeySize  = strlen($Key) * 8;
$IVSize   = strlen($IV) * 8;
$TextSize = strlen($Text) * 8;
$Size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CFB);
echo "Block size is " . $Size . " bytes or " . $Size * 8 . " bits'n";
$Crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $Key, $Text, MCRYPT_MODE_CFB, $IV);
$Decrypt = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $Key, $Crypt, MCRYPT_MODE_CFB, $IV);
echo "Key   is   " . $Key  . " with size " . $KeySize  . "'n";
echo "IV    is   " . $IV   . " with size " . $IVSize   . "'n";
echo "Text  is   " . $Text . " with size " . $TextSize . "'n";
echo "Crypt is " . $Crypt  . "'n";
dump("Crypt", $Crypt);
echo "Decrypt is " . $Decrypt . "'n";
dump("Decrypt", $Decrypt);
?>

当我运行它时,我得到以下 PHP 输出:

Block size is 16 bytes or 128 bits
Key   is   abcdefghabcdefgh with size 128
IV    is   12345678abcdefgh with size 128
Text  is   1234567812345678 with size 128
Crypt is ��*�b�ls�M��
Crypt is 139 0 188 42 175 98 18 177 108 27 115 189 77 144 127 176
Decrypt is 1234567812345678
Decrypt is 49 50 51 52 53 54 55 56 49 50 51 52 53 54 55 56

Erlang 版本是:

-module(test_crypto).
-export([
         test/0
        ]).
test() ->
    Key  = <<"abcdefghabcdefgh">>,
    IV   = <<"12345678abcdefgh">>,
    Text = <<"1234567812345678">> ,
    KeySize  = bit_size(Key),
    IVSize   = bit_size(IV),
    TextSize = bit_size(Text),
    io:format("Key  is ~p with size ~p~n", [Key, KeySize]),
    io:format("IV   is ~p with size ~p~n", [IV, IVSize]),
    io:format("Text is ~p with size ~p~n", [Text, TextSize]),
    Crypt = crypto:aes_cfb_128_encrypt(Key, IV, Text),
    io:format("Crypt is ~p~n", [Crypt]),
    Decrypt = crypto:aes_cfb_128_decrypt(Key, IV, Crypt),
    io:format("Decrypt is ~p~n", [Decrypt]),
    ok.

当我运行它时,我得到 Erlang 输出:

Key  is <<"abcdefghabcdefgh">> with size 128
IV   is <<"12345678abcdefgh">> with size 128
Text is <<"1234567812345678">> with size 128
Crypt is <<139,182,94,68,208,173,127,90,14,236,33,230,41,29,210,121>>
Decrypt is <<"1234567812345678">>

所以每个人都正确地执行加密/解密循环 - 但加密形式不同 - 我不能在 Erlang 和 PHP 之间使用它。

我相信有一些简单的解释 - 但我不知所措。

PHP

将字符串作为输入 - Erlang 接受二进制文件 - 但似乎 PHP ascii 字符串存储为二进制文件。是这样,还是我错过了索姆?

更新 1

我已经发现crypto:md5_mac/2函数可以用 php 函数复制:

function encrypt_term_hex($Key, $Msg) {
    return hash_hmac("md5", $Msg, $Key);
}

原始问题

我想在两个系统之间共享信息,一个用 Erlang 编写,一个用 PHP 编写。

该计划是使用已经在多个 Erlang 系统之间运行的系统,该系统涉及使用在 Erlang 系统之间共享的私钥对 Erlang 术语进行签名。

Erlang 端使用函数 crypto:md5_mac/2crypto:aes_cfb_128_encrypt/3,而函数又使用底层的 OpenSSL 加密库。

加密端(在 Erlang 中)是:

encrypt_bin(Key0, PlainT0) ->
    PlainT = extend(PlainT0),
    Key = crypto:md5_mac(get_server_salt(), Key0),
    crypto:aes_cfb_128_encrypt(Key, get_salt(), PlainT).

此函数的作用是获取一个键值对。然后,它将术语的大小扩展到固定大小,使用 md5_mac 和固定(共享)盐生成密钥,最后进行加密 - 使用另一个盐向量初始化它。

目前为止,一切都好。然后我的任务是在PHP中复制这个fn(当然还有它的解密孪生)。

Erlang 文档很好:

http://erlang.org/doc/man/crypto.html

试图弄清楚 Erlang 如何准确调用底层加密库有点困难 - 因为源代码显示的是晦涩难懂的 NIF 宏。

看起来PHP在OpenSSL周围实现了库包装器,所以它应该是直截了当的。不幸的是,我不能做它的头或尾。例如,我查看openssl-encrypt,它指出它没有记录:

http://php.net/manual/en/function.openssl-encrypt.php

有没有任何地方我可以得到关于OpenSSL如何在PHP中实现的好例子/文档?或者 Erlang NIF 加载器是如何工作的?还是两者兼而有之?

将问题切换到Security StackExchange - 得到了答案。 https://security.stackexchange.com/questions/30168/aes-cfb-128-decryption-encryption-problem-between-erlang-and-php