OpenSSL和GmSSL都提供了SM2加解密的实现,GmSSL自带例子很简单就能跑起来,网上大部分代码都是基于OpenSSL。还有一些自己实现的代码,需要测试可靠性。
GmSSL加密串长度限制为255(SM2_MAX_PLAINTEXT_SIZE),不适合长文本的加解密。尝试分段加密方案,但测试时加密结果的长度不固定导致没法分段解密,没调通。转而使用OpenSSL库。
使用OpenSSL新版本,因OpenSSL 早先版本存在BUG。编译OpenSSL-1.1.1w,\openssl-1.1.1w\test\sm2_internal_test.c中有SM2测试代码,直接使用是不行的,链接时会提示“未定义的引用”,只找到了一个评论有关于它的解释“对于OpenSSL 1.1.1系列,sm2.h 是一个内部的头文件,其中定义的函数不对外公开”这里有原文。
CSDN里找了一个基于OpenSSL的代码实现,联系这位作者。
关于SM2加密调用要注意:
一、明确明文和密文的编码。
二、密码散列函数,默认使用国密SM3。
三、密文顺序,有C1||C2||C3 和 C1||C3||C2的区别。
四、公钥、私钥格式。一般为Hex字符串格式,也可以用PEM格式。公钥为65字节,第一个字节要注意。公钥是将0x4、公钥的X坐标(32字节长)、公钥的Y坐标(32字节长)三部分拼在一起构成的。其中0x4是一个特殊指示符字节,表示未对椭圆曲线上的点坐标表示进行过压缩处理。
代码分为加密、解密两个接口,参数为std::string。
//sInput:明文,shexPublicKey:Hex格式公钥,shexEnc:Hex格式密文
int SM2Encrypt_hex(const std::string& sInput, const std::string& shexPublicKey, std::string& shexEnc)
{
int error_code;
size_t msg_len = sInput.size();
SM2_KEY_PAIR key_pair;
unsigned char c1[65], c3[32];
unsigned char* c2;
long len;
unsigned char* pPubKey = OPENSSL_hexstr2buf(shexPublicKey.c_str(), &len);
memcpy(key_pair.pub_key, pPubKey, PUBLIC_KEY_SIZE);
//if (error_code = sm2_create_key_pair(&key_pair))
//{
// qDebug() << "Create SM2 key pair failed!\n";
// return (-1);
//}
//qDebug() << "Create SM2 key pair succeeded!\n";
qDebug() << "Public key:\n";
char* pub_key_str = OPENSSL_buf2hexstr(key_pair.pub_key, PUBLIC_KEY_SIZE);
QString sOut = QString("%1\n\n").arg(pub_key_str);
qDebug() << sOut;
qDebug() << "/*********************************************************/\n";
if (!(c2 = (unsigned char*)malloc(msg_len)))
{
qDebug() << "c2 Memory allocation failed!\n";
return ALLOCATION_MEMORY_FAIL;
}
if (error_code = sm2_encrypt((const unsigned char*)sInput.c_str(),
msg_len,
key_pair.pub_key,
c1,
c3,
c2))
{
qDebug() << "Create SM2 ciphertext failed!\n";
free(c2);
return error_code;
}
qDebug() << "Create SM2 ciphertext succeeded!\n";
qDebug() << "SM2 ciphertext:\n\n";
qDebug() << "C1 component:\n";
char* c1str = OPENSSL_buf2hexstr(c1, 65);
sOut = QString("%1\n").arg(c1str);
qDebug() << sOut;
qDebug() << "C3 component:\n";
char* c3str = OPENSSL_buf2hexstr(c3, 32);
sOut = QString("%1\n").arg(c3str);
qDebug() << sOut;
qDebug() << "C2 component:\n";
char* c2str = OPENSSL_buf2hexstr(c2, msg_len);
sOut = QString("%1\n").arg(c2str);
qDebug() << sOut;
std::stringstream ss;
ss << c1str << c3str << c2str;
shexEnc = ss.str();
free(c2);
return 0;
}
int SM2Decrypt_hex(const std::string& shexEnc, const std::string& shexPrivateKey, std::string& sOutput)
{
int error_code;
SM2_KEY_PAIR key_pair;
unsigned char c1[65], c3[32];
unsigned char* c2, * plaintext;
long len;
unsigned char* pPriKey = OPENSSL_hexstr2buf(shexPrivateKey.c_str(), &len);
memcpy(key_pair.pri_key, pPriKey, PRIVATE_KEY_SIZE);
unsigned char* pEnc = OPENSSL_hexstr2buf(shexEnc.c_str(), &len);
qDebug() << "C1 component:\n";
c2 = (unsigned char*)malloc(len - 97);
for (size_t i = 0; i < len; i++)
{
if (i < 65)
c1[i] = pEnc[i];
else if (i < 97)
c3[i - 65] = pEnc[i];
else
c2[i - 97] = pEnc[i];
}
free(pEnc);
size_t msg_len = len - 97;
if (!(plaintext = (unsigned char*)malloc(msg_len)))
{
free(c2);
qDebug() << "Memory allocation failed!\n";
return ALLOCATION_MEMORY_FAIL;
}
if (error_code = sm2_decrypt(c1,
c3,
c2,
msg_len,
key_pair.pri_key,
plaintext))
{
free(plaintext);
free(c2);
printf("Decrypt SM2 ciphertext failed!\n");
return error_code;
}
for (size_t i = 0; i < msg_len; i++)
sOutput.push_back(plaintext[i]);
free(plaintext);
free(c2);
return 0;
}
测试代码:
//密钥可以用sm2_create_key_pair生成
//这里私钥和公钥为Hex格式,明文为utf-8编码
int main(void)
{
QString sInput, sOutput;
QString sPrivateKey, sPublicKey;
sPrivateKey = QString(u8"4F:8A:65:8B:D7:9A:04:C1:52:18:A1:03:D2:78:EF:CE:EE:C2:BF:A1:3E:66:F9:2F:76:CA:96:65:50:77:56:D7");
sPublicKey = QString(u8"04:14:77:50:9F:72:AA:41:B8:2C:35:25:79:7D:2B:40:BB:FD:1F:5F:60:5D:BD:32:6F:1F:21:DE:86:1E:98:AA:9A6D:18:81:99:9F:FD:89:66:2E:4D:34:2C:84:7F:FA:DD:F9:E8:D6:E1:08:AE:58:26:DD:36:B7:D7:B5:18:E9:5A");
sInput = QString(u8"哈喽哈喽X23223414341SDss000999090dweimnfkhinbdmnbxh321314512341sdkfjiwejkngjkaWDFSFSDFW3245234154120587854wwdsfEWRW000999090324332dsfsfwefsedgdfgwefsdfweX23223414341SDss000999090dweimnfkhinbdmnbxh321314512341sdkfjiwejkngjkaWDFSFSDFW3245234154120587854wwdsfEWRW000999090324332dsfsfwefsedgdfgwefsdfweX23223414341SDss000999090dweimnfkhinbdmnbxh321314512341sdkfjiwejkngjkaWDFSFSDFW3245234154120587854wwdsfEWRW000999090324332dsfsfwefsedgdfgwefsdfweX23223414341SDss000999090dweimnfkhinbdmnbxh321314512341sdkfjiwejkngjkaWDFSFSDFW3245234154120587854wwdsfEWRW000999090324332dsfsfwefsedgdfgwefsdfweX23223414341SDss000999090dweimnfkhinbdmnbxh321314512341sdkfjiwejkngjkaWDFSFSDFW3245234154120587854wwdsfEWRW000999090324332dsfsfwefsedgdfgwefsdfweX23223414341SDss000999090dweimnfkhinbdmnbxh321314512341sdkfjiwejkngjkaWDFSFSDFW3245234154120587854wwdsfEWRW000999090324332dsfsfwefsedgdfgwefsdfwe哈喽哈喽");
std::string strEnc;
SM2Encrypt(sInput.toStdString(), sPublicKey.toStdString(), strEnc);
std::string strOutput;
SM2Decrypt(strEnc, sPrivateKey.toStdString(), strOutput);
sOutput = QString::fromStdString(strOutput);
if (0 != sOutput.compare(sInput, Qt::CaseInsensitive))
{
qDebug() << "Enc not justify Dec";
return -1;
}
return 0;
}
适配测试结果:
java使用Hutool(底层bouncycastle)SM2加密(解密),C++解密(加密)成功。