国密数字信封技术原理与实现

密码学概念 · 2026-07-05

概述

数字信封(Digital Envelope)是一种将对称加密的高效性和非对称加密的密钥分发安全性相结合的密码学技术。它通过非对称加密保护对称密钥,再用对称加密保护实际数据,实现了"用公钥锁住信封(对称密钥),用私钥打开信封取数据"的巧妙设计。

在中国商用密码体系中,数字信封特指使用国密算法(SM2 + SM4)实现的数字信封方案,遵循GM/T 0044.2-2014《基于SM2密码算法的证书认证系统密码及其相关安全技术规范 第2部分:数字信封技术规范》。该技术广泛应用于电子政务、金融支付、医疗健康、物联网等对数据安全有严格要求的场景。

基本原理

1. 为什么需要数字信封

纯对称加密和非对称加密各有优劣:

特性对称加密(SM4/AES)非对称加密(SM2/RSA)
加密速度极快(硬件加速)慢(100-1000倍)
密钥管理困难(N个用户需N²个密钥)简单(仅需公钥)
密钥长度128/256位256位(SM2)/ 2048+位(RSA)
适用场景大数据量加密小数据量加密、密钥交换
安全性依赖密钥保密依赖数学难题(离散对数/因子分解)
数字信封的设计哲学是:各取所长。用非对称加密的安全方式传递对称密钥,用对称加密的高效方式处理大量数据。

2. 工作流程

#### 2.1 加密流程(发送方)

#### 2.2 数学描述

设:

  • $M$:明文消息
  • $K$:随机生成的SM4对称密钥(128位)
  • $PK_R$:接收方的SM2公钥
  • $SK_R$:接收方的SM2私钥
  • $Enc_{sm2}(PK_R, \cdot)$:SM2加密运算
  • $Dec_{sm2}(SK_R, \cdot)$:SM2解密运算
  • $Enc_{sm4}(K, \cdot)$:SM4加密运算(CBC/GCM模式)
  • $Dec_{sm4}(K, \cdot)$:SM4解密运算
则数字信封的输出为:

$$Envelope = (E_K, C) = (Enc_{sm2}(PK_R, K), Enc_{sm4}(K, M))$$

接收方恢复明文:

$$M = Dec_{sm4}(K, C) = Dec_{sm4}(Dec_{sm2}(SK_R, E_K), C)$$

3. SM2数字信封的详细过程

GM/T 0044.2-2014定义了SM2数字信封的具体实现方式。SM2加密采用C1C3C2顺序(非标准ASN.1 DER编码):

CODE
SM2加密过程:
1. 随机选择整数 k ∈ [1, n-1](n为SM2曲线阶数)
2. C1 = [k]G (G为生成点,k为随机数,[k]表示标量乘法)
3. [k]PB (PB为接收方公钥,[k]表示标量乘法)
4. (X2, Y2) = [k]PB 的坐标
5. t = KDF(X2 || Y2, klen) (密钥派生函数)
6. C2 = M ⊕ t (明文与派生密钥异或)
7. C3 = Hash(X2 || M || Y2) (SM3哈希)
8. 密文输出:C1 || C3 || C2

SM2数字信封中,对称密钥K(128位)作为明文M输入上述加密过程,输出C1||C3||C2形式的密文。

关键点:SM2加密本身是概率性的(每次加密使用不同的随机数k),这意味着相同的对称密钥K使用相同的SM2公钥加密两次,会得到完全不同的密文。这一性质保证了数字信封的语义安全性。

国密数字信封的实现

1. Python实现(gmssl库)

java // ⚠️ 环境要求:Bouncy Castle 1.70+,需启用国密提供程序 import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.crypto.engines.SM2Engine; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.GCMParameterSpec; import java.security.Security; import java.security.SecureRandom; import java.util.Arrays;

public class DigitalEnvelope {

static { Security.addProvider(new BouncyCastleProvider()); }

/** * 数字信封加密 */ public static byte[] encrypt(byte[] data, ECPublicKey publicKey) throws Exception { // 1. 生成随机SM4密钥 KeyGenerator kg = KeyGenerator.getInstance("SM4", "BC"); kg.init(128); SecretKey sm4Key = kg.generateKey(); byte[] sm4KeyBytes = sm4Key.getEncoded();

// 2. SM2加密SM4密钥 SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2); sm2Engine.init(true, publicKey); byte[] encryptedSm4Key = sm2Engine.processBlock(sm4KeyBytes, 0, sm4KeyBytes.length);

// 3. SM4-GCM加密数据 Cipher sm4Cipher = Cipher.getInstance("SM4/GCM/NoPadding", "BC"); byte[] iv = new byte[12]; new SecureRandom().nextBytes(iv); GCMParameterSpec paramSpec = new GCMParameterSpec(128, iv); sm4Cipher.init(Cipher.ENCRYPT_MODE, sm4Key, paramSpec); byte[] encryptedData = sm4Cipher.doFinal(data);

// 4. 组装信封 // 格式: [encryptedSm4Key长度(4字节)][encryptedSm4Key][iv(12字节)][encryptedData] ByteBuffer buffer = ByteBuffer.allocate(4 + encryptedSm4Key.length + 12 + encryptedData.length); buffer.putInt(encryptedSm4Key.length); buffer.put(encryptedSm4Key); buffer.put(iv); buffer.put(encryptedData); return buffer.array(); }

/** * 数字信封解密 */ public static byte[] decrypt(byte[] envelope, ECPrivateKey privateKey, ECDomainParameters domain) throws Exception { ByteBuffer buffer = ByteBuffer.wrap(envelope); int keyLen = buffer.getInt(); byte[] encryptedSm4Key = new byte[keyLen]; buffer.get(encryptedSm4Key); byte[] iv = new byte[12]; buffer.get(iv); byte[] encryptedData = new byte[buffer.remaining()]; buffer.get(encryptedData);

// 1. SM2解密SM4密钥 SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2); sm2Engine.init(false, new ECPrivateKeyParameters(privateKey.getD(), domain)); byte[] sm4KeyBytes = sm2Engine.processBlock(encryptedSm4Key, 0, keyLen);

// 2. SM4-GCM解密数据 Cipher sm4Cipher = Cipher.getInstance("SM4/GCM/NoPadding", "BC"); GCMParameterSpec paramSpec = new GCMParameterSpec(128, iv); sm4Cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(sm4KeyBytes, "SM4"), paramSpec); return sm4Cipher.doFinal(encryptedData); } }

CODE
### 3. C/C++实现(Tongsuo/OpenSSL)
c /* * ⚠️ 环境要求:Tongsuo (原BabaSSL) 或 GmSSL 库 * 标准OpenSSL不支持国密SM2/SM4 * 编译: gcc -o envelope envelope.c -ltongsuo -lcrypto */

#include #include #include #include #include #include

#define SM4_KEY_LEN 16 #define SM4_IV_LEN 12 #define SM4_GCM_TAG_LEN 16

/** * 数字信封加密 * 输入: plaintext, plaintext_len, public_key * 输出: 动态分配的信封缓冲区(调用者free) */ int digital_envelope_encrypt( const unsigned char *plaintext, size_t plaintext_len, EC_KEY *pub_key, unsigned char **out_envelope, size_t *out_len) { unsigned char sm4_key[SM4_KEY_LEN]; unsigned char sm4_iv[SM4_IV_LEN]; unsigned char encrypted_sm4_key[512]; // SM2密文最大512字节 unsigned char encrypted_data[4096]; unsigned char gcm_tag[SM4_GCM_TAG_LEN];

// 1. 生成随机SM4密钥和IV if (RAND_bytes(sm4_key, SM4_KEY_LEN) != 1 || RAND_bytes(sm4_iv, SM4_IV_LEN) != 1) { return -1; }

// 2. SM2加密SM4密钥 int encrypted_key_len = SM2_encrypt(pub_key, sm4_key, SM4_KEY_LEN, encrypted_sm4_key, sizeof(encrypted_sm4_key)); if (encrypted_key_len <= 0) { return -1; }

// 3. SM4-GCM加密数据 EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); int len = 0, total_len = 0;

EVP_EncryptInit_ex(ctx, EVP_sm4_gcm(), NULL, NULL, NULL); EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, SM4_IV_LEN, NULL); EVP_EncryptInit_ex(ctx, NULL, NULL, sm4_key, sm4_iv); EVP_EncryptUpdate(ctx, encrypted_data, &len, plaintext, plaintext_len); total_len = len; EVP_EncryptFinal_ex(ctx, encrypted_data + len, &len); total_len += len; EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, SM4_GCM_TAG_LEN, gcm_tag); EVP_CIPHER_CTX_free(ctx);

// 4. 组装信封: [sm2密文长度(2B)][sm2密文][iv(12B)][gcm_tag(16B)][密文] size_t envelope_size = 2 + encrypted_key_len + SM4_IV_LEN + SM4_GCM_TAG_LEN + total_len; *out_envelope = malloc(envelope_size); if (!*out_envelope) return -1;

unsigned char *p = *out_envelope; p[0] = (encrypted_key_len >> 8) & 0xFF; p[1] = encrypted_key_len & 0xFF; p += 2; memcpy(p, encrypted_sm4_key, encrypted_key_len); p += encrypted_key_len; memcpy(p, sm4_iv, SM4_IV_LEN); p += SM4_IV_LEN; memcpy(p, gcm_tag, SM4_GCM_TAG_LEN); p += SM4_GCM_TAG_LEN; memcpy(p, encrypted_data, total_len);

*out_len = envelope_size; return 0; }

患者病历数据 → SM4-GCM加密(快速) ↗ SM2加密SM4密钥(患者公钥) ↗ SM2加密SM4密钥(医生公钥) ↗ SM2加密SM4密钥(院方公钥) 数字信封输出: [E_SM2_K_patient, E_SM2_K_doctor, E_SM2_K_hospital, C_SM4] ``

不同角色持有各自的SM2私钥,可以独立解密SM4密钥进而访问病历数据。院方密钥用于审计和合规检查。

2. 金融交易数据保护

银行间数据传输使用数字信封:

  • 交易报文使用SM4-GCM加密(高性能,支持硬件加速)
  • SM4密钥使用收款方SM2公钥加密(确保只有收款方能解密)
  • 同时使用发送方SM2私钥签名(不可否认性)
  • 使用SM3对信封整体做完整性校验

3. 物联网设备固件升级

IoT设备固件升级时:

  • 固件镜像较大(MB级别),不适合直接用SM2加密
  • 使用SM4加密固件镜像(速度快)
  • 使用设备唯一SM2公钥加密SM4密钥(数字信封)
  • 设备收到升级包后,用设备私钥解密SM4密钥,再用SM4密钥解密固件

4. 云存储数据加密

用户在云端存储加密数据时:

  • 数据在客户端使用SM4-GCM加密
  • SM4密钥使用用户SM2公钥加密形成数字信封
  • 云服务商只存储密文和数字信封,无法查看原始数据
  • 用户可随时随地用私钥解密访问

安全性分析

1. 数字信封的攻击面

数字信封的安全性依赖于SM2和SM4两个组件的安全性:

攻击类型影响组件后果防护措施
SM2私钥泄露SM2所有历史通信可被解密密钥定期轮换、HSM保护
SM4密钥泄露SM4单次通信可被解密每次加密生成新密钥
随机数重用SM2C1重用导致私钥泄露使用CSPRNG生成k
选择密文攻击SM2通过恶意密文获取信息使用CCA2安全的SM2加密模式
侧信道攻击SM2/SM4通过功耗/时间推断密钥常量时间实现、掩码防护

2. SM2数字信封的CCA2安全性

标准SM2加密(GM/T 0003.2-2012)在随机预言机模型下具有CCA2(选择密文攻击第2类安全)安全性。但实际实现中需注意:

  • C1C3C2顺序:GM/T 0044.2规定密文顺序为C1||C3||C2,与ISO/IEC 18033-2的C1||C2||C3不同
  • KDF参数:密钥派生函数的输出长度必须等于SM4密钥长度(128位)
  • 哈希验证:解密时应先验证C3(SM3哈希),再解密SM4密钥,防止选择密文攻击

3. 密钥管理安全

数字信封中最脆弱的环节往往是密钥管理而非算法本身:

  • SM2私钥存储:应使用HSM(密码机)或安全 enclave 存储
  • SM4密钥生成:必须使用CSPRNG(如/dev/urandom),不可用伪随机数
  • 密钥销毁:内存中的SM4密钥使用后应立即清零(memset_sSecureZeroMemory`)
  • 密钥备份:SM2私钥不应备份到明文文件或可被访问的存储介质

性能基准

操作SM4-GCMSM2加密数字信封(总)
1KB数据~0.01ms~2ms~2.01ms
10KB数据~0.1ms~2ms~2.1ms
100KB数据~1ms~2ms~3ms
1MB数据~10ms~2ms~12ms
测试环境:Intel Xeon E5-2680 v4, Tongsuo 1.1.1, SM4-GCM模式

关键观察:SM2加密耗时固定(约2ms),与数据量无关;SM4加密耗时与数据量线性相关。对于大数据量,数字信封的性能几乎等同于纯SM4加密,SM2的开销可以忽略不计。

相关实践

参考来源

  • GM/T 0044.2-2014《基于SM2密码算法的证书认证系统密码及其相关安全技术规范 第2部分:数字信封技术规范》
  • GM/T 0003.2-2012《SM2椭圆曲线公钥密码算法 第2部分:数字加密规范》
  • GM/T 0051-2016《SMVP智能密码钥匙密码应用接口规范》
  • GB/T 32907-2016《信息安全技术 SM2椭圆曲线公钥密码算法》
  • NIST SP 800-56A《Recommendation for Pair-Wise Key-Scheme Establishment Using Integer Factorization Cryptography》
  • ISO/IEC 18033-2《Information technology — Security techniques — Encryption algorithms — Part 2: Asymmetric ciphers》