背景:近期需要在項目中進行RSA簽名驗證,廠商會給出pem格式的RSA公鑰。在以往項目中使用openssl讀取RSA公鑰時基本都是從pem文件中讀取,基本沒什麼問題,可最近由於項目需要需要從數據庫中讀取RSA公鑰,經查資料發現openssl提供了bio接口以支持各種形式的祕鑰讀取。
在使用bio接口從內存中讀取pem格式的公鑰時,總是讀取公鑰失敗,經不斷查找資料,發現在我們得到base64編碼的RSA公鑰後,從內存中讀取這個公鑰時要注意以下幾點:
(1)公鑰字符串開頭要加上“-----BEGIN PUBLIC KEY-----\n”,結尾加上“\n-----END PUBLIC KEY-----\n”。否則會出現error:0906D06C:PEM routines:PEM_read_bio:no start line
(2)公鑰字符串每隔64個字符要加一個換行,否則會報祕鑰格式錯誤。
c++代碼實現舉例:
int nPublicKeyLen = strPublicKey.size(); //strPublicKey爲base64編碼的公鑰字符串
for(int i = 64; i < nPublicKeyLen; i+=64)
{
if(strPublicKey[i] != '\n')
{
strPublicKey.insert(i, "\n");
}
i++;
}
strPublicKey.insert(0, "-----BEGIN PUBLIC KEY-----\n");
strPublicKey.append("\n-----END PUBLIC KEY-----\n");
BIO *bio = NULL;
RSA *rsa = NULL;
char *chPublicKey = const_cast<char *>(strPublicKey.c_str());
if ((bio = BIO_new_mem_buf(chPublicKey, -1)) == NULL) //從字符串讀取RSA公鑰
{
cout<<"BIO_new_mem_buf failed!"<<endl;
}
rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); //從bio結構中得到rsa結構
if (!rsa)
{
ERR_load_crypto_strings();
char errBuf[512];
ERR_error_string_n(ERR_get_error(), errBuf, sizeof(errBuf));
cout<< "load public key failed["<<errBuf<<"]"<<endl;
BIO_free_all(bio);
}
由於使用RSA_vefify函數未能成功驗證簽名,改爲使用evp相關函數進行簽名驗證。下面貼上使用公鑰進行簽名驗證的代碼:
string strBase64DecodedSig = base64_decode(strSignature); //strSignature爲簽名
char *chInAppDataSignature = const_cast<char *>(strBase64DecodedSig.c_str()); //簽名先進行base64解碼
int result = 0;
char *chInAppData = const_cast<char *>(strUnsignedData.c_str()); //strUnsignedData爲原始數據,即未加密數據
EVP_PKEY *evpKey = NULL;
EVP_MD_CTX ctx;
evpKey = EVP_PKEY_new();
if(evpKey == NULL)
{
cout<<"error EVP_PEKY_new"<<endl;
RSA_free(rsa);
BIO_free_all(bio);return;
}
if((result = EVP_PKEY_set1_RSA(evpKey,rsa)) != 1)
{
cout<<"error EVP_PKEY_set1_RSA"<<endl;
RSA_free(rsa);
EVP_PKEY_free(evpKey);
BIO_free_all(bio);return;
}
EVP_MD_CTX_init(&ctx);
if(result == 1 && (result = EVP_VerifyInit_ex(&ctx, EVP_sha1(), NULL)) != 1)
{
cout<<"error EVP_VerfyInit_ex"<<endl;
}
if(result == 1 && (result = EVP_VerifyUpdate(&ctx, chInAppData,strUnsignedData.size())) != 1)
{
cout<<"error EVP_VerifyUpdate"<<endl;
}
if(result == 1 && (result = EVP_VerifyFinal(&ctx, (unsigned char *)chInAppDataSignature, strBase64DecodedSig.size(), evpKey)) != 1)
{
cout<<"error EVP_VerifyFinal"<<endl;
}
if(result == 1)
{
cout<<"Verify success"<<endl;
}
else
{
ERR_load_crypto_strings();
char errBuf[512];
ERR_error_string_n(ERR_get_error(), errBuf, sizeof(errBuf));
cout<<"verify failed["<<errBuf<<"]"<<endl;
}
EVP_MD_CTX_cleanup(&ctx);
RSA_free(rsa);
EVP_PKEY_free(evpKey);
BIO_free_all(bio);