当前位置: 首页 > news >正文

SM2和SM4加密算法详解

SM2和SM4加密算法详解

目录

  • 概述
  • SM2算法详解
  • SM4算法详解
  • Java实现工具
  • 实际应用场景
  • 完整代码示例

概述

SM2和SM4是中国国家密码管理局发布的商用密码算法:

算法类型主要用途密钥特点性能
SM2非对称加密数字签名、身份认证、密钥交换公钥+私钥对较慢
SM4对称加密数据加密解密单一共享密钥较快

SM2算法详解

算法原理

SM2基于椭圆曲线离散对数问题,使用的椭圆曲线方程为:

y² = x³ + ax + b (mod p)

核心参数(256位)

  • 素数p: FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
  • 参数a: FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
  • 参数b: 28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
  • 基点G: 椭圆曲线上的基础点坐标
  • 阶n: FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B61C6823577B

SM2的三个核心操作

1. 密钥生成(Key Generation)

目的:创建一对密钥(公钥和私钥)

比喻:制作一把锁和对应的钥匙

  • 私钥 = 钥匙(保密,只有你知道)
  • 公钥 = 锁(公开,任何人都能用)

执行步骤

// 第一步:初始化密钥生成器
ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();// 第二步:设置生成参数(使用SM2的数学参数)
keyPairGenerator.init(new ECKeyGenerationParameters(SM2_DOMAIN, new SecureRandom()));// 第三步:生成密钥对
AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair();// 第四步:分别获取公钥和私钥
ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate();
ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic();

数学原理

  1. 随机选择私钥:d ∈ [1, n-1]
  2. 计算公钥:P = d × G(椭圆曲线点乘)
2. 数字签名(Digital Signature)

目的:证明文件/消息确实是你发送的,没有被篡改

比喻:在文件上盖你的私人印章

  • 用你的私钥(印章)在消息上"盖章"
  • 生成一个签名(印章印记)

应用场景

  • 银行转账确认
  • 合同签署
  • 软件更新验证

执行步骤

// 假设要给这条消息签名
String message = "转账给张三1000元";
byte[] messageBytes = message.getBytes();// 第一步:创建签名器
SM2Signer signer = new SM2Signer();// 第二步:用私钥初始化签名器(准备盖章)
signer.init(true, privateKey); // true表示签名模式// 第三步:把消息"喂"给签名器
signer.update(messageBytes, 0, messageBytes.length);// 第四步:生成签名(盖章完成)
byte[] signature = signer.generateSignature();

数学原理

  1. 计算消息摘要:e = Hash(M)
  2. 生成随机数k,计算:(x1, y1) = k × G
  3. 计算:r = (e + x1) mod n
  4. 计算:s = (1 + dA)^(-1) × (k - r × dA) mod n
  5. 签名结果:(r, s)
3. 签名验证(Signature Verification)

目的:验证签名是否真实,消息是否被篡改

比喻:检查印章是否真实

  • 用发送者的公钥(公开的印章样本)
  • 验证印章(签名)是否匹配

执行步骤

// 接收方收到了:原消息 + 签名
// 现在要验证签名是否有效// 第一步:创建验证器
SM2Signer verifier = new SM2Signer();// 第二步:用公钥初始化验证器(准备验证印章)
verifier.init(false, publicKey); // false表示验证模式// 第三步:把原消息"喂"给验证器
verifier.update(messageBytes, 0, messageBytes.length);// 第四步:验证签名
boolean isValid = verifier.verifySignature(signature);if (isValid) {System.out.println("签名有效!消息确实是发送者发的,且未被篡改");
} else {System.out.println("签名无效!消息可能被篡改或者不是声称的发送者发的");
}

数学原理

  1. 计算:t = (r + s) mod n
  2. 计算:(x1’, y1’) = s × G + t × PA
  3. 计算:r’ = (e + x1’) mod n
  4. 验证:r’ = r 是否成立

SM4算法详解

算法原理

SM4是分组密码算法,采用Feistel结构:

  • 分组长度:128位(16字节)
  • 密钥长度:128位(16字节)
  • 轮数:32轮迭代

核心组件

  1. S盒(Sbox):8×8的非线性变换表
  2. 线性变换L:位移和异或运算
  3. 轮函数F:T变换 = L变换 ∘ τ变换

SM4的核心操作

1. 密钥生成
// 生成128位(16字节)的随机密钥
byte[] key = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(key);
2. 加密流程

执行步骤

// 第一步:创建SM4引擎
SM4Engine engine = new SM4Engine();
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(engine)
);// 第二步:用密钥初始化加密器
cipher.init(true, new KeyParameter(key)); // true表示加密模式// 第三步:处理明文数据
byte[] plaintext = "机密信息".getBytes();
byte[] output = new byte[cipher.getOutputSize(plaintext.length)];
int len = cipher.processBytes(plaintext, 0, plaintext.length, output, 0);// 第四步:完成加密
cipher.doFinal(output, len);

数学原理

输入:X0, X1, X2, X3 = 明文分组(每个32位)
for i in range(32):Xi+4 = Xi ⊕ T(Xi+1 ⊕ Xi+2 ⊕ Xi+3 ⊕ rki)
输出:密文 = X35, X34, X33, X32
3. 解密流程

执行步骤

// 第一步:创建SM4引擎
SM4Engine engine = new SM4Engine();
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(engine)
);// 第二步:用密钥初始化解密器
cipher.init(false, new KeyParameter(key)); // false表示解密模式// 第三步:处理密文数据
byte[] output = new byte[cipher.getOutputSize(ciphertext.length)];
int len = cipher.processBytes(ciphertext, 0, ciphertext.length, output, 0);// 第四步:完成解密
cipher.doFinal(output, len);

Java实现工具

1. Bouncy Castle(推荐)

Maven依赖

<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.70</version>
</dependency>

特点

  • 功能最完整,支持SM2/SM3/SM4
  • 社区活跃,更新及时
  • 性能中等,稳定可靠

2. 华为鲲鹏BouncyCastle(KAE)

Maven依赖

<dependency><groupId>com.huawei.kae</groupId><artifactId>kae-provider</artifactId><version>1.1.18</version>
</dependency>

特点

  • 硬件加速,性能最优
  • 需要特定硬件支持
  • 适合高性能场景

3. 使用JCA Provider方式

// 注册Provider
Security.addProvider(new BouncyCastleProvider());// 使用标准JCA接口
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "BC");
ECGenParameterSpec ecSpec = new ECGenParameterSpec("sm2p256v1");
keyGen.initialize(ecSpec);
KeyPair keyPair = keyGen.generateKeyPair();// SM4对称加密
Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS5Padding", "BC");
SecretKeySpec keySpec = new SecretKeySpec(key, "SM4");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encrypted = cipher.doFinal(plaintext);

实际应用场景

组合使用策略

在实际应用中,SM2和SM4经常组合使用:

  1. 用SM2传输SM4的密钥(密钥交换)
  2. 用SM4加密大量数据(性能好)
  3. 用SM2对数据进行签名(验证完整性)

典型应用场景

场景SM2的作用SM4的作用
网银转账数字签名确认身份加密传输数据
电子合同合同签名防篡改合同内容加密
文件传输身份验证文件内容加密
API接口请求签名验证敏感数据加密

完整代码示例

SM2完整示例

public class SM2Example {public static void main(String[] args) {try {// === 第一阶段:密钥生成 ===System.out.println("=== SM2密钥生成 ===");AsymmetricCipherKeyPair keyPair = generateSM2KeyPair();ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate();ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic();System.out.println("密钥生成完成");// === 第二阶段:数字签名 ===System.out.println("\n=== 数字签名 ===");String message = "重要合同内容:转账1000万元";byte[] signature = signMessage(message, privateKey);System.out.println("原始消息: " + message);System.out.println("签名完成,签名长度: " + signature.length + " 字节");// === 第三阶段:签名验证 ===System.out.println("\n=== 签名验证 ===");boolean isValid = verifySignature(message, signature, publicKey);System.out.println("签名验证结果: " + (isValid ? "有效" : "无效"));// === 第四阶段:篡改测试 ===System.out.println("\n=== 篡改测试 ===");String tamperedMessage = "重要合同内容:转账1元"; // 故意篡改boolean isTamperedValid = verifySignature(tamperedMessage, signature, publicKey);System.out.println("篡改后验证结果: " + (isTamperedValid ? "有效" : "无效"));} catch (Exception e) {e.printStackTrace();}}// 生成SM2密钥对private static AsymmetricCipherKeyPair generateSM2KeyPair() {ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();keyPairGenerator.init(new ECKeyGenerationParameters(SM2ParameterSpec.INSTANCE, new SecureRandom()));return keyPairGenerator.generateKeyPair();}// SM2数字签名private static byte[] signMessage(String message, ECPrivateKeyParameters privateKey) {try {SM2Signer signer = new SM2Signer();signer.init(true, privateKey);byte[] messageBytes = message.getBytes("UTF-8");signer.update(messageBytes, 0, messageBytes.length);return signer.generateSignature();} catch (Exception e) {throw new RuntimeException("签名失败", e);}}// SM2签名验证private static boolean verifySignature(String message, byte[] signature, ECPublicKeyParameters publicKey) {try {SM2Signer verifier = new SM2Signer();verifier.init(false, publicKey);byte[] messageBytes = message.getBytes("UTF-8");verifier.update(messageBytes, 0, messageBytes.length);return verifier.verifySignature(signature);} catch (Exception e) {throw new RuntimeException("验证失败", e);}}
}

SM4完整示例

public class SM4Example {public static void main(String[] args) {try {// === 第一阶段:密钥生成 ===System.out.println("=== SM4密钥生成 ===");byte[] key = generateSM4Key();System.out.println("SM4密钥生成完成,密钥长度: " + key.length + " 字节");// === 第二阶段:数据加密 ===System.out.println("\n=== 数据加密 ===");String plaintext = "这是需要加密的机密信息:银行账户密码123456";byte[] encrypted = encryptSM4(plaintext.getBytes("UTF-8"), key);System.out.println("原始数据: " + plaintext);System.out.println("加密完成,密文长度: " + encrypted.length + " 字节");System.out.println("密文(Base64): " + Base64.getEncoder().encodeToString(encrypted));// === 第三阶段:数据解密 ===System.out.println("\n=== 数据解密 ===");byte[] decrypted = decryptSM4(encrypted, key);String decryptedText = new String(decrypted, "UTF-8");System.out.println("解密结果: " + decryptedText);System.out.println("解密成功: " + plaintext.equals(decryptedText));} catch (Exception e) {e.printStackTrace();}}// 生成SM4密钥private static byte[] generateSM4Key() {byte[] key = new byte[16]; // SM4使用128位(16字节)密钥new SecureRandom().nextBytes(key);return key;}// SM4加密private static byte[] encryptSM4(byte[] plaintext, byte[] key) {try {SM4Engine engine = new SM4Engine();PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(engine));// 初始化加密器cipher.init(true, new KeyParameter(key));// 执行加密byte[] output = new byte[cipher.getOutputSize(plaintext.length)];int len = cipher.processBytes(plaintext, 0, plaintext.length, output, 0);len += cipher.doFinal(output, len);// 返回实际长度的数组return Arrays.copyOf(output, len);} catch (Exception e) {throw new RuntimeException("SM4加密失败", e);}}// SM4解密private static byte[] decryptSM4(byte[] ciphertext, byte[] key) {try {SM4Engine engine = new SM4Engine();PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(engine));// 初始化解密器cipher.init(false, new KeyParameter(key));// 执行解密byte[] output = new byte[cipher.getOutputSize(ciphertext.length)];int len = cipher.processBytes(ciphertext, 0, ciphertext.length, output, 0);len += cipher.doFinal(output, len);// 返回实际长度的数组return Arrays.copyOf(output, len);} catch (Exception e) {throw new RuntimeException("SM4解密失败", e);}}
}

组合使用示例

public class CombinedSM2SM4Example {public static void main(String[] args) {try {System.out.println("=== SM2+SM4组合使用示例 ===");// 1. 生成SM2密钥对(用于密钥交换和签名)AsymmetricCipherKeyPair senderKeyPair = generateSM2KeyPair();AsymmetricCipherKeyPair receiverKeyPair = generateSM2KeyPair();// 2. 生成SM4密钥(用于数据加密)byte[] sm4Key = generateSM4Key();// 3. 准备要传输的大量数据String largeData = "这是一份包含大量机密信息的文档..." + "在实际应用中,这可能是几MB甚至几GB的数据";// === 发送方操作 ===System.out.println("\n=== 发送方操作 ===");// 4. 用接收方公钥加密SM4密钥byte[] encryptedSM4Key = sm2Encrypt(sm4Key, (ECPublicKeyParameters) receiverKeyPair.getPublic());System.out.println("SM4密钥已用SM2加密");// 5. 用SM4加密大量数据byte[] encryptedData = encryptSM4(largeData.getBytes("UTF-8"), sm4Key);System.out.println("大量数据已用SM4加密");// 6. 用发送方私钥对加密数据进行签名byte[] signature = signMessage(Base64.getEncoder().encodeToString(encryptedData),(ECPrivateKeyParameters) senderKeyPair.getPrivate());System.out.println("数据已签名");// === 模拟网络传输 ===System.out.println("\n=== 网络传输 ===");System.out.println("传输内容:加密的SM4密钥 + 加密的数据 + 数字签名");// === 接收方操作 ===System.out.println("\n=== 接收方操作 ===");// 7. 用接收方私钥解密SM4密钥byte[] decryptedSM4Key = sm2Decrypt(encryptedSM4Key,(ECPrivateKeyParameters) receiverKeyPair.getPrivate());System.out.println("SM4密钥解密成功");// 8. 验证数字签名boolean signatureValid = verifySignature(Base64.getEncoder().encodeToString(encryptedData),signature,(ECPublicKeyParameters) senderKeyPair.getPublic());System.out.println("签名验证结果: " + (signatureValid ? "有效" : "无效"));if (signatureValid) {// 9. 用解密的SM4密钥解密数据byte[] decryptedData = decryptSM4(encryptedData, decryptedSM4Key);String finalResult = new String(decryptedData, "UTF-8");System.out.println("数据解密成功!");System.out.println("原始数据: " + largeData);System.out.println("解密数据: " + finalResult);System.out.println("数据完整性: " + largeData.equals(finalResult));} else {System.out.println("签名验证失败,数据可能被篡改!");}} catch (Exception e) {e.printStackTrace();}}// SM2加密(用于加密SM4密钥)private static byte[] sm2Encrypt(byte[] data, ECPublicKeyParameters publicKey) {// 实现SM2加密逻辑// 这里简化处理,实际需要完整的SM2加密实现return data; // 简化返回}// SM2解密private static byte[] sm2Decrypt(byte[] encryptedData, ECPrivateKeyParameters privateKey) {// 实现SM2解密逻辑// 这里简化处理,实际需要完整的SM2解密实现return encryptedData; // 简化返回}// 其他方法同前面示例...
}

安全使用建议

密钥管理

  1. 私钥保护:私钥必须安全存储,建议使用硬件安全模块(HSM)
  2. 密钥轮换:定期更换密钥,特别是SM4的对称密钥
  3. 密钥分发:SM4密钥分发要通过安全通道(如SM2加密)

实现建议

  1. 使用成熟库:推荐使用Bouncy Castle等经过验证的库
  2. 随机数质量:确保使用密码学安全的随机数生成器
  3. 填充模式:SM4建议使用CBC或GCM模式,避免ECB模式
  4. 错误处理:不要泄露加密过程中的错误信息

性能优化

  1. 算法选择:大数据用SM4,签名认证用SM2
  2. 硬件加速:在支持的环境中使用硬件加速版本
  3. 缓存机制:合理缓存密钥对,避免重复生成

总结

SM2和SM4算法是国产密码算法的重要组成部分:

  • SM2:非对称算法,主要用于数字签名、身份认证和密钥交换
  • SM4:对称算法,主要用于数据的快速加密解密
  • 组合使用:在实际应用中,两者通常配合使用,发挥各自优势
  • Java实现:Bouncy Castle提供了完整可靠的实现

掌握这两个算法的原理和使用方法,对于开发安全可靠的密码系统具有重要意义。

http://www.lryc.cn/news/618703.html

相关文章:

  • 防火墙快速管理软件,66K超小巧
  • 【网络运维】Linux和自动化:Ansible
  • WEB虚拟主机3种部署方式全解析
  • Linux软件编程(三)文件操作-文件 I/O
  • Outstanding和Credit的概念详解
  • 动态路由协议(一)
  • 《Redis日志系统操作:LIST结构实现日志收集与查询》
  • 在线免VIP的动漫网站
  • 机器学习-集成学习(EnsembleLearning)
  • GitHub的简单使用方法----(4)
  • 为什么灰度图用G(绿色)通道?
  • CSRF 攻击
  • 记对外国某服务器的内网渗透
  • 解释 Spring MVC 的工作原理
  • Linux中使用计划任务和tar命令实现文件备份
  • 模拟人脑处理文本——从段落到时间线叙事,再到动画
  • 【PCB设计经验】去耦电容如何布局?
  • C++ 学习与 CLion 使用:(二)using namespace std 语句详解,以及 std 空间的标识符罗列
  • 【python实用小脚本-182】Python一键爬取今日新闻:5分钟生成Word+CSV——再也不用复制粘贴
  • 【web站点安全开发】任务2:HTML5核心特性与元素详解
  • 02-Ansible 基本使用
  • Python day42
  • 【运维进阶】Ansible 自动化
  • [激光原理与应用-250]:理论 - 几何光学 - 透镜成像的优缺点,以及如克服缺点
  • TensorBoard的使用 小土堆pytorch记录
  • centos 怎么部署 vscode 网页版
  • 半精度模型(16位)解析
  • TRO风暴预警GoPro携BSF律所重拳打击跨境侵权
  • QT6 如何在Linux Wayland 桌面系统抓屏和分享屏幕
  • 使用Excel制作甘特图