OPENSSL Blowfish CBC 加密不同于 PHP 到 C++
OPENSSL Blowfish CBC encryption differs from PHP to C++
我正在尝试使用 OPENSSL 库加密和解密 C++ 库和 PHP 服务器之间的通信。我想使用 Blowfish CBC 算法,但 C++ 代码和 PHP 代码的结果似乎不同。 C++ 代码取自此处:
这是PHP代码:
<?php
function strtohex($x)
{
$s='';
foreach (str_split($x) as $c) $s.=sprintf("%02X",ord($c));
return($s);
}
$encryptedMessage = openssl_encrypt("input", "BF-CBC", "123456", 0, "initvect");
echo $encryptedMessage;
echo "\n";
echo strtohex($encryptedMessage);
PHP 输出是这样的:
x9jDa2WMwvQ=
78396A446132574D7776513D
这是 C++ 代码:
bool do_encrypt(const char *in, unsigned char *out, int *outlen, unsigned char *key, unsigned char *iv)
{
int buflen, tmplen;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), nullptr, key, iv);
if (!EVP_EncryptUpdate(&ctx, out, &buflen, (unsigned char*)in, strlen(in)))
{
return false;
}
if (!EVP_EncryptFinal_ex(&ctx, out + buflen, &tmplen))
{
return false;
}
buflen += tmplen;
*outlen = buflen;
EVP_CIPHER_CTX_cleanup(&ctx);
return true;
}
unsigned char output[2048] = { 0 };
int outLen;
auto result = do_encrypt("input", output, &outLen, (unsigned char*)"123456", (unsigned char*)"initvect");
BIGNUM *outputStr = BN_new();
BN_bin2bn(output, outLen, outputStr);
cout << base64_encode(output, outLen) << "\n";
cout << BN_bn2hex(outputStr) << "\n";
C++ 输出是这样的:
EfRhhWqGmSQ=
11F461856A869924
有人可以帮我理解我做错了什么吗?任何帮助将不胜感激。
谢谢!
编辑 1:
在 jww 的 回答后,我设法修复了 C++ 代码,并且运行良好。我错过了 EVP_CIPHER_CTX_set_key_length 但是,我无法使 PHP 代码 return 相同,最终我们决定转向 AES 和它现在可以完美运行。谢谢!
对于您的 OpenSSL 代码,我相信您需要调用 EVP_CIPHER_CTX_set_key_length
来告诉库密钥只有 6 个字节。
让我把Crypto++扔进下面的竞技场。添加缺失的 EVP_CIPHER_CTX_set_key_length
OpenSSL 调用后,OpenSSL 和 Crypto++ 将收敛于正确答案。正确答案是 32CEBA7E046431EB
(十六进制)。
我不知道PHP发生了什么:
x9jDa2WMwvQ=
78396A446132574D7776513D
考虑到 x
是 ASCII 0x78,9
是 ASCII 0x39,我猜你对 Base64 字符串进行了十六进制编码。
$ cat test.cxx
#include "blowfish.h"
#include "modes.h"
#include "channels.h"
#include "filters.h"
#include "base64.h"
#include "hex.h"
using namespace CryptoPP;
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char* argv[])
{
const byte key[] = "123456"; // 6
const byte iv[] = "initvect"; // 8
CBC_Mode<Blowfish>::Encryption encryptor;
encryptor.SetKeyWithIV(key, 6, iv, 8);
string r1, r2;
ChannelSwitch chsw;
Base64Encoder e1(new StringSink(r1));
HexEncoder e2(new StringSink(r2));
chsw.AddDefaultRoute(e1);
chsw.AddDefaultRoute(e2);
string msg = "input";
StringSource ss(msg, true,
new StreamTransformationFilter(encryptor,
new Redirector(chsw)));
cout << r1 << endl;
cout << r2 << endl;
return 0;
}
测试程序结果为:
$ ./test.exe
Ms66fgRkMes=
32CEBA7E046431EB
这是 OpenSSL 部分。注意 EVP_EncryptInit_ex
被调用了两次。首先,调用 EVP_EncryptInit_ex
来设置分组密码 EVP_bf_cbc
。密钥长度设置为 EVP_CIPHER_CTX_set_key_length
。其次,调用 EVP_EncryptInit_ex
设置密钥和 iv.
#include <openssl/evp.h>
#include <iostream>
#include <iomanip>
#include <stdexcept>
using namespace std;
typedef unsigned char byte;
int main()
{
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int rc;
const byte key[] = "123456"; // 6
const byte iv[] = "initvect"; // 8
rc = EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), NULL, 0, 0);
if (rc != 1)
throw runtime_error("EVP_EncryptInit_ex failed");
rc = EVP_CIPHER_CTX_set_key_length(&ctx, 6);
if (rc != 1)
throw runtime_error("EVP_CIPHER_CTX_set_key_length failed");
rc = EVP_EncryptInit_ex(&ctx, NULL, NULL, key, iv);
if (rc != 1)
throw runtime_error("EVP_EncryptInit_ex failed");
const byte msg[] = "input";
byte buf[32];
int len1 = sizeof(buf), len2 = sizeof(buf);
rc = EVP_EncryptUpdate(&ctx, buf, &len1, msg, 5);
if (rc != 1)
throw runtime_error("EVP_EncryptUpdate failed");
rc = EVP_EncryptFinal_ex(&ctx, buf+len1, &len2);
if (rc != 1)
throw runtime_error("EVP_EncryptFinal_ex failed");
for(unsigned int i=0; i<len1+len2; i++)
cout << std::hex << setw(2) << setfill('0') << (int)buf[i];
cout << endl;
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
我正在尝试使用 OPENSSL 库加密和解密 C++ 库和 PHP 服务器之间的通信。我想使用 Blowfish CBC 算法,但 C++ 代码和 PHP 代码的结果似乎不同。 C++ 代码取自此处:
这是PHP代码:
<?php
function strtohex($x)
{
$s='';
foreach (str_split($x) as $c) $s.=sprintf("%02X",ord($c));
return($s);
}
$encryptedMessage = openssl_encrypt("input", "BF-CBC", "123456", 0, "initvect");
echo $encryptedMessage;
echo "\n";
echo strtohex($encryptedMessage);
PHP 输出是这样的:
x9jDa2WMwvQ=
78396A446132574D7776513D
这是 C++ 代码:
bool do_encrypt(const char *in, unsigned char *out, int *outlen, unsigned char *key, unsigned char *iv)
{
int buflen, tmplen;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), nullptr, key, iv);
if (!EVP_EncryptUpdate(&ctx, out, &buflen, (unsigned char*)in, strlen(in)))
{
return false;
}
if (!EVP_EncryptFinal_ex(&ctx, out + buflen, &tmplen))
{
return false;
}
buflen += tmplen;
*outlen = buflen;
EVP_CIPHER_CTX_cleanup(&ctx);
return true;
}
unsigned char output[2048] = { 0 };
int outLen;
auto result = do_encrypt("input", output, &outLen, (unsigned char*)"123456", (unsigned char*)"initvect");
BIGNUM *outputStr = BN_new();
BN_bin2bn(output, outLen, outputStr);
cout << base64_encode(output, outLen) << "\n";
cout << BN_bn2hex(outputStr) << "\n";
C++ 输出是这样的:
EfRhhWqGmSQ=
11F461856A869924
有人可以帮我理解我做错了什么吗?任何帮助将不胜感激。
谢谢!
编辑 1: 在 jww 的 回答后,我设法修复了 C++ 代码,并且运行良好。我错过了 EVP_CIPHER_CTX_set_key_length 但是,我无法使 PHP 代码 return 相同,最终我们决定转向 AES 和它现在可以完美运行。谢谢!
对于您的 OpenSSL 代码,我相信您需要调用 EVP_CIPHER_CTX_set_key_length
来告诉库密钥只有 6 个字节。
让我把Crypto++扔进下面的竞技场。添加缺失的 EVP_CIPHER_CTX_set_key_length
OpenSSL 调用后,OpenSSL 和 Crypto++ 将收敛于正确答案。正确答案是 32CEBA7E046431EB
(十六进制)。
我不知道PHP发生了什么:
x9jDa2WMwvQ=
78396A446132574D7776513D
考虑到 x
是 ASCII 0x78,9
是 ASCII 0x39,我猜你对 Base64 字符串进行了十六进制编码。
$ cat test.cxx
#include "blowfish.h"
#include "modes.h"
#include "channels.h"
#include "filters.h"
#include "base64.h"
#include "hex.h"
using namespace CryptoPP;
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char* argv[])
{
const byte key[] = "123456"; // 6
const byte iv[] = "initvect"; // 8
CBC_Mode<Blowfish>::Encryption encryptor;
encryptor.SetKeyWithIV(key, 6, iv, 8);
string r1, r2;
ChannelSwitch chsw;
Base64Encoder e1(new StringSink(r1));
HexEncoder e2(new StringSink(r2));
chsw.AddDefaultRoute(e1);
chsw.AddDefaultRoute(e2);
string msg = "input";
StringSource ss(msg, true,
new StreamTransformationFilter(encryptor,
new Redirector(chsw)));
cout << r1 << endl;
cout << r2 << endl;
return 0;
}
测试程序结果为:
$ ./test.exe
Ms66fgRkMes=
32CEBA7E046431EB
这是 OpenSSL 部分。注意 EVP_EncryptInit_ex
被调用了两次。首先,调用 EVP_EncryptInit_ex
来设置分组密码 EVP_bf_cbc
。密钥长度设置为 EVP_CIPHER_CTX_set_key_length
。其次,调用 EVP_EncryptInit_ex
设置密钥和 iv.
#include <openssl/evp.h>
#include <iostream>
#include <iomanip>
#include <stdexcept>
using namespace std;
typedef unsigned char byte;
int main()
{
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int rc;
const byte key[] = "123456"; // 6
const byte iv[] = "initvect"; // 8
rc = EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), NULL, 0, 0);
if (rc != 1)
throw runtime_error("EVP_EncryptInit_ex failed");
rc = EVP_CIPHER_CTX_set_key_length(&ctx, 6);
if (rc != 1)
throw runtime_error("EVP_CIPHER_CTX_set_key_length failed");
rc = EVP_EncryptInit_ex(&ctx, NULL, NULL, key, iv);
if (rc != 1)
throw runtime_error("EVP_EncryptInit_ex failed");
const byte msg[] = "input";
byte buf[32];
int len1 = sizeof(buf), len2 = sizeof(buf);
rc = EVP_EncryptUpdate(&ctx, buf, &len1, msg, 5);
if (rc != 1)
throw runtime_error("EVP_EncryptUpdate failed");
rc = EVP_EncryptFinal_ex(&ctx, buf+len1, &len2);
if (rc != 1)
throw runtime_error("EVP_EncryptFinal_ex failed");
for(unsigned int i=0; i<len1+len2; i++)
cout << std::hex << setw(2) << setfill('0') << (int)buf[i];
cout << endl;
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}