介绍
SM4 加密模式
SM4 是中国国家密码管理局发布的对称加密算法,主要用于保护数据的机密性。在加密过程中,SM4 可以采用不同的工作模式,每种模式都有不同的用途和特性。常见的加密模式包括:
电子密码本模式(ECB, Electronic Codebook):
- 作用: 将明文分成块,每块单独加密。
- 优点: 简单易实现。
- 缺点: 相同的明文块会产生相同的密文块,这会暴露一些模式信息,降低安全性。
- 用途和应用场景: 通常不推荐用于高安全性需求的场合。
密文分组链接模式(CBC, Cipher Block Chaining):
- 作用: 每个明文块在加密之前与前一个密文块进行异或操作。第一个块与一个初始化向量(IV)异或。
- 优点: 增强了安全性,因为相同的明文块会由于前一个块的加密结果而有所不同。
- 用途和应用场景: 适合大多数加密需求,特别是需要抵抗模式分析的场合。
计数器模式(CTR, Counter Mode):
- 作用: 将明文与加密后的计数器值进行异或。计数器值会随着每个块增加。
- 优点: 可以并行处理,提高加密和解密速度。
- 用途和应用场景: 适用于高性能要求的环境,例如流媒体传输和大数据处理。
输出反馈模式(OFB, Output Feedback):
- 作用: 使用加密算法的输出作为后续块的输入。
- 优点: 可以将加密过程转换为流加密,适合数据流加密。
- 用途和应用场景: 适用于需要加密流数据的场合,如网络传输中的数据加密。
加密反馈模式(CFB, Cipher Feedback):
- 作用: 将加密输出的一部分作为反馈用于加密明文的下一部分。
- 优点: 类似于流加密,能够处理任意长度的数据。
- 用途和应用场景: 适用于实时数据流加密和响应时间要求高的系统。
IV(初始化向量)
IV 是“Initialization Vector”的缩写,它是一种随机数或伪随机数,用于增强加密算法的安全性。
作用:
- 在加密过程中,用于确保即使相同的明文块和相同的密钥在不同的加密会话中也产生不同的密文。
- 提供额外的随机性,以避免加密模式产生的模式性攻击。
区别:
- 与密钥的不同: IV 是用于加密过程中的随机化因素,而密钥是加密和解密的核心。
- 与加密模式的不同: IV 是许多加密模式(如 CBC 和 CFB)所需要的,但不是所有模式都使用 IV。
用途和应用场景:
- 广泛应用于块加密模式(如 CBC、CFB)中。
- IV 应该在每次加密操作中都重新生成,确保安全性。
- 常见于安全传输协议(如 SSL/TLS)和存储加密中。
Padding(填充)
Padding 是在加密过程中对明文数据进行填充的一种方式,以确保数据的长度符合块加密算法的要求。
作用:
- 填充操作使得明文数据的长度满足加密算法要求的块大小。
- 防止数据被截断或被篡改,提高加密的安全性。
PKCS5 和 PKCS7:
- PKCS5:
- 只对 64 位块大小的加密算法(如 DES)有效。
- 填充内容包括填充字节的数量,填充字节的值与填充的字节数相同。
- PKCS7:
- 扩展了 PKCS5,可以用于任何块大小的加密算法(如 AES 的 128、192、256 位块)。
- 填充字节的值也是填充的字节数。
- 区别: PKCS7 比 PKCS5 更为通用,因为它支持多种块大小。
具体用途和应用场景:
- 填充在块加密模式(如 CBC、ECB)中非常重要,用于确保明文数据的完整性。
- PKCS7 是现代应用中更常用的填充方式,广泛应用于各种加密标准和协议。
使用
官方: https://github.com/JuneAndGreen/sm-crypto
npm
安装
1
| npm install --save sm-crypto
|
加密
1 2 3 4 5 6 7 8
| const sm4 = require('sm-crypto').sm4 const msg = 'hello world! 我是 juneandgreen.' const key = '0123456789abcdeffedcba9876543210'
let encryptData = sm4.encrypt(msg, key) let encryptData = sm4.encrypt(msg, key, {padding: 'none'}) let encryptData = sm4.encrypt(msg, key, {padding: 'none', output: 'array'}) let encryptData = sm4.encrypt(msg, key, {mode: 'cbc', iv: 'fedcba98765432100123456789abcdef'})
|
解密
1 2 3 4 5 6 7 8
| const sm4 = require('sm-crypto').sm4 const encryptData = '0e395deb10f6e8a17e17823e1fd9bd98a1bff1df508b5b8a1efb79ec633d1bb129432ac1b74972dbe97bab04f024e89c' const key = '0123456789abcdeffedcba9876543210'
let decryptData = sm4.decrypt(encryptData, key) let decryptData = sm4.decrypt(encryptData, key, {padding: 'none'}) let decryptData = sm4.decrypt(encryptData, key, {padding: 'none', output: 'array'}) let decryptData = sm4.decrypt(encryptData, key, {mode: 'cbc', iv: 'fedcba98765432100123456789abcdef'})
|
apifox
完全版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| fox.liveRequire("sm-crypto", (smCrypto) => { const sm4 = smCrypto.sm4;
if (!sm4) { console.error("sm-crypto 库加载失败"); return; }
const keyHex = '854f38e8cb3f46665ad3b93ecfcc679d'; const ivHex = 'fedcba98765432100123456789abcdef';
console.log("密钥 (Hex):", keyHex); console.log("IV (Hex):", ivHex);
const options = { mode: 'cbc', iv: ivHex
};
const requestData = "Hello!";
console.log("原始请求数据:", requestData);
try { console.log("开始加密:", requestData);
const encryptedData = sm4.encrypt(requestData, keyHex, options); console.log("encryptedData", encryptedData); if (!encryptedData || encryptedData.length === 0) { console.error("加密失败,数据为空"); return; }
console.log("加密后的数据:", encryptedData);
const decryptedDataHex = sm4.decrypt(encryptedData, keyHex, options); console.log("解密后的数据:", decryptedDataHex);
} catch (error) { console.error("加密/解密过程中出现错误:", error); } });
|
前置操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| fox.liveRequire("sm-crypto", (smCrypto) => { const sm4 = smCrypto.sm4;
if (!sm4) { console.error("sm-crypto 库加载失败"); return; } const keyHex = pm.environment.get('sm4key'); const ivHex = pm.environment.get('ivHex');
const options = { mode: 'cbc', iv: ivHex };
const requestData = pm.request.body.raw; if (!requestData) { console.log("原始请求数据为空,忽略..."); return; }
try { console.log("开始加密:", requestData); const encryptedData = sm4.encrypt(requestData, keyHex, options); if (!encryptedData || encryptedData.length === 0) { console.error("加密失败,数据为空"); return; }
const dataPackage = { data: encryptedData, }; console.log("加密后的数据包", dataPackage); pm.request.body.update(JSON.stringify(dataPackage));
} catch (error) { console.error("加密过程中出现错误:", error); } });
|
后置操作
java
1 2 3 4 5
| <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.31</version> </dependency>
|
// 和前端sm-crypto
对接这里采用 encryptHex
方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.HexUtil; import cn.hutool.crypto.Mode; import cn.hutool.crypto.Padding; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.symmetric.SM4; import lombok.extern.slf4j.Slf4j;
import javax.crypto.SecretKey;
@Slf4j public class Sm4Utils {
public static String generateSm4Key() { SecretKey secretKey = SecureUtil.generateKey("SM4", 128); return HexUtil.encodeHexStr(secretKey.getEncoded()); }
public static String encrypt(String keyHex, String ivHex, String content) { byte[] key = HexUtil.decodeHex(keyHex); byte[] iv = HexUtil.decodeHex(ivHex); SM4 sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, key, iv); return sm4.encryptHex(content, CharsetUtil.CHARSET_UTF_8); }
public static String decrypt(String keyHex, String ivHex, String content) { byte[] key = HexUtil.decodeHex(keyHex); byte[] iv = HexUtil.decodeHex(ivHex); SM4 sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, key, iv); return sm4.decryptStr(content, CharsetUtil.CHARSET_UTF_8); }
public static void main(String[] args) { String plainText = "Hello, Hutool!"; String keyHex = "854f38e8cb3f46665ad3b93ecfcc679d"; String ivHex = "fedcba98765432100123456789abcdef"; String encrypt = encrypt(keyHex, ivHex, plainText); System.out.println(encrypt); System.out.println(decrypt(keyHex, ivHex, encrypt)); }
}
|