1

对于我正在处理的项目,我需要使用 Blowfish 以跨 NSIS 和 PHP 的兼容方式加密和解密字符串。

目前我正在使用NSIS的Blowfish++ 插件mcrypt和PHP 库。问题是,我不能让它们都产生相同的输出。

让我们从 NSIS Blowfish++ 插件开始。基本上API是:

; Second argument needs to be base64 encoded
; base64_encode("12345678") == "MTIzNDU2Nzg="

blowfish::encrypt "test@test.com***" "MTIzNDU2Nzg="
Pop $0 ; 0 on success, 1 on failure
Pop $1 ; encrypted message on success, error message on failure

没有提到它是否是 CBC、ECB、CFB 等,而且我对 Blowfish 还不够熟悉,无法通过阅读大部分未记录的来源来判断。我认为它是 ECB ,因为PHP 文档mcrypt告诉我 ECB 不需要 IV

我还通过阅读源代码了解到 Blowfish++ 插件将对第二个参数进行 Base64 解码以进行加密(我不确定为什么)。它还返回一个 Base64 编码的字符串。

对于 PHP 方面,我基本上是使用这段代码来加密:

$plainText = "test@test.com***";
$cipher = mcrypt_module_open(MCRYPT_BLOWFISH, '', MCRYPT_MODE_ECB, '');   
$iv = '00000000';  // Show not be used anyway.
$key = "12345678";

$cipherText = "";
if (mcrypt_generic_init($cipher, $key, $iv) != -1)
{
    $cipherText = mcrypt_generic($cipher, $plainText);
    mcrypt_generic_deinit($cipher);        
}

echo base64_encode($cipherText);

但是,如果我做所有这些事情,我会得到以下输出:

NSIS: GyCyBcUE0s5gqVDshVUB8w==
PHP:  BQdlPd19zEkX5KT9tnF8Ng==

我究竟做错了什么?NSIS 插件不使用 ECB 吗?如果不是,它的 IV 有什么用?

4

1 回答 1

11

好的,我已经浏览了该代码并复制了您的结果。问题不在于密码模式——NSIS使用的ECB。问题是 NSIS Blowfish 代码在 little-endian 机器上被破坏了。

Blowfish 算法对两个 32 位无符号整数进行运算。要在 64 位明文或密文块和这两个整数之间进行转换,该块应该被解释为两个 Big Endian 整数。NSIS Blowfish 插件改为以主机字节顺序解释它们 - 因此它无法在 little-endian 主机(如 x86)上做正确的事情。这意味着它将与自身互操作,但不能与真正的 Blowfish 实现(如mcrypt)互操作。

我已经为您修补了 Blowfish++ 以使其做正确的事情 - 修改后的Blowfish::EncryptBlowfish::Decrypt在下面,新版本blowfish.cppPastebin 上

void Blowfish::Encrypt(void *Ptr,unsigned int N_Bytes)
{
    unsigned int i;
    unsigned char *Work;

    if (N_Bytes%8)
    {
            return;
    }

    Work = (unsigned char *)Ptr;

    for (i=0;i<N_Bytes;i+=8)
    {
        Word word0, word1;

        word0.byte.zero = Work[i];
        word0.byte.one = Work[i+1];
        word0.byte.two = Work[i+2];
        word0.byte.three = Work[i+3];

        word1.byte.zero = Work[i+4];
        word1.byte.one = Work[i+5];
        word1.byte.two = Work[i+6];
        word1.byte.three = Work[i+7];

        BF_En(&word0, &word1);

        Work[i] = word0.byte.zero;
        Work[i+1] = word0.byte.one;
        Work[i+2] = word0.byte.two;
        Work[i+3] = word0.byte.three;

        Work[i+4] = word1.byte.zero;
        Work[i+5] = word1.byte.one;
        Work[i+6] = word1.byte.two;
        Work[i+7] = word1.byte.three;
    }

    Work = NULL;
}

void Blowfish::Decrypt(void *Ptr, unsigned int N_Bytes)
{
    unsigned int i;
    unsigned char *Work;

    if (N_Bytes%8)
    {
            return;
    }

    Work = (unsigned char *)Ptr;
    for (i=0;i<N_Bytes;i+=8)
    {
        Word word0, word1;

        word0.byte.zero = Work[i];
        word0.byte.one = Work[i+1];
        word0.byte.two = Work[i+2];
        word0.byte.three = Work[i+3];

        word1.byte.zero = Work[i+4];
        word1.byte.one = Work[i+5];
        word1.byte.two = Work[i+6];
        word1.byte.three = Work[i+7];

        BF_De(&word0, &word1);

        Work[i] = word0.byte.zero;
        Work[i+1] = word0.byte.one;
        Work[i+2] = word0.byte.two;
        Work[i+3] = word0.byte.three;

        Work[i+4] = word1.byte.zero;
        Work[i+5] = word1.byte.one;
        Work[i+6] = word1.byte.two;
        Work[i+7] = word1.byte.three;
    }

    Work = NULL;
}
于 2010-01-28T11:26:07.587 回答