SM3 密码杂凑算法
2025/11/23大约 10 分钟国密算法哈希算法SM3哈希摘要算法消息认证
SM3 密码杂凑算法
提示
提示:本章要点
- 概述
- 快速开始
- 输出格式
- 流式处理
- 面向对象 API
概述
SM3 是国密哈希算法,输出 256 位摘要,用于完整性校验与数字指纹。
它常用于签名预处理、文件指纹、接口验签等场景,与 SHA-256 安全强度相近,但算法不兼容。
参考标准
| 项目 | 说明 |
|---|---|
| GM/T 0004-2012 | SM3 密码杂凑算法 |
| ISO/IEC 10118-3:2018 | 国际标准收录 |
商密场景中的 SM3
| 项目 | 说明 |
|---|---|
| 合规诉求 | 在涉密或国密合规系统里,SM3 往往是默认哈希选择 |
| 业务习惯 | 签名验签前先做摘要(SM2 内部已自动处理) |
| 接口协定 | 跨系统对接时,明确输出编码(hex/base64) |
使用要点
| 项目 | 说明 |
|---|---|
| 输出长度 | 固定 256 位(32 字节) |
| 输入类型 | string 或 Uint8Array |
| 输出格式 | hex(默认)/ base64 |
| 流式处理 | 适合大文件分块更新 |
提示
提示:性能提示
SM3 与 SHA-256 性能接近,实际速度取决于平台硬件与运行环境。
合规场景优先 SM3,其余场景可按平台特性选择。
快速开始
基本用法
import { sm3Digest } from 'gmkitx';
// 输入支持 string 或 Uint8Array,默认输出十六进制(64 字符)
const hash = sm3Digest('Hello, SM3!');
// 处理二进制数据:自行构造 Uint8Array
const bytesHash = sm3Digest(new TextEncoder().encode('binary-data'));
// 结构化数据需自行序列化
const objHash = sm3Digest(JSON.stringify({ name: '张三', age: 30 }));使用命名空间
import { sm3 } from 'gmkitx';
const hash = sm3.digest('Hello, SM3!');输出格式
SM3 支持多种输出格式:
十六进制输出(默认)
import { sm3Digest, OutputFormat } from 'gmkitx';
const hash = sm3Digest('Hello', {
outputFormat: OutputFormat.HEX
});
// 输出: "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0"Base64 输出
const hash = sm3Digest('Hello', {
outputFormat: OutputFormat.BASE64
});
// 输出: "Zsfw9GLu7dnR8tRr3BDk4kFnxIdc8veiKX2gK49LqOA="如需字节数组,可自行从 hex/base64 转换:
const hexHash = sm3Digest('Hello');
const bytes = Buffer.from(hexHash, 'hex'); // Node.js流式处理
对于大文件或需要分块处理的场景,可以使用流式 API:
基本流式处理
import { SM3 } from 'gmkitx';
const sm3 = new SM3();
// 分块更新数据
sm3.update('第一部分数据');
sm3.update('第二部分数据');
sm3.update('第三部分数据');
// 获取最终哈希值
const hash = sm3.digest();处理大文件
import { SM3 } from 'gmkitx';
import fs from 'fs';
const sm3 = new SM3();
const stream = fs.createReadStream('large-file.dat');
stream.on('data', (chunk) => {
sm3.update(chunk);
});
stream.on('end', () => {
const hash = sm3.digest();
console.log('文件哈希:', hash);
});Node.js 文件哈希示例
import { SM3 } from 'gmkitx';
import { readFileSync } from 'fs';
function hashFile(filePath: string): string {
const data = readFileSync(filePath);
return sm3Digest(data);
}
const fileHash = hashFile('./document.pdf');面向对象 API
import { SM3, OutputFormat } from 'gmkitx';
// 创建实例(构造时可指定输出格式)
const sm3 = new SM3(OutputFormat.HEX);
// 更新数据(可多次调用)
sm3.update('part1');
sm3.update('part2');
// 获取哈希值(十六进制)
const hexHash = sm3.digest();
// 修改输出格式后再 digest
sm3.setOutputFormat(OutputFormat.BASE64);
sm3.update('new data');
const base64Hash = sm3.digest();
// 重置状态,可以重新使用
sm3.reset();
sm3.update('other data');
const newHash = sm3.digest();完整 API 参考
函数式 API
| 函数 | 说明 | 返回值 |
|---|---|---|
digest(data, options?) | 计算 SM3 哈希值(主函数) | string |
sm3Digest(data, options?) | 计算 SM3 哈希值(别名) | string |
hmac(key, data, options?) | 计算 HMAC-SM3 | string |
类 API
| 方法 | 说明 | 返回值 |
|---|---|---|
new SM3() | 创建 SM3 实例 | SM3 |
update(data) | 更新数据 | void |
digest() | 获取哈希值 | string |
reset() | 重置状态 | void |
SM3.hmac(key, data, options?) | 计算 HMAC-SM3(静态方法) | string |
选项参数
interface DigestOptions {
outputFormat?: 'hex' | 'base64'; // 输出格式
}使用场景
1. 数据完整性校验
import { sm3Digest } from 'gmkitx';
// 发送方计算哈希
const data = '重要数据';
const hash = sm3Digest(data);
sendData(data, hash);
// 接收方验证
const receivedData = receiveData();
const receivedHash = receiveHash();
const calculatedHash = sm3Digest(receivedData);
if (calculatedHash === receivedHash) {
console.log('数据完整,未被篡改');
} else {
console.log('数据已被篡改!');
}2. 密码存储
import { sm3Digest } from 'gmkitx';
// 存储密码时先哈希(示例;生产请使用带迭代的 KDF)
function hashPassword(password: string, salt: string): string {
return sm3Digest(password + salt);
}
// 注册
const salt = generateRandomSalt();
const hashedPassword = hashPassword('userPassword', salt);
saveToDatabase(username, hashedPassword, salt);
// 登录验证
const storedHash = getFromDatabase(username);
const storedSalt = getSaltFromDatabase(username);
const inputHash = hashPassword(inputPassword, storedSalt);
if (inputHash === storedHash) {
console.log('密码正确');
}3. 数字签名的消息摘要
import { sign } from 'gmkitx';
// 注意:SM2 签名默认会计算 SM3(Z || M),一般无需手动预哈希
const message = '合同内容...';
const signature = sign(privateKey, message);4. 区块链哈希
import { sm3Digest } from 'gmkitx';
interface Block {
index: number;
timestamp: number;
data: string;
previousHash: string;
nonce: number;
}
function calculateBlockHash(block: Block): string {
const blockString = JSON.stringify(block);
return sm3Digest(blockString);
}
function mineBlock(block: Block, difficulty: number): string {
const target = '0'.repeat(difficulty);
while (true) {
block.nonce++;
const hash = calculateBlockHash(block);
if (hash.startsWith(target)) {
return hash;
}
}
}5. 内容去重
import { sm3Digest } from 'gmkitx';
const contentHashMap = new Map<string, string>();
function isDuplicate(content: string): boolean {
const hash = sm3Digest(content);
if (contentHashMap.has(hash)) {
return true; // 重复内容
}
contentHashMap.set(hash, content);
return false;
}高级用法
HMAC-SM3(密钥哈希)
GMKitX 已内置 HMAC-SM3:
import { hmac, OutputFormat } from 'gmkitx';
const mac = hmac('secret-key', 'message');
const mac64 = hmac('secret-key', 'message', { outputFormat: OutputFormat.BASE64 });计算文件指纹
import { SM3 } from 'gmkitx';
interface FileFingerprint {
hash: string;
size: number;
chunkCount: number;
}
function calculateFileFingerprint(
fileData: Uint8Array,
chunkSize: number = 1024 * 1024 // 1MB
): FileFingerprint {
const sm3 = new SM3();
let chunkCount = 0;
for (let i = 0; i < fileData.length; i += chunkSize) {
const chunk = fileData.slice(i, i + chunkSize);
sm3.update(chunk);
chunkCount++;
}
return {
hash: sm3.digest(),
size: fileData.length,
chunkCount
};
}批量哈希计算
import { sm3Digest } from 'gmkitx';
// 并行计算多个哈希值
function batchHash(items: string[]): Map<string, string> {
const results = new Map<string, string>();
for (const item of items) {
results.set(item, sm3Digest(item));
}
return results;
}
// 使用
const items = ['item1', 'item2', 'item3'];
const hashes = batchHash(items);性能优化
1. 重用实例
import { SM3 } from 'gmkitx';
// 创建实例并重用
const sm3 = new SM3();
function hashMultipleMessages(messages: string[]): string[] {
return messages.map(msg => {
sm3.reset(); // 重置状态
sm3.update(msg);
return sm3.digest();
});
}2. 流式处理大数据
// ✅ 推荐:流式处理
const sm3 = new SM3();
for (const chunk of largeData) {
sm3.update(chunk);
}
const hash = sm3.digest();
// ❌ 不推荐:一次性处理
const hash = sm3Digest(largeDataAsString); // 可能导致内存问题注意事项
注意
注意:以下内容涉及安全性、互操作或易错点,建议上线前逐条核对。
| 项目 | 说明 |
|---|---|
| 哈希不可逆 | SM3 是单向函数,无法从哈希值还原原始数据 |
| 雪崩效应 | 输入微小变化会导致输出完全不同 |
| 固定长度 | 无论输入多大,输出始终是 256 位(32 字节) |
| 编码一致性 | 确保输入数据编码一致(UTF-8) |
| 不要用于加密 | SM3 是哈希算法,不是加密算法 |
| 盐值 | 存储密码时务必加盐(salt) |
| 密码存储 | 仅哈希不够,建议使用 PBKDF2/BCrypt 等加盐与迭代的 KDF |
常见问题
Q: SM3 和 SHA-256 有什么区别?
A: SM3 和 SHA-256 都输出 256 位哈希值,但:
- SM3 是中国国家标准
- SM3 的内部结构和设计不同
- 两者不兼容,对相同输入产生不同的哈希值
- 安全强度相当
Q: 如何验证 SM3 实现的正确性?
A: 可以使用官方测试向量验证:
// GM/T 0004-2012 标准测试向量
const testVector = 'abc';
const expectedHash = '66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0';
const actualHash = sm3Digest(testVector);
console.log(actualHash === expectedHash); // trueQ: 可以用 SM3 加密数据吗?
A: 不可以。SM3 是哈希算法,不是加密算法。哈希是单向的,无法解密。如需加密,请使用 SM4 或 SM2。
Q: 如何处理二进制数据?
A: SM3 支持多种输入格式:
// 字符串
sm3Digest('text');
// 字节数组
sm3Digest(new Uint8Array([1, 2, 3]));
// Buffer (Node.js)
sm3Digest(Buffer.from('data'));性能基准
在现代硬件上的性能参考(仅供参考):
| 数据大小 | 处理时间 |
|---|---|
| 1 KB | < 1 ms |
| 1 MB | ~10 ms |
| 10 MB | ~100 ms |
| 100 MB | ~1 s |
注: 实际性能取决于硬件配置和运行环境