OpenSSL 3.1.1 ECC 加密、解密、签名、验签(国密 sm2、sm3)

这篇文章展示了如何使用OpenSSL3.1.1的新接口进行ECC加密解密,特别是针对国密算法SM2。通过C++类`sm2PrivateKey`和`sm2PublicKey`实现了密钥对生成、公钥导入导出、加密解密以及签名验签的功能。代码中包含了错误处理和内存管理,简化了密码学操作的复杂性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OpenSSL 3.1.1 ECC 加密、解密、签名、验签(国密 sm2、sm3)

openssl 3 默认废弃了 旧版本 (opessl 1.x) 的部分api 导致部分旧ecc 代码无法使用(可以通过配置编译选项打开)
,这里展示如何使用新接口用ECC 进行加密解密。

新接口是真的方便,基本上你都不需要懂啥密码学知识,对我们这种密码白痴来说太好了

头文件
生成密钥对
导出公钥&导入公钥
公钥加密
私钥解密
私钥签名
公钥验签
工具函数
所有代码(后来改过)

头文件

小小的封装了一下


#include "openssl/crypto.h"
#include "openssl/types.h"
#include "openssl/x509.h"
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/pem.h>


class sm2PrivateKey;

 struct EVP_CUNSTOM{
   
   
        EVP_PKEY * pkey=NULL;
        ~EVP_CUNSTOM(){
   
   
            if(pkey!=NULL){
   
   
                EVP_PKEY_free(pkey);
            }
        }
    };

class sm2PublicKey{
   
   
    public:
    sm2PublicKey()=default;

    sm2PublicKey(const sm2PublicKey & other){
   
   
        m_pkey=other.m_pkey;
    }

    sm2PublicKey(const std::string & pub_str);

    sm2PublicKey(const unsigned char * pub_str,size_t len);

    std::string Encrypt(const std::string  & message,std::string & error);
	
    bool SignatureVerification(const std::string & signature,const std::string & message,std::string & error);


    std::string GetPublicString();

    std::string GetPublicStringBase64();
private:
   
    std::shared_ptr<EVP_CUNSTOM> m_pkey=nullptr;//使用shared_ptr 防止拷贝构造的时候造成内存泄漏和意外释放
};


class sm2PrivateKey{
   
   
    public:

    sm2PrivateKey();

    sm2PrivateKey(const std::string & priv_str);

    sm2PublicKey CreatePublic();

    std::string Decrypt(const std::string & encoded,std::string & error);

    std::string Signature(const std::string & message ,std::string & error);

    std::string GetPrivateString();

private:

    std::shared_ptr<EVP_CUNSTOM>  M_PKEY=nullptr;
};

生成密钥对

sm2PrivateKey::sm2PrivateKey(){
   
   
    EVP_PKEY *ret = NULL;

    EVP_PKEY_CTX *pkctx = NULL;

    pkctx = EVP_PKEY_CTX_new_id(EVP_PKEY_SM2, NULL);//创建sm2 上下文
    if(pkctx==NULL){
   
   
        errorL("EVP_PKEY_CTX_new_id");
        return;
    }
    int retV=1;
    retV=EVP_PKEY_keygen_init(pkctx);//初始化sm2 上下文

    if (retV <= 0) {
   
   
       errorL("EVP_PKEY_keygen_init:" << GetErrorStr());
       EVP_PKEY_CTX_free(pkctx);
       return ;
    }


    retV=EVP_PKEY_keygen(pkctx, &ret);//生成密钥对
     if (retV <= 0) {
   
   
        errorL("EVP_PKEY_keygen:" << GetErrorStr());
        EVP_PKEY_CTX_free(pkctx);
        return ;
    }
    EVP_CUNSTOM * cst=new EVP_CUNSTOM{
   
   ret};
    M_PKEY=std::shared_ptr<EVP_CUNSTOM>(cst);
    EVP_PKEY_CTX_free(pkctx);
}

导出公钥和导入公钥

sm2PublicKey sm2PrivateKey::CreatePublic(){
   
   
    unsigned char *buffer=nullptr;
    int retV=i2d_PUBKEY(M_PKEY.get()->pkey, &buffer);//导出
     if (retV <= 0) {
   
   
        errorL("i2d_PUBKEY:" <<GetErrorStr());
        return sm2PublicKey{
   
   };
    }
    //buffer 里的是公钥二进制
    sm2PublicKey pub(buffer,retV);
    //OPENSSL_free(buffer);
    return pub;
}

sm2PublicKey::sm2PublicKey(const unsigned char * pub_str,size_t len){
   
   
    EVP_PKEY * pkey_t=NULL;
    //pkey_t=d2i_PublicKey(EVP_PKEY_SM2,NULL, &pub_str, len);
    pkey_t=d2i_PUBKEY(NULL, &pub_str, len);//导入
    std::string error;
    if(pkey_t==NULL){
   
   
        error=GetErrorStr();
        errorL(error);
        return;
    }
    EVP_CUNSTOM *cst=new EVP_CUNSTOM{
   
   pkey_t};
    m_pkey=std::shared_ptr<EVP_CUNSTOM>(cst);
}

公钥加密

std::string sm2PublicKey::Encrypt(const std::string &message,std::string &error) {
   
   
    std::string encodedstr;
     EVP_PKEY_CTX *pkctx = NULL;
    int retV=1;
    if (!(pkctx = EVP_PKEY_CTX_new(m_pkey.get()->pkey, NULL))) {
   
   //生成上下文
        error=GetErrorStr();
        errorL("EVP_PKEY_CTX_new:" << error);
         EVP_PKEY_CTX_free(pkctx);
         return "";
    }
    retV=EVP_PKEY_encrypt_init(pkctx);//加密初始化
    if (retV <= 0) {
   
   
        error=GetErrorStr();
       errorL("EVP_PKEY_encrypt_init:" <<error);
        EVP_PKEY_CTX_free(pkctx);
        return "";
    }

    size_t outbuflen=0;
    unsigned char * outbuf=NULL;
    retV=EVP_PKEY_encrypt(pkctx, NULL, &outbuflen, 
    		(const unsigned char *)message.c_str(), message.size());//加密 (传NULL 仅获取密文长度)
    if (retV <= 0) {
   
   
        error=GetErrorStr();
        errorL("EVP_PKEY_encrypt:" << error  );
        EVP_PKEY_CTX_free(pkctx);
        return "";
    }
    if(outbuflen==0){
   
   
        errorL("EVP_PKEY_encrypt:" << "no memery");
        EVP_PKEY_CTX_free(pkctx);
        return "";
    }

    outbuf=new unsigned char[outbuflen];

    retV=EVP_PKEY_encrypt(pkctx, outbuf, &outbuflen,
    		 (const unsigned char *)message.c_str(), message.size());//加密
    if (retV <= 0) {
   
   
        error=GetErrorStr();
        errorL("EVP_PKEY_encrypt:" << error  );
        EVP_PKEY_CTX_free(pkctx);
        delete[] outbuf;
        return "";
    }
    encodedstr=std::string((const char *)outbuf,outbuflen);//获取结果
    delete[] outbuf;
    EVP_PKEY_CTX_free(pkctx);
    return encodedstr;
}

私钥解密

std
OpenSSL 3.1.1版本中,要使用C语言接口进行中国国家密码局(SM2SM3、SM4等)相关的双证书认证和安全套接字层(SSL/TLS)链路建立,你需要先安装并设置支持国密OpenSSL发行版。以下是一般步骤: 1. **环境准备**: - 安装OpenSSL 3.1.1:确保你的系统上已经安装了OpenSSL 3.1.1或者更高版本,对于Linux系统,通常通过包管理器如`apt-get`或`yum`安装。 - 添加国密支持:检查你的OpenSSL是否包含对SM2的支持,如果没有,需要从OpenSSL官方或第三方源获取添加国密支持的补丁。 2. **库文件链接**: 编译时需要链接`crypto`和`sm2`库,例如: ``` gcc -o myapp myapp.c -lssl -lcrypto -lsm2 ``` 3. **初始化SM2上下文**: 使用`EVP_PKEY_CTX_new_id(EVP_PKEY_sm2, NULL)`创建一个新的SM2秘钥上下文。 4. **加载证书**: 加载国密证书,例如私钥用`EVP_PKEY_load`,公钥则用`X509`结构加载。 5. **双证书认证**: - 创建一个握手消息,传递公钥证书给对方进行证。 - 对方收到消息后,会使用接收者的私钥证消息。 6. **建立SSL连接**: 使用`SSL_library_init()`初始化SSL库,然后`SSL_CTX_new(TLSv1_2_server_method())`创建服务器上下文。设置必要的证书和加密选项,然后`SSL_new(SSL_CTX)`创建新的SSL连接。 7. **应用SM2算法**: - 在`SSL_connect()`之前或之后,可能需要调用`sm2_cipher`函数来设置特定的SM2密码套件。 8. **处理通信**: 使用`SSL_write`和`SSL_read`进行数据传输,注意SM2可能有不同的数据包装和解包机制。 9. **关闭资源**: 通信结束后,记得清理所有的上下文和内存,使用`SSL_free()`、`EVP_PKEY_CTX_free()`等函数。 请注意,具体的代码示例可能会因OpenSSL API的变化而有所调整。在编写实际代码时,建议查阅最新的OpenSSL文档或官方API指南。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值