C++使用openssl建立证书,进行签名,验签,加密,解密(基于RSA)

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;
}
————————————————


                        

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
OpenSSL是一个开源的加密库,它提供了RSA加密解密签名验签的功能。 对于RSA加密解密,我们可以使用OpenSSL提供的命令行工具或者API来实现。 使用命令行工具,我们可以通过以下命令进行RSA加密openssl rsautl -encrypt -in <input file> -out <output file> -inkey <public key file> -pubin 其中,<input file>是要加密的文件,<output file>是加密后的文件,<public key file>是存储公钥的文件,-pubin参数表示输入的是公钥。 使用命令行工具,我们可以通过以下命令进行RSA解密openssl rsautl -decrypt -in <input file> -out <output file> -inkey <private key file> 其中,<input file>是要解密的文件,<output file>是解密后的文件,<private key file>是存储私钥的文件。 对于RSA签名验签,我们可以使用以下命令进行签名openssl rsautl -sign -in <input file> -out <output file> -inkey <private key file> 其中,<input file>是要签名的文件,<output file>是签名后的文件,<private key file>是存储私钥的文件。 使用以下命令进行验签openssl rsautl -verify -in <input file> -out <output file> -inkey <public key file> -pubin 其中,<input file>是要验签的文件,<output file>是验签后的文件,<public key file>是存储公钥的文件,-pubin参数表示输入的是公钥。 使用OpenSSL的API进行RSA加密解密签名验签的操作也是类似的,我们可以通过调用相应的函数来实现。需要注意的是,API的使用需要在代码中显式引入OpenSSL的头文件和链接OpenSSL的库文件。 总之,OpenSSL提供了便捷的工具和API来实现RSA加密解密签名验签的功能,无论是命令行工具还是API,都可以选择合适的方式进行操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值