话不多说,步骤详细,总结坑点
巨坑 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 "Socket.hpp"
#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