赞
踩
C++使用openssl建立证书,进行签名,验签,加密,解密(基于RSA)
话不多说,步骤详细,总结坑点
巨坑 RSA_verify()验证签名总是无法成功
官网的介绍
int RSA_verify(int type, const unsigned char *m, unsigned int m_len,
unsigned char *sigbuf, unsigned int siglen, RSA *rsa);
/*RSA_verify() verifies that the signature sigbuf of size siglen matches a given message digest m of size m_len. type denotes the message digest algorithm that was used to generate the signature. rsa is the signer's public key.*/
意思就是type需要为NID_sha1,NID_md5,等等哈希类型,然后m是信息摘要(坑点),m_len是该摘要的长度,sigbuf是签名的内容,siglen是签名长度(坑点),rsa是读取的RSA私钥信息。
听起来很简单的样子,实际上对信息做签名很容易,但是私钥验证总是会报错,各种各样都有。下面总结坑点:
1:以前以为签名和验证签名就只要把message塞进签名函数,然后和签名后的信息放进验证函数里就行了,后来发现不是这样的,RSA的签名是对信息摘要(hash)来做的签名,以为着我们需要先把原先的信息做哈希,才能签名。同理,验签的时候也需要该哈希,和签名后的数据。(PS:其实真正的通信在签名过后还需要进行base64编码处理,对应接收时也需要base64解码,因为签名后的数据是二进制的,无法正常阅读)
2:明明做了哈希,却一直验签不成功?
官网告诉了我们一个很好用的函数接口,会给出具体的错误提示:
unsigned long ulErr = ERR_get_error();
这个函数会给出错误代码,配合其余的代码会告诉我们哪里出错,比如他告诉了我,我的签名数据的长度错误,siglen error,因为这里的签名是二进制,不能通过使用strlen()等方式获取,所以我在签名后打印了签名的长度(自动绑定在outlen中),才发现长度是128.(不管我做的sha1还是sha256或者md5)
RSA_sign(NID_sha1, (const unsigned char*)strData.c_str(), strData.length() , (unsigned char*)pEncode, &outlen, pRSAPriKey);
//签名数据存储在pEncode,对应长度存储在outlen
3:为什么???我做了哈希,然后按照官网的参数说明,总是报错:错误的私钥(公钥)
rsa routines:RSA_padding_check_PKCS1_type_1:invalid padding
可以明确的是,我的公私钥没有问题,因此开始定位,也没啥好说的,问题肯定在哈希函数里,所以我试了网上的很多办法,SHA256,SHA1,MD5等等,都无法成功验证,最后回归官网rsa_verify
使用了官方给出的SHA1头文件中的函数进行哈希,才成功的
生成1024位rsa私钥,保存为pem格式:
openssl genpkey -out prikey.pem -algorithm rsa
生成对应的公钥:
openssl pkey -in prikey.pem -pubout -out pubkey.pem
#include <iostream>
#include <functional>
#include <bits/stdc++.h>
#include <openssl/sha.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <iostream>
#include <string>
#include <cstring>
#include <cassert>
using namespace std;
//加密
std::string EncodeRSAKeyFile( const std::string& strPemFileName, const std::string& strData )
{
if (strPemFileName.empty() || strData.empty())
{
assert(false);
return "";
}
FILE* hPubKeyFile = fopen(strPemFileName.c_str(), "rb");
if( hPubKeyFile == NULL )
{
assert(false);
return "";
}
std::string strRet;
RSA* pRSAPublicKey = RSA_new();
if(PEM_read_RSA_PUBKEY(hPubKeyFile, &pRSAPublicKey, 0, 0) == NULL)
{
assert(false);
return "";
}
int nLen = RSA_size(pRSAPublicKey);
char* pEncode = new char[nLen + 1];
int ret = RSA_public_encrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pEncode, pRSAPublicKey, RSA_PKCS1_PADDING);
if (ret >= 0)
{
strRet = std::string(pEncode, ret);
}
delete[] pEncode;
RSA_free(pRSAPublicKey);
fclose(hPubKeyFile);
CRYPTO_cleanup_all_ex_data();
return strRet;
}
//签名 use private key
std::string SignRSAKeyFile( const std::string& strPemFileName, std::string& strData )
{
if (strPemFileName.empty() || strData.empty())
{
assert(false);
return "";
}
FILE* hPriKeyFile = fopen(strPemFileName.c_str(), "rb");
if( hPriKeyFile == NULL )
{
assert(false);
return "";
}
std::string strRet;
RSA* pRSAPriKey = RSA_new();
if(PEM_read_RSAPrivateKey(hPriKeyFile, &pRSAPriKey, 0, 0) == NULL)
{
assert(false);
return "";
}
int nLen = RSA_size(pRSAPriKey);
char* pEncode = new char[nLen + 1];
unsigned int outlen;
int ret = RSA_sign(NID_sha1, (const unsigned char*)strData.c_str(), strData.length() , (unsigned char*)pEncode, &outlen, pRSAPriKey);
if (ret >= 0)
{
strRet = std::string(pEncode);
std::cout << "\n" << strRet << endl;
//std::cout << "next \n" << pEncode << endl;
std::cout << "critical length:\n" << outlen << endl;
}
if( ret != 1)
std::cout << "sign failed\n";
delete[] pEncode;
RSA_free(pRSAPriKey);
fclose(hPriKeyFile);
CRYPTO_cleanup_all_ex_data();
return strRet;
}
//解密
std::string DecodeRSAKeyFile( const std::string& strPemFileName, const std::string& strData )
{
if (strPemFileName.empty() || strData.empty())
{
assert(false);
return "";
}
FILE* hPriKeyFile = fopen(strPemFileName.c_str(),"rb");
if( hPriKeyFile == NULL )
{
assert(false);
return "";
}
std::string strRet;
RSA* pRSAPriKey = RSA_new();
if(PEM_read_RSAPrivateKey(hPriKeyFile, &pRSAPriKey, 0, 0) == NULL)
{
assert(false);
return "";
}
int nLen = RSA_size(pRSAPriKey);
char* pDecode = new char[nLen+1];
int ret = RSA_private_decrypt(strData.length(), (const unsigned char*)strData.c_str(), (unsigned char*)pDecode, pRSAPriKey, RSA_PKCS1_PADDING);
if(ret >= 0)
{
strRet = std::string((char*)pDecode, ret);
}
delete [] pDecode;
RSA_free(pRSAPriKey);
fclose(hPriKeyFile);
CRYPTO_cleanup_all_ex_data();
return strRet;
}
//验证签名 use pubkey
int VerifyRSAKeyFile( const std::string& strPemFileName, const std::string& strData , const std::string& sign_data)
{
if (strPemFileName.empty() || strData.empty())
{
assert(false);
return 0;
}
FILE* hPubKeyFile = fopen(strPemFileName.c_str(), "rb");
if( hPubKeyFile == NULL )
{
assert(false);
return 0;
}
std::string strRet;
RSA* pRSAPublicKey = RSA_new();
if(PEM_read_RSA_PUBKEY(hPubKeyFile, &pRSAPublicKey, 0, 0) == NULL)
{
assert(false);
return 0;
}
int nLen = RSA_size(pRSAPublicKey);
char* pEncode = new char[nLen + 1];
unsigned int outlen;
int ret = RSA_verify(NID_sha1, (const unsigned char*)strData.c_str(), strlen(strData.c_str()), (const unsigned char*)sign_data.c_str(), 128, pRSAPublicKey);
if(ret != 1){
std::cout << "verify error\n";
unsigned long ulErr = ERR_get_error();
char szErrMsg[1024] = {0};
cout << "error number:" << ulErr << endl;
char *pTmp = NULL;
pTmp = ERR_error_string(ulErr,szErrMsg); // 格式:error:errId:库:函数:原因
cout << szErrMsg << endl;
return -1;
}
else
std::cout << "verify success\n";
delete[] pEncode;
RSA_free(pRSAPublicKey);
fclose(hPubKeyFile);
CRYPTO_cleanup_all_ex_data();
return 1;
}
int main(void)
{
string m = "test";
//hash SHA1
unsigned char digest[SHA_DIGEST_LENGTH];
SHA_CTX ctx;
SHA1_Init(&ctx);
SHA1_Update(&ctx, m.c_str(), strlen(m.c_str()));
SHA1_Final(digest, &ctx);
char mdString[SHA_DIGEST_LENGTH*2+1];
for (int i = 0; i < SHA_DIGEST_LENGTH; i++)
sprintf(&mdString[i*2], "%02x", (unsigned int)digest[i]);
cout << "digest:" << digest << endl;
std::cout << "SHA1 digest translate:" << mdString << endl;
string digest_s((char*)digest);
string mdString1(mdString);
auto sign_ = SignRSAKeyFile("prikey.pem",mdString1);
std::cout << "data:" << sign_ << std::endl;
std::cout << "verify:" << VerifyRSAKeyFile("pubkey.pem",(const string)mdString,sign_);
return 0;
}
————————————————
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。