国密算法SM2实现(Openssl)

在openssl3.0+的版本上已经支持国密算法,在openssl3.0.x的版本中,使用的是EVP统一接口+provider。

Provider是算法的实现提供者,可以动态加载卸载。主要类型如下:

cpp 复制代码
-default:默认提供者(包含主流算法)
-legacy:遗留算法提供者(不安全的旧算法)
-fips:FIPS认证算法提供者
-base:基础功能提供者

EVP(Envelope)统一接口,是算法操作的统一操作层。

cpp 复制代码
应用程序->EVP统一接口->Provider实现
          (做什么)    (怎么做)

Provider加载方式

cpp 复制代码
#include<openssl/openssl.h>


//1.显式加载Provider
OSSL_PROVIDER *legacy = OSSL_PROVIDER_load(NULL,"legacy");
OSSL_PROVIDER *default = OSSL_PROVIDER_load(NULL,"default")


//2.卸载Provider
OSSL_PROVIDER_load(legacy);
legacy = NULL;

Evp接口

cpp 复制代码
//摘要算法(哈希)
EVP_MD_CTX     //SM3,SHA256

//对称加密
EVP_CIPHER_CTX    //SM4,AES...

//非对称加密/签名
EVP_PKEY_CTX     //SM2,RSA...

//密钥派生
EVP_KDF_CTX     //PBKDF2,HKDF...

//消息认证码
EVP_MAC_CTX     //HMAC,CMAC...

EVP工作原理

cpp 复制代码
EVP_MD_CTX *md_ctx = NULL;
EVP_MD *md = NULL;
ctx = EVP_MD_CTX_new();
md = EVP_sm3();

下面提供算法实现SM2,SM3,SM4:

cpp 复制代码
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/ec.h>
#include <openssl/provider.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

void print_hex(const char* label, const unsigned char* data, size_t len) {
    printf("%s: ", label);
    for (size_t i = 0; i < len; i++) {
        printf("%02x", data[i]);
    }
    printf("\n");
}

void handle_errors(void) {
    ERR_print_errors_fp(stderr);
    abort();
}

// 全局 providers
static OSSL_PROVIDER *legacy_provider = NULL;
static OSSL_PROVIDER *default_provider = NULL;

// 初始化 OpenSSL 3.x providers
int init_openssl_providers(void) {
    static int initialized = 0;
    
    if (!initialized) {
        // OpenSSL 3.x 初始化
        OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS | 
                           OPENSSL_INIT_ADD_ALL_CIPHERS |
                           OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
        
        // 加载 legacy provider(包含国密算法)
        legacy_provider = OSSL_PROVIDER_load(NULL, "legacy");
        if (!legacy_provider) {
            printf("警告:无法加载 legacy provider,国密算法可能不可用\n");
        }
        
        // 加载 default provider
        default_provider = OSSL_PROVIDER_load(NULL, "default");
        if (!default_provider) {
            printf("错误:无法加载 default provider\n");
            return 0;
        }
        
        initialized = 1;
        printf("OpenSSL providers 初始化成功\n");
    }
    
    return 1;
}

// 清理 providers
void cleanup_openssl_providers(void) {
    if (legacy_provider) {
        OSSL_PROVIDER_unload(legacy_provider);
        legacy_provider = NULL;
    }
    if (default_provider) {
        OSSL_PROVIDER_unload(default_provider);
        default_provider = NULL;
    }
    
    // OpenSSL 3.x 不需要 EVP_cleanup()
    // 但可以调用通用的清理函数
    EVP_cleanup();
    ERR_free_strings();
}

// ==================== SM3实现 ====================
int sm3_demo() {
    printf("\n=== SM3哈希算法示例 ===\n");
    
    EVP_MD_CTX *md_ctx = NULL;
    const EVP_MD *md = NULL;
    unsigned char digest[EVP_MAX_MD_SIZE];
    unsigned int digest_len = 0;
    
    const char *message = "Hello SM3! 测试消息";
    
    // 1. 创建消息摘要上下文
    md_ctx = EVP_MD_CTX_new();
    if (md_ctx == NULL) {
        handle_errors();
    }
    
    // 2. 获取SM3摘要算法
    md = EVP_sm3();
    if (md == NULL) {
        printf("SM3算法不可用,请检查OpenSSL配置\n");
        // 尝试使用新的 API 获取
        md = EVP_MD_fetch(NULL, "SM3", NULL);
        if (!md) {
            printf("错误:无法获取 SM3 算法\n");
            EVP_MD_CTX_free(md_ctx);
            return 0;
        }
    }
    
    // 3. 初始化、更新、完成
    if (1 != EVP_DigestInit_ex(md_ctx, md, NULL)) {
        handle_errors();
    }
    
    if (1 != EVP_DigestUpdate(md_ctx, message, strlen(message))) {
        handle_errors();
    }
    
    if (1 != EVP_DigestFinal_ex(md_ctx, digest, &digest_len)) {
        handle_errors();
    }
    
    printf("原始数据: %s\n", message);
    print_hex("SM3哈希值", digest, digest_len);
    
    // 4. 清理
    EVP_MD_CTX_free(md_ctx);
    
    // 如果使用了 EVP_MD_fetch,需要释放
    if (md != EVP_sm3()) {
        EVP_MD_free((EVP_MD*)md);
    }
    
    return 1;
}

// ==================== SM4实现 ====================
int sm4_demo() {
    printf("\n=== SM4对称加密示例 ===\n");
    
    EVP_CIPHER_CTX *ctx = NULL;
    const EVP_CIPHER *cipher = NULL;
    
    // SM4-CBC参数
    unsigned char key[16] = {
        0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
        0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10
    };
    
    unsigned char iv[16] = {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
    };
    
    unsigned char plaintext[] = "这是一个SM4加密测试消息";
    unsigned char ciphertext[256];
    unsigned char decryptedtext[256];
    int ciphertext_len = 0, decryptedtext_len = 0;
    int plaintext_len = strlen((char *)plaintext);
    int len = 0;
    
    printf("原始数据: %s\n", plaintext);
    print_hex("SM4密钥", key, 16);
    print_hex("初始化向量(IV)", iv, 16);
    
    // ========== 加密 ==========
    ctx = EVP_CIPHER_CTX_new();
    if (ctx == NULL) handle_errors();
    
    cipher = EVP_sm4_cbc();
    if (cipher == NULL) {
        // 尝试使用新 API 获取
        cipher = EVP_CIPHER_fetch(NULL, "SM4-CBC", NULL);
        if (cipher == NULL) {
            printf("SM4算法不可用\n");
            EVP_CIPHER_CTX_free(ctx);
            return 0;
        }
    }
    
    // 初始化加密
    if (1 != EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv)) {
        handle_errors();
    }
    
    // 设置PKCS7填充(默认)
    EVP_CIPHER_CTX_set_padding(ctx, 1);
    
    // 执行加密
    if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) {
        handle_errors();
    }
    ciphertext_len = len;
    
    if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
        handle_errors();
    }
    ciphertext_len += len;
    
    print_hex("加密结果", ciphertext, ciphertext_len);
    
    // ========== 解密 ==========
    // 重置上下文
    if (1 != EVP_CIPHER_CTX_reset(ctx)) {
        handle_errors();
    }
    
    // 初始化解密
    if (1 != EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) {
        handle_errors();
    }
    
    // 执行解密
    if (1 != EVP_DecryptUpdate(ctx, decryptedtext, &len, ciphertext, ciphertext_len)) {
        handle_errors();
    }
    decryptedtext_len = len;
    
    if (1 != EVP_DecryptFinal_ex(ctx, decryptedtext + len, &len)) {
        handle_errors();
    }
    decryptedtext_len += len;
    
    // 添加字符串结束符
    decryptedtext[decryptedtext_len] = '\0';
    
    printf("解密结果: %s\n", decryptedtext);
    
    // 清理
    EVP_CIPHER_CTX_free(ctx);
    
    // 如果使用了 EVP_CIPHER_fetch,需要释放
    if (cipher != EVP_sm4_cbc()) {
        EVP_CIPHER_free((EVP_CIPHER*)cipher);
    }
    
    return 1;
}

// ==================== SM2实现 ====================
int sm2_demo() {
    printf("\n=== SM2非对称加密示例 ===\n");
    
    // 确保 providers 已加载
    if (!init_openssl_providers()) {
        printf("错误:OpenSSL providers 初始化失败\n");
        return 0;
    }
    
    EVP_PKEY_CTX *pctx = NULL;
    EVP_PKEY *pkey = NULL;
    EVP_PKEY_CTX *ctx = NULL;
    size_t outlen = 0;
    
    unsigned char plaintext[] = "这是一个SM2加密测试消息";
    unsigned char *ciphertext = NULL;
    unsigned char *decrypted = NULL;
    size_t plaintext_len = strlen((char *)plaintext);
    size_t ciphertext_len = 0, decrypted_len = 0;
    
    printf("原始数据: %s\n", plaintext);
    
    // ========== 生成SM2密钥对 ==========
    printf("1. 生成SM2密钥对...\n");
    
    // OpenSSL 3.x 推荐使用新 API
    pctx = EVP_PKEY_CTX_new_from_name(NULL, "SM2", NULL);
    if (!pctx) {
        // 回退到旧 API
        pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
        if (!pctx) {
            handle_errors();
        }
        
        if (EVP_PKEY_keygen_init(pctx) <= 0) {
            handle_errors();
        }
        
        // 设置SM2曲线参数
        if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, 1172) <= 0) {
            printf("错误:SM2曲线不可用,NID_sm2=1172\n");
            EVP_PKEY_CTX_free(pctx);
            return 0;
        }
    } else {
        // 新 API
        if (EVP_PKEY_keygen_init(pctx) <= 0) {
            handle_errors();
        }
    }
    
    // 生成密钥对
    if (EVP_PKEY_generate(pctx, &pkey) <= 0) {
        handle_errors();
    }
    
    printf("  密钥对生成成功\n");
    printf("  密钥位数: %d bits\n", EVP_PKEY_get_bits(pkey));
    
    // ========== 加密 ==========
    printf("2. 使用公钥加密...\n");
    
    ctx = EVP_PKEY_CTX_new(pkey, NULL);
    if (!ctx) {
        handle_errors();
    }
    
    if (EVP_PKEY_encrypt_init(ctx) <= 0) {
        handle_errors();
    }
    
    // 获取输出长度
    if (EVP_PKEY_encrypt(ctx, NULL, &outlen, plaintext, plaintext_len) <= 0) {
        handle_errors();
    }
    
    ciphertext = (unsigned char *)malloc(outlen);
    if (!ciphertext) {
        printf("内存分配失败\n");
        goto cleanup;
    }
    
    // 执行加密
    if (EVP_PKEY_encrypt(ctx, ciphertext, &outlen, plaintext, plaintext_len) <= 0) {
        printf("加密失败,错误信息:\n");
        ERR_print_errors_fp(stderr);
        
        // 可能是算法不支持,尝试使用 SM2-specific 上下文
        EVP_PKEY_CTX_free(ctx);
        ctx = EVP_PKEY_CTX_new(pkey, NULL);
        
        // 设置 SM2 特定的加密参数
        if (EVP_PKEY_encrypt_init(ctx) <= 0 ||
            EVP_PKEY_encrypt(ctx, ciphertext, &outlen, plaintext, plaintext_len) <= 0) {
            printf("SM2加密完全失败\n");
            goto cleanup;
        }
    }
    ciphertext_len = outlen;
    
    printf("  加密成功,密文长度: %zu bytes\n", ciphertext_len);
    print_hex("  密文(前64字节)", ciphertext, ciphertext_len > 64 ? 64 : ciphertext_len);
    
    // ========== 解密 ==========
    printf("3. 使用私钥解密...\n");
    
    // 创建新的解密上下文
    EVP_PKEY_CTX_free(ctx);
    ctx = EVP_PKEY_CTX_new(pkey, NULL);
    if (!ctx) {
        handle_errors();
    }
    
    if (EVP_PKEY_decrypt_init(ctx) <= 0) {
        handle_errors();
    }
    
    // 获取输出长度
    if (EVP_PKEY_decrypt(ctx, NULL, &outlen, ciphertext, ciphertext_len) <= 0) {
        handle_errors();
    }
    
    decrypted = (unsigned char *)malloc(outlen + 1); // +1 for null terminator
    if (!decrypted) {
        printf("内存分配失败\n");
        goto cleanup;
    }
    
    // 执行解密
    if (EVP_PKEY_decrypt(ctx, decrypted, &outlen, ciphertext, ciphertext_len) <= 0) {
        printf("解密失败,错误信息:\n");
        ERR_print_errors_fp(stderr);
        goto cleanup;
    }
    decrypted_len = outlen;
    decrypted[decrypted_len] = '\0';
    
    printf("  解密成功,解密长度: %zu bytes\n", decrypted_len);
    printf("  解密结果: %s\n", decrypted);
    
    // ========== 清理 ==========
cleanup:
    if (ciphertext) free(ciphertext);
    if (decrypted) free(decrypted);
    if (ctx) EVP_PKEY_CTX_free(ctx);
    if (pctx) EVP_PKEY_CTX_free(pctx);
    if (pkey) EVP_PKEY_free(pkey);
    
    return 1;
}

// ==================== 综合示例:数值加密 ====================
int number_encryption_demo() {
    printf("\n=== 数值加密综合示例 ===\n");
    
    // 确保 providers 已加载
    if (!init_openssl_providers()) {
        printf("错误:OpenSSL providers 初始化失败\n");
        return 0;
    }
    
    // 准备要加密的数值
    int original_number = 1234567890;
    unsigned char number_bytes[sizeof(int)];
    memcpy(number_bytes, &original_number, sizeof(int));
    
    printf("原始数值: %d\n", original_number);
    print_hex("数值字节表示", number_bytes, sizeof(int));
    
    // 1. 使用SM3计算数值的哈希
    printf("\n1. SM3哈希验证:\n");
    EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
    const EVP_MD *md = EVP_sm3();
    if (!md) {
        md = EVP_MD_fetch(NULL, "SM3", NULL);
    }
    unsigned char hash[EVP_MAX_MD_SIZE];
    unsigned int hash_len = 0;
    
    if (md && md_ctx) {
        EVP_DigestInit_ex(md_ctx, md, NULL);
        EVP_DigestUpdate(md_ctx, number_bytes, sizeof(int));
        EVP_DigestFinal_ex(md_ctx, hash, &hash_len);
        print_hex("  SM3哈希值", hash, hash_len);
    }
    
    // 2. 使用SM4加密数值
    printf("\n2. SM4加密数值:\n");
    unsigned char sm4_key[16] = {0};
    unsigned char sm4_iv[16] = {0};
    
    // 生成随机密钥和IV
    if (RAND_bytes(sm4_key, sizeof(sm4_key)) <= 0 ||
        RAND_bytes(sm4_iv, sizeof(sm4_iv)) <= 0) {
        printf("错误:无法生成随机数\n");
    } else {
        print_hex("  随机SM4密钥", sm4_key, sizeof(sm4_key));
        print_hex("  随机IV", sm4_iv, sizeof(sm4_iv));
    }
    
    EVP_CIPHER_CTX *cipher_ctx = EVP_CIPHER_CTX_new();
    const EVP_CIPHER *cipher = EVP_sm4_cbc();
    if (!cipher) {
        cipher = EVP_CIPHER_fetch(NULL, "SM4-CBC", NULL);
    }
    unsigned char encrypted_number[256];
    int enc_len = 0, total_enc_len = 0;
    
    if (cipher && cipher_ctx) {
        // 加密
        EVP_EncryptInit_ex(cipher_ctx, cipher, NULL, sm4_key, sm4_iv);
        EVP_EncryptUpdate(cipher_ctx, encrypted_number, &enc_len, 
                         number_bytes, sizeof(int));
        total_enc_len = enc_len;
        EVP_EncryptFinal_ex(cipher_ctx, encrypted_number + enc_len, &enc_len);
        total_enc_len += enc_len;
        
        print_hex("  SM4加密结果", encrypted_number, total_enc_len);
        
        // 解密
        unsigned char decrypted_bytes[256];
        int dec_len = 0, total_dec_len = 0;
        
        EVP_CIPHER_CTX_reset(cipher_ctx);
        EVP_DecryptInit_ex(cipher_ctx, cipher, NULL, sm4_key, sm4_iv);
        EVP_DecryptUpdate(cipher_ctx, decrypted_bytes, &dec_len, 
                         encrypted_number, total_enc_len);
        total_dec_len = dec_len;
        EVP_DecryptFinal_ex(cipher_ctx, decrypted_bytes + dec_len, &dec_len);
        total_dec_len += dec_len;
        
        // 转换回整数
        int decrypted_number;
        memcpy(&decrypted_number, decrypted_bytes, sizeof(int));
        printf("  解密后数值: %d\n", decrypted_number);
        
        if (original_number == decrypted_number) {
            printf("  ✓ SM4加解密验证成功\n");
        } else {
            printf("  ✗ SM4加解密验证失败\n");
        }
    }
    
    // 3. 清理
    if (md_ctx) EVP_MD_CTX_free(md_ctx);
    if (cipher_ctx) EVP_CIPHER_CTX_free(cipher_ctx);
    
    // 释放 fetch 的资源
    if (md && md != EVP_sm3()) {
        EVP_MD_free((EVP_MD*)md);
    }
    if (cipher && cipher != EVP_sm4_cbc()) {
        EVP_CIPHER_free((EVP_CIPHER*)cipher);
    }
    
    return 1;
}

int main() {
    printf("========================================\n");
    printf("OpenSSL EVP国密算法演示 (OpenSSL 3.x兼容版)\n");
    printf("OpenSSL版本: %s\n", OpenSSL_version(OPENSSL_VERSION));
    printf("========================================\n");
    
    // 初始化 OpenSSL 3.x providers
    if (!init_openssl_providers()) {
        printf("错误:无法初始化 OpenSSL\n");
        return 1;
    }
    
    // 测试各算法
    int sm3_ok = sm3_demo();               // SM3哈希
    int sm4_ok = sm4_demo();               // SM4对称加密
    int sm2_ok = sm2_demo();               // SM2非对称加密
    int number_ok = number_encryption_demo(); // 数值加密示例
    
    // 清理
    cleanup_openssl_providers();
    
    printf("\n========================================\n");
    printf("演示完成\n");
    printf("算法支持状态:\n");
    printf("  SM3哈希: %s\n", sm3_ok ? "✓ 支持" : "✗ 不支持");
    printf("  SM4加密: %s\n", sm4_ok ? "✓ 支持" : "✗ 不支持");
    printf("  SM2加密: %s\n", sm2_ok ? "✓ 支持" : "✗ 不支持");
    printf("  数值加密: %s\n", number_ok ? "✓ 支持" : "✗ 不支持");
    printf("========================================\n");
    
    return 0;
}
相关推荐
爱上妖精的尾巴2 小时前
7-16 WPS JS宏 RandBetween、Address实例8--[唯一性]类的应用
开发语言·javascript·wps·js宏·jsa
从此不归路2 小时前
Qt5 进阶【10】应用架构与插件化设计实战:从「单体窗口」走向「可扩展框架」
开发语言·c++·qt·架构
sjjhd6522 小时前
C++模拟器开发实践
开发语言·c++·算法
曹天骄2 小时前
Cloudflare CDN 预热全面实战指南(含全球 PoP 解析 + 预热覆盖模型)
运维·开发语言·缓存
csbysj20202 小时前
传输对象模式(Object Transfer Pattern)
开发语言
步达硬件2 小时前
【Matlab】把视频里每一帧存为单独的图片
开发语言·matlab·音视频
黎雁·泠崖2 小时前
Java字符串API:String/StringBuffer/StringBuilder详解
java·开发语言
上海合宙LuatOS2 小时前
LuatOS socket基础知识和应用开发
开发语言·人工智能·单片机·嵌入式硬件·物联网·开源·php
凯子坚持 c2 小时前
Qt常用控件指南(6)
开发语言·qt