在项目中遇到需要使用sm4加密的功能,同事在一个动态库(C库)里面使用了openssl中的sm4加密,成功编译并功能正常。我也需要在项目(QT程序)中同样的方式使用sm4加密与之通讯,结果我卡在链接的位置无法编程完成,下面是摘出来的demo:
#include <stdio.h>
#include <memory.h>
#include <malloc.h>
#include <openssl/ossl_typ.h>
#include <openssl/aes.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/sm2.h>
#include <openssl/sm4.h>
#include <openssl/evp.h>
#include <openssl/ec.h>
#define PKCS1_HEADER "-----BEGIN RSA PUBLIC KEY-----"
#define PKCS8_HEADER "-----BEGIN PUBLIC KEY-----"
enum EncryptType {
ET_RSA = 0,
ET_SM2
};
int main(int argc, char *argv[])
{
const int tokenSize = 256;
const int padding = AES_BLOCK_SIZE - tokenSize % AES_BLOCK_SIZE;
const int blockCount = 256;
const int bufferSize = blockCount * AES_BLOCK_SIZE;
char *tokenBuffer = new char[static_cast<size_t>(bufferSize)];
memset(tokenBuffer, padding, static_cast<size_t>(bufferSize));
char *ciphertext = new char[static_cast<size_t>(bufferSize)];
memset(ciphertext, 0, static_cast<size_t>(bufferSize));
char *symmetricKey = new char[static_cast<size_t>(bufferSize)];
SM4_KEY sm4;
AES_KEY aes;
int ret = 0;
EncryptType m_encryptType = ET_SM2;
if (ET_SM2 == m_encryptType) {
SM4_set_key(reinterpret_cast<uint8_t *>(symmetricKey), &sm4);
} else {
AES_set_encrypt_key(reinterpret_cast<unsigned char *>(symmetricKey), strlen(symmetricKey), &aes);
}
if (ret < 0) {
delete[] tokenBuffer;
delete[] ciphertext;
return 0;
}
unsigned char *iv = new unsigned char[AES_BLOCK_SIZE];
memset(iv, 0, AES_BLOCK_SIZE);
if (ET_SM2 == m_encryptType) {
SM4_encrypt(reinterpret_cast<uint8_t *>(tokenBuffer), reinterpret_cast<uint8_t *>(ciphertext), &sm4);
} else {
AES_cbc_encrypt(reinterpret_cast<unsigned char *>(tokenBuffer), reinterpret_cast<unsigned char *>(ciphertext), static_cast<size_t>(bufferSize), &aes, iv, AES_ENCRYPT);
}
delete[] ciphertext;
delete[] tokenBuffer;
delete[] iv;
return 0;
}
报错如下:
$ gcc -o libtest.so test.cpp -lstdc++ -lcrypto
/usr/bin/ld: /tmp/ccR5x6An.o: in function `main':
test.cpp:(.text+0xb0): undefined reference to `SM4_set_key(unsigned char const*, SM4_KEY_st*)'
/usr/bin/ld: test.cpp:(.text+0x14f): undefined reference to `SM4_encrypt(unsigned char const*, unsigned char*, SM4_KEY_st const*)'
collect2: error: ld returned 1 exit status
库肯定是link成功了 ,因为其他的函数都没有报错,只有sm4的程序报了。我也是百思不得其解,我尝试使用同事编译过的项目,发现,加上-shared就能编译通过
gcc -shared -o libtest.so test.cpp -lstdc++ -lcrypto
很离谱,我的QT界面项目当然不能加这个参数。
我就仔细看了下sm4.h,忽然发现头文件中没有用extern "C"修复函数!
C++在编译的过程中会改变函数的名称,与C语言的规则不一致,这样在链接的时候就无法找到C语言对应的函数,从而导致undefined reference!
在需要使用源文件中将原来的include 用extern "C"修饰就可以编译通过了。
#ifdef __cplusplus
extern "C" {
#endif
#include <openssl/sm2.h>
#include <openssl/sm4.h>
#ifdef __cplusplus
}
#endif
CMakeLists
需要注意的几个地方:
# cmake > 3.12 详见https://cmake.org/cmake/help/latest/module/FindOpenSSL.html
find_package(OpenSSL REQUIRED)
target_link_libraries(${ProjectName} PRIVATE
${OPENSSL_CRYPTO_LIBRARY}
)
总结
在C++中使用C库的时候,如果遇到链接问题,要注意C库的头文件中是否使用extern "C"修饰了函数。