Compare commits
2
Commits
a7aef0f7dd
...
0ef0a78c38
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ef0a78c38 | ||
|
|
9be06035f4 |
Generated
+16
@@ -9,6 +9,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"gm-crypto": "*",
|
"gm-crypto": "*",
|
||||||
|
"gmsm-sm3js": "^0.2.0",
|
||||||
"gmsm-sm4js": "^0.7.0",
|
"gmsm-sm4js": "^0.7.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
@@ -4281,6 +4282,15 @@
|
|||||||
"to-arraybuffer": "^1.0.1"
|
"to-arraybuffer": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/gmsm-sm3js": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/gmsm-sm3js/-/gmsm-sm3js-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-z4SZH+xoQtYSIp22qwwZ0Z45Fajaxav+Y2OGlEoAVm2tK+o0fOQHowPeh91FpCCH/+p0jNiKL2G9C4T9EEQJWA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"long": "^5.2.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/gmsm-sm4js": {
|
"node_modules/gmsm-sm4js": {
|
||||||
"version": "0.7.0",
|
"version": "0.7.0",
|
||||||
"resolved": "https://registry.npmmirror.com/gmsm-sm4js/-/gmsm-sm4js-0.7.0.tgz",
|
"resolved": "https://registry.npmmirror.com/gmsm-sm4js/-/gmsm-sm4js-0.7.0.tgz",
|
||||||
@@ -5467,6 +5477,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/long": {
|
||||||
|
"version": "5.3.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/long/-/long-5.3.2.tgz",
|
||||||
|
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/loose-envify": {
|
"node_modules/loose-envify": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
|
|||||||
+3
-1
@@ -8,10 +8,12 @@
|
|||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"test": "jest"
|
"test": "jest",
|
||||||
|
"public": "tsc && vite build && surge dist seckit.surge.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"gm-crypto": "*",
|
"gm-crypto": "*",
|
||||||
|
"gmsm-sm3js": "^0.2.0",
|
||||||
"gmsm-sm4js": "^0.7.0",
|
"gmsm-sm4js": "^0.7.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|||||||
+39
-178
@@ -1,24 +1,19 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { generateSm2KeyPair, sm2Encrypt, sm2Decrypt } from './utils/algorithm/sm2'
|
|
||||||
import { sm3Digest } from './utils/algorithm/sm3'
|
import { sm3Digest, sm3Hmac } from './utils/algorithm/sm3'
|
||||||
import { sm4Encrypt, sm4Decrypt } from './utils/algorithm/sm4'
|
import { sm4Encrypt, sm4Decrypt } from './utils/algorithm/sm4'
|
||||||
import { hexToUtf8, utf8ToHex } from './utils/convert'
|
import { hexToUtf8, utf8ToHex } from './utils/convert'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [activeTab, setActiveTab] = useState('sm2')
|
const [activeTab, setActiveTab] = useState('sm3')
|
||||||
|
|
||||||
|
|
||||||
// SM2 状态
|
|
||||||
const [sm2PrivateKey, setSm2PrivateKey] = useState('')
|
|
||||||
const [sm2PublicKey, setSm2PublicKey] = useState('')
|
|
||||||
const [sm2Message, setSm2Message] = useState('')
|
|
||||||
const [sm2Signature, setSm2Signature] = useState('')
|
|
||||||
const [sm2Encrypted, setSm2Encrypted] = useState('')
|
|
||||||
const [sm2Decrypted, setSm2Decrypted] = useState('')
|
|
||||||
|
|
||||||
// SM3 状态
|
// SM3 状态
|
||||||
const [sm3Message, setSm3Message] = useState('')
|
const [sm3Message, setSm3Message] = useState('')
|
||||||
const [sm3Result, setSm3Result] = useState('')
|
const [sm3Result, setSm3Result] = useState('')
|
||||||
const [sm3MacKey, setSm3MacKey] = useState('')
|
const [sm3HmacKey, setSm3HmacKey] = useState('')
|
||||||
|
const [sm3HmacResult, setSm3HmacResult] = useState('')
|
||||||
|
|
||||||
// SM4 状态
|
// SM4 状态
|
||||||
const [sm4Key, setSm4Key] = useState('')
|
const [sm4Key, setSm4Key] = useState('')
|
||||||
@@ -33,66 +28,7 @@ function App() {
|
|||||||
const [hexInput, setHexInput] = useState('')
|
const [hexInput, setHexInput] = useState('')
|
||||||
const [utf8Input, setUtf8Input] = useState('')
|
const [utf8Input, setUtf8Input] = useState('')
|
||||||
|
|
||||||
// SM2 生成密钥对
|
|
||||||
const handleGenerateSm2KeyPair = () => {
|
|
||||||
try {
|
|
||||||
const { privateKey, publicKey } = generateSm2KeyPair()
|
|
||||||
setSm2PrivateKey(privateKey)
|
|
||||||
setSm2PublicKey(publicKey)
|
|
||||||
} catch (error) {
|
|
||||||
alert('生成密钥对失败: ' + error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SM2 从私钥计算公钥
|
|
||||||
const calculateSm2PublicKey = () => {
|
|
||||||
try {
|
|
||||||
// 简化处理,实际项目中可能需要使用正确的 API
|
|
||||||
alert('从私钥计算公钥功能需要正确的 API 支持')
|
|
||||||
} catch (error) {
|
|
||||||
alert('计算公钥失败: ' + error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SM2 签名
|
|
||||||
const signSm2 = () => {
|
|
||||||
try {
|
|
||||||
// 简化处理,实际项目中可能需要使用正确的 API
|
|
||||||
alert('签名功能需要正确的 API 支持')
|
|
||||||
} catch (error) {
|
|
||||||
alert('签名失败: ' + error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SM2 验签
|
|
||||||
const verifySm2 = () => {
|
|
||||||
try {
|
|
||||||
// 简化处理,实际项目中可能需要使用正确的 API
|
|
||||||
alert('验签功能需要正确的 API 支持')
|
|
||||||
} catch (error) {
|
|
||||||
alert('验签失败: ' + error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SM2 公钥加密
|
|
||||||
const handleEncryptSm2 = () => {
|
|
||||||
try {
|
|
||||||
const encrypted = sm2Encrypt(sm2Message, sm2PublicKey)
|
|
||||||
setSm2Encrypted(encrypted)
|
|
||||||
} catch (error) {
|
|
||||||
alert('加密失败: ' + (error instanceof Error ? error.message : error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SM2 私钥解密
|
|
||||||
const handleDecryptSm2 = () => {
|
|
||||||
try {
|
|
||||||
const decrypted = sm2Decrypt(sm2Encrypted, sm2PrivateKey)
|
|
||||||
setSm2Decrypted(decrypted)
|
|
||||||
} catch (error) {
|
|
||||||
alert('解密失败: ' + (error instanceof Error ? error.message : error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SM3 计算
|
// SM3 计算
|
||||||
const handleCalculateSm3 = () => {
|
const handleCalculateSm3 = () => {
|
||||||
@@ -104,13 +40,13 @@ function App() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SM3 MAC 计算
|
// SM3 HMAC 计算
|
||||||
const calculateSm3Mac = () => {
|
const calculateSm3Hmac = () => {
|
||||||
try {
|
try {
|
||||||
// 简化处理,实际项目中可能需要使用正确的 API
|
const result = sm3Hmac(sm3HmacKey, sm3Message)
|
||||||
alert('MAC 计算功能需要正确的 API 支持')
|
setSm3HmacResult(result)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert('MAC 计算失败: ' + error)
|
alert('HMAC 计算失败: ' + (error instanceof Error ? error.message : error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,12 +95,6 @@ function App() {
|
|||||||
<h1>安全算法计算工具</h1>
|
<h1>安全算法计算工具</h1>
|
||||||
|
|
||||||
<div className="tabs">
|
<div className="tabs">
|
||||||
<button
|
|
||||||
className={`tab ${activeTab === 'sm2' ? 'active' : ''}`}
|
|
||||||
onClick={() => setActiveTab('sm2')}
|
|
||||||
>
|
|
||||||
SM2
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
className={`tab ${activeTab === 'sm3' ? 'active' : ''}`}
|
className={`tab ${activeTab === 'sm3' ? 'active' : ''}`}
|
||||||
onClick={() => setActiveTab('sm3')}
|
onClick={() => setActiveTab('sm3')}
|
||||||
@@ -185,81 +115,7 @@ function App() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* SM2 面板 */}
|
|
||||||
{activeTab === 'sm2' && (
|
|
||||||
<div className="panel">
|
|
||||||
<h2>SM2 算法</h2>
|
|
||||||
|
|
||||||
<div className="form-group">
|
|
||||||
<label>私钥</label>
|
|
||||||
<textarea
|
|
||||||
value={sm2PrivateKey}
|
|
||||||
onChange={(e) => setSm2PrivateKey(e.target.value)}
|
|
||||||
placeholder="输入 SM2 私钥 (hex)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group">
|
|
||||||
<label>公钥</label>
|
|
||||||
<textarea
|
|
||||||
value={sm2PublicKey}
|
|
||||||
onChange={(e) => setSm2PublicKey(e.target.value)}
|
|
||||||
placeholder="输入 SM2 公钥 (hex)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="button-group">
|
|
||||||
<button onClick={handleGenerateSm2KeyPair}>生成密钥对</button>
|
|
||||||
<button onClick={calculateSm2PublicKey}>从私钥计算公钥</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group">
|
|
||||||
<label>消息</label>
|
|
||||||
<textarea
|
|
||||||
value={sm2Message}
|
|
||||||
onChange={(e) => setSm2Message(e.target.value)}
|
|
||||||
placeholder="输入要签名或加密的消息"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group">
|
|
||||||
<label>签名</label>
|
|
||||||
<textarea
|
|
||||||
value={sm2Signature}
|
|
||||||
onChange={(e) => setSm2Signature(e.target.value)}
|
|
||||||
placeholder="输入签名结果"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="button-group">
|
|
||||||
<button onClick={signSm2}>签名</button>
|
|
||||||
<button onClick={verifySm2}>验签</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group">
|
|
||||||
<label>加密结果</label>
|
|
||||||
<textarea
|
|
||||||
value={sm2Encrypted}
|
|
||||||
onChange={(e) => setSm2Encrypted(e.target.value)}
|
|
||||||
placeholder="输入加密结果"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group">
|
|
||||||
<label>解密结果</label>
|
|
||||||
<textarea
|
|
||||||
value={sm2Decrypted}
|
|
||||||
onChange={(e) => setSm2Decrypted(e.target.value)}
|
|
||||||
placeholder="解密结果将显示在这里"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="button-group">
|
|
||||||
<button onClick={handleEncryptSm2}>公钥加密</button>
|
|
||||||
<button onClick={handleDecryptSm2}>私钥解密</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* SM3 面板 */}
|
{/* SM3 面板 */}
|
||||||
{activeTab === 'sm3' && (
|
{activeTab === 'sm3' && (
|
||||||
@@ -287,24 +143,25 @@ function App() {
|
|||||||
<button onClick={handleCalculateSm3}>计算 SM3</button>
|
<button onClick={handleCalculateSm3}>计算 SM3</button>
|
||||||
|
|
||||||
<div className="form-group" style={{ marginTop: '1.5rem' }}>
|
<div className="form-group" style={{ marginTop: '1.5rem' }}>
|
||||||
<label>MAC 密钥</label>
|
<label>HMAC 密钥</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={sm3MacKey}
|
value={sm3HmacKey}
|
||||||
onChange={(e) => setSm3MacKey(e.target.value)}
|
onChange={(e) => setSm3HmacKey(e.target.value)}
|
||||||
placeholder="输入 MAC 密钥 (hex)"
|
placeholder="输入 HMAC 密钥 (hex)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>SM3 MAC 结果</label>
|
<label>SM3 HMAC 结果</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
value={sm3HmacResult}
|
||||||
readOnly
|
readOnly
|
||||||
placeholder="SM3 MAC 计算结果将显示在这里"
|
placeholder="SM3 HMAC 计算结果将显示在这里"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button onClick={calculateSm3Mac}>计算 SM3 MAC</button>
|
<button onClick={calculateSm3Hmac}>计算 SM3 HMAC</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -336,7 +193,7 @@ function App() {
|
|||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>模式</label>
|
<label>模式</label>
|
||||||
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
|
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
|
||||||
{['ecb', 'cbc', 'cfb', 'gcm'].map(mode => (
|
{['ecb', 'cbc', 'gcm'].map(mode => (
|
||||||
<button
|
<button
|
||||||
key={mode}
|
key={mode}
|
||||||
className={sm4Mode === mode ? 'primary' : ''}
|
className={sm4Mode === mode ? 'primary' : ''}
|
||||||
@@ -402,22 +259,26 @@ function App() {
|
|||||||
<div className="panel">
|
<div className="panel">
|
||||||
<h2>Hex / UTF8 转换</h2>
|
<h2>Hex / UTF8 转换</h2>
|
||||||
|
|
||||||
<div className="form-group">
|
<div style={{ display: 'flex', gap: '1rem', marginBottom: '1rem' }}>
|
||||||
<label>Hex 输入</label>
|
<div className="form-group" style={{ flex: 1 }}>
|
||||||
<textarea
|
<label>Hex 输入</label>
|
||||||
value={hexInput}
|
<textarea
|
||||||
onChange={(e) => setHexInput(e.target.value)}
|
value={hexInput}
|
||||||
placeholder="输入 hex 字符串"
|
onChange={(e) => setHexInput(e.target.value)}
|
||||||
/>
|
placeholder="输入 hex 字符串"
|
||||||
</div>
|
style={{ height: '200px' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
<div className="form-group" style={{ flex: 1 }}>
|
||||||
<label>UTF8 输入</label>
|
<label>UTF8 输入</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={utf8Input}
|
value={utf8Input}
|
||||||
onChange={(e) => setUtf8Input(e.target.value)}
|
onChange={(e) => setUtf8Input(e.target.value)}
|
||||||
placeholder="输入 UTF8 字符串"
|
placeholder="输入 UTF8 字符串"
|
||||||
/>
|
style={{ height: '200px' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="button-group">
|
<div className="button-group">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { sm3Digest } from '../../utils/algorithm/sm3'
|
import { sm3Digest, sm3Hmac } from '../../utils/algorithm/sm3'
|
||||||
|
|
||||||
// UTF8 转 Hex
|
// UTF8 转 Hex
|
||||||
const utf8ToHex = (utf8: string): string => {
|
const utf8ToHex = (utf8: string): string => {
|
||||||
@@ -33,3 +33,44 @@ describe('SM3 算法测试', () => {
|
|||||||
expect(result1).not.toBe(result2)
|
expect(result1).not.toBe(result2)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('SM3 HMAC 算法测试', () => {
|
||||||
|
test('计算 SM3 HMAC', () => {
|
||||||
|
const key = '0123456789abcdef'
|
||||||
|
const originalMessage = '测试消息'
|
||||||
|
const messageHex = utf8ToHex(originalMessage)
|
||||||
|
const result = sm3Hmac(key, messageHex)
|
||||||
|
expect(result).toBeTruthy()
|
||||||
|
expect(result.length).toBe(64) // SM3 HMAC 结果为 256 位,即 64 个十六进制字符
|
||||||
|
})
|
||||||
|
|
||||||
|
test('相同 key 和消息应该产生相同的 HMAC', () => {
|
||||||
|
const key = '0123456789abcdef'
|
||||||
|
const originalMessage = '测试消息'
|
||||||
|
const messageHex = utf8ToHex(originalMessage)
|
||||||
|
const result1 = sm3Hmac(key, messageHex)
|
||||||
|
const result2 = sm3Hmac(key, messageHex)
|
||||||
|
expect(result1).toBe(result2)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('不同消息应该产生不同的 HMAC', () => {
|
||||||
|
const key = '0123456789abcdef'
|
||||||
|
const originalMessage1 = '测试消息1'
|
||||||
|
const originalMessage2 = '测试消息2'
|
||||||
|
const messageHex1 = utf8ToHex(originalMessage1)
|
||||||
|
const messageHex2 = utf8ToHex(originalMessage2)
|
||||||
|
const result1 = sm3Hmac(key, messageHex1)
|
||||||
|
const result2 = sm3Hmac(key, messageHex2)
|
||||||
|
expect(result1).not.toBe(result2)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('不同 key 应该产生不同的 HMAC', () => {
|
||||||
|
const key1 = '0123456789abcdef'
|
||||||
|
const key2 = 'fedcba9876543210'
|
||||||
|
const originalMessage = '测试消息'
|
||||||
|
const messageHex = utf8ToHex(originalMessage)
|
||||||
|
const result1 = sm3Hmac(key1, messageHex)
|
||||||
|
const result2 = sm3Hmac(key2, messageHex)
|
||||||
|
expect(result1).not.toBe(result2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
declare module 'gmsm-sm3js' {
|
||||||
|
export function sumHex(data: Uint8Array): string;
|
||||||
|
export function fromHex(hex: string): Uint8Array;
|
||||||
|
export function create(): any;
|
||||||
|
export function kdf(data: Uint8Array, len: number): Uint8Array;
|
||||||
|
export function sum(data: Uint8Array): Uint8Array;
|
||||||
|
export function toHex(bytes: Uint8Array): string;
|
||||||
|
export function normalizeInput(input: string | Buffer | Uint8Array): Uint8Array;
|
||||||
|
export function testSpeed(hashFn: Function, N: number, M: number): void;
|
||||||
|
}
|
||||||
+82
-14
@@ -1,16 +1,10 @@
|
|||||||
import { SM3 } from 'gm-crypto'
|
import { sumHex, fromHex } from 'gmsm-sm3js'
|
||||||
|
|
||||||
// 验证是否为有效的 hex 字符串
|
// 验证是否为有效的 hex 字符串
|
||||||
const isValidHex = (hex: string): boolean => {
|
const isValidHex = (hex: string): boolean => {
|
||||||
return /^[0-9a-fA-F]*$/.test(hex) && hex.length % 2 === 0
|
return /^[0-9a-fA-F]*$/.test(hex) && hex.length % 2 === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArrayBuffer 转 hex 字符串
|
|
||||||
const arrayBufferToHex = (buffer: ArrayBuffer): string => {
|
|
||||||
const bytes = new Uint8Array(buffer)
|
|
||||||
return Array.from(bytes).map(byte => byte.toString(16).padStart(2, '0')).join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
// SM3 计算
|
// SM3 计算
|
||||||
export const sm3Digest = (message: string): string => {
|
export const sm3Digest = (message: string): string => {
|
||||||
try {
|
try {
|
||||||
@@ -21,17 +15,91 @@ export const sm3Digest = (message: string): string => {
|
|||||||
|
|
||||||
console.log('Input hex message:', message)
|
console.log('Input hex message:', message)
|
||||||
|
|
||||||
// 直接使用 hex 字符串计算 SM3
|
// 将 hex 字符串转换为 Uint8Array
|
||||||
const result = SM3.digest(message, 'hex')
|
const messageBytes = fromHex(message)
|
||||||
|
|
||||||
|
// 使用 gmsm-sm3js 计算 SM3
|
||||||
|
const result = sumHex(messageBytes)
|
||||||
console.log('SM3 result:', result)
|
console.log('SM3 result:', result)
|
||||||
|
|
||||||
// 处理返回结果
|
return result
|
||||||
const hexResult = typeof result === 'string' ? result : arrayBufferToHex(result)
|
|
||||||
console.log('Hex result:', hexResult)
|
|
||||||
|
|
||||||
return hexResult
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('SM3 digest error:', error)
|
console.error('SM3 digest error:', error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HMAC-SM3 计算
|
||||||
|
export const sm3Hmac = (key: string, message: string): string => {
|
||||||
|
try {
|
||||||
|
// 验证 key 是否为有效的 hex 字符串
|
||||||
|
if (!key) {
|
||||||
|
throw new Error('HMAC key is required')
|
||||||
|
}
|
||||||
|
if (!isValidHex(key)) {
|
||||||
|
throw new Error('Invalid key: must be hex string')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证 message 是否为有效的 hex 字符串
|
||||||
|
if (!isValidHex(message)) {
|
||||||
|
throw new Error('Invalid message: must be hex string')
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Input key:', key)
|
||||||
|
console.log('Input message:', message)
|
||||||
|
|
||||||
|
// 将 hex 字符串转换为 Uint8Array
|
||||||
|
const keyBytes = fromHex(key)
|
||||||
|
const messageBytes = fromHex(message)
|
||||||
|
|
||||||
|
// 检查密钥长度
|
||||||
|
if (keyBytes.length === 0) {
|
||||||
|
throw new Error('HMAC key cannot be empty')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实现 HMAC-SM3 算法
|
||||||
|
const blockSize = 64 // SM3 块大小为 512 位 = 64 字节
|
||||||
|
|
||||||
|
// 处理密钥
|
||||||
|
let k: Uint8Array
|
||||||
|
if (keyBytes.length > blockSize) {
|
||||||
|
// 如果密钥长度超过块大小,对密钥进行 SM3 哈希
|
||||||
|
k = fromHex(sumHex(keyBytes))
|
||||||
|
} else {
|
||||||
|
// 否则,用 0 填充到块大小
|
||||||
|
k = new Uint8Array(blockSize)
|
||||||
|
k.set(keyBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算 inner padding
|
||||||
|
const ipad = new Uint8Array(blockSize)
|
||||||
|
for (let i = 0; i < blockSize; i++) {
|
||||||
|
ipad[i] = k[i] ^ 0x36
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算 outer padding
|
||||||
|
const opad = new Uint8Array(blockSize)
|
||||||
|
for (let i = 0; i < blockSize; i++) {
|
||||||
|
opad[i] = k[i] ^ 0x5c
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算 inner hash: SM3(ipad || message)
|
||||||
|
const innerData = new Uint8Array(ipad.length + messageBytes.length)
|
||||||
|
innerData.set(ipad)
|
||||||
|
innerData.set(messageBytes, ipad.length)
|
||||||
|
const innerHash = fromHex(sumHex(innerData))
|
||||||
|
|
||||||
|
// 计算 outer hash: SM3(opad || innerHash)
|
||||||
|
const outerData = new Uint8Array(opad.length + innerHash.length)
|
||||||
|
outerData.set(opad)
|
||||||
|
outerData.set(innerHash, opad.length)
|
||||||
|
const result = sumHex(outerData)
|
||||||
|
|
||||||
|
console.log('SM3 HMAC result:', result)
|
||||||
|
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
console.error('SM3 HMAC error:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+59
-50
@@ -1,5 +1,6 @@
|
|||||||
import * as sjcl from 'sjcl-with-all'
|
import * as sjcl from 'sjcl-with-all'
|
||||||
import { bindSM4 } from 'gmsm-sm4js'
|
import { bindSM4 } from 'gmsm-sm4js'
|
||||||
|
import { SM4 } from 'gm-crypto'
|
||||||
|
|
||||||
// 绑定 SM4 到 sjcl
|
// 绑定 SM4 到 sjcl
|
||||||
bindSM4(sjcl)
|
bindSM4(sjcl)
|
||||||
@@ -76,38 +77,34 @@ export const sm4Encrypt = (message: string, key: string, mode: string, iv?: stri
|
|||||||
throw new Error('Invalid IV: must be hex string')
|
throw new Error('Invalid IV: must be hex string')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换为字节数组
|
|
||||||
const messageBytes = hexToBytes(message)
|
|
||||||
const keyBytes = hexToBytes(key)
|
|
||||||
|
|
||||||
// 处理填充
|
|
||||||
let dataToEncrypt = messageBytes
|
|
||||||
if (paddingMode === 'pkcs7') {
|
|
||||||
dataToEncrypt = pkcs7Pad(messageBytes, 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建 SM4 密钥
|
|
||||||
const sm4Key = new sjcl.cipher.sm4(sjcl.codec.bytes.toBits(keyBytes))
|
|
||||||
|
|
||||||
// 根据模式选择加密方式
|
// 根据模式选择加密方式
|
||||||
const modeLower = mode.toLowerCase()
|
const modeLower = mode.toLowerCase()
|
||||||
if (modeLower === 'ecb') {
|
if (modeLower === 'ecb') {
|
||||||
// ECB 模式
|
// ECB 模式使用 gm-crypto
|
||||||
const blockSize = 16
|
return SM4.encrypt(message, key, {
|
||||||
const encryptedBytes: number[] = []
|
mode: 'ecb',
|
||||||
for (let i = 0; i < dataToEncrypt.length; i += blockSize) {
|
inputEncoding: 'hex',
|
||||||
const block = dataToEncrypt.slice(i, i + blockSize)
|
outputEncoding: 'hex',
|
||||||
const blockBits = sjcl.codec.bytes.toBits(block)
|
padding: paddingMode === 'pkcs7' ? 'pkcs7' : 'none'
|
||||||
const encryptedBlockBits = sm4Key.encrypt(blockBits)
|
})
|
||||||
const encryptedBlockBytes = sjcl.codec.bytes.fromBits(encryptedBlockBits)
|
|
||||||
encryptedBytes.push(...encryptedBlockBytes)
|
|
||||||
}
|
|
||||||
return bytesToHex(encryptedBytes)
|
|
||||||
} else if (modeLower === 'cbc') {
|
} else if (modeLower === 'cbc') {
|
||||||
// CBC 模式
|
// CBC 模式使用 gmsm-sm4js
|
||||||
if (!iv) {
|
if (!iv) {
|
||||||
throw new Error('IV is required for CBC mode')
|
throw new Error('IV is required for CBC mode')
|
||||||
}
|
}
|
||||||
|
// 转换为字节数组
|
||||||
|
const messageBytes = hexToBytes(message)
|
||||||
|
const keyBytes = hexToBytes(key)
|
||||||
|
|
||||||
|
// 处理填充
|
||||||
|
let dataToEncrypt = messageBytes
|
||||||
|
if (paddingMode === 'pkcs7') {
|
||||||
|
dataToEncrypt = pkcs7Pad(messageBytes, 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建 SM4 密钥
|
||||||
|
const sm4Key = new sjcl.cipher.sm4(sjcl.codec.bytes.toBits(keyBytes))
|
||||||
|
|
||||||
const ivBytes = hexToBytes(iv)
|
const ivBytes = hexToBytes(iv)
|
||||||
const ivBits = sjcl.codec.bytes.toBits(ivBytes)
|
const ivBits = sjcl.codec.bytes.toBits(ivBytes)
|
||||||
const dataBits = sjcl.codec.bytes.toBits(dataToEncrypt)
|
const dataBits = sjcl.codec.bytes.toBits(dataToEncrypt)
|
||||||
@@ -115,10 +112,23 @@ export const sm4Encrypt = (message: string, key: string, mode: string, iv?: stri
|
|||||||
const encryptedBytes = sjcl.codec.bytes.fromBits(encryptedBits)
|
const encryptedBytes = sjcl.codec.bytes.fromBits(encryptedBits)
|
||||||
return bytesToHex(encryptedBytes)
|
return bytesToHex(encryptedBytes)
|
||||||
} else if (modeLower === 'gcm') {
|
} else if (modeLower === 'gcm') {
|
||||||
// GCM 模式
|
// GCM 模式使用 gmsm-sm4js
|
||||||
if (!iv) {
|
if (!iv) {
|
||||||
throw new Error('IV is required for GCM mode')
|
throw new Error('IV is required for GCM mode')
|
||||||
}
|
}
|
||||||
|
// 转换为字节数组
|
||||||
|
const messageBytes = hexToBytes(message)
|
||||||
|
const keyBytes = hexToBytes(key)
|
||||||
|
|
||||||
|
// 处理填充
|
||||||
|
let dataToEncrypt = messageBytes
|
||||||
|
if (paddingMode === 'pkcs7') {
|
||||||
|
dataToEncrypt = pkcs7Pad(messageBytes, 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建 SM4 密钥
|
||||||
|
const sm4Key = new sjcl.cipher.sm4(sjcl.codec.bytes.toBits(keyBytes))
|
||||||
|
|
||||||
const ivBytes = hexToBytes(iv)
|
const ivBytes = hexToBytes(iv)
|
||||||
const ivBits = sjcl.codec.bytes.toBits(ivBytes)
|
const ivBits = sjcl.codec.bytes.toBits(ivBytes)
|
||||||
const dataBits = sjcl.codec.bytes.toBits(dataToEncrypt)
|
const dataBits = sjcl.codec.bytes.toBits(dataToEncrypt)
|
||||||
@@ -161,36 +171,28 @@ export const sm4Decrypt = (encrypted: string, key: string, mode: string, iv?: st
|
|||||||
throw new Error('Invalid IV: must be hex string')
|
throw new Error('Invalid IV: must be hex string')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换为字节数组
|
|
||||||
const encryptedBytes = hexToBytes(encrypted)
|
|
||||||
const keyBytes = hexToBytes(key)
|
|
||||||
|
|
||||||
// 创建 SM4 密钥
|
|
||||||
const sm4Key = new sjcl.cipher.sm4(sjcl.codec.bytes.toBits(keyBytes))
|
|
||||||
|
|
||||||
// 根据模式选择解密方式
|
// 根据模式选择解密方式
|
||||||
const modeLower = mode.toLowerCase()
|
const modeLower = mode.toLowerCase()
|
||||||
if (modeLower === 'ecb') {
|
if (modeLower === 'ecb') {
|
||||||
// ECB 模式
|
// ECB 模式使用 gm-crypto
|
||||||
const blockSize = 16
|
return SM4.decrypt(encrypted, key, {
|
||||||
const decryptedBytes: number[] = []
|
mode: 'ecb',
|
||||||
for (let i = 0; i < encryptedBytes.length; i += blockSize) {
|
inputEncoding: 'hex',
|
||||||
const block = encryptedBytes.slice(i, i + blockSize)
|
outputEncoding: 'hex',
|
||||||
const blockBits = sjcl.codec.bytes.toBits(block)
|
padding: paddingMode === 'pkcs7' ? 'pkcs7' : 'none'
|
||||||
const decryptedBlockBits = sm4Key.decrypt(blockBits)
|
})
|
||||||
const decryptedBlockBytes = sjcl.codec.bytes.fromBits(decryptedBlockBits)
|
|
||||||
decryptedBytes.push(...decryptedBlockBytes)
|
|
||||||
}
|
|
||||||
// 处理去填充
|
|
||||||
if (paddingMode === 'pkcs7') {
|
|
||||||
return bytesToHex(pkcs7Unpad(decryptedBytes))
|
|
||||||
}
|
|
||||||
return bytesToHex(decryptedBytes)
|
|
||||||
} else if (modeLower === 'cbc') {
|
} else if (modeLower === 'cbc') {
|
||||||
// CBC 模式
|
// CBC 模式使用 gmsm-sm4js
|
||||||
if (!iv) {
|
if (!iv) {
|
||||||
throw new Error('IV is required for CBC mode')
|
throw new Error('IV is required for CBC mode')
|
||||||
}
|
}
|
||||||
|
// 转换为字节数组
|
||||||
|
const encryptedBytes = hexToBytes(encrypted)
|
||||||
|
const keyBytes = hexToBytes(key)
|
||||||
|
|
||||||
|
// 创建 SM4 密钥
|
||||||
|
const sm4Key = new sjcl.cipher.sm4(sjcl.codec.bytes.toBits(keyBytes))
|
||||||
|
|
||||||
const ivBytes = hexToBytes(iv)
|
const ivBytes = hexToBytes(iv)
|
||||||
const ivBits = sjcl.codec.bytes.toBits(ivBytes)
|
const ivBits = sjcl.codec.bytes.toBits(ivBytes)
|
||||||
const encryptedBits = sjcl.codec.bytes.toBits(encryptedBytes)
|
const encryptedBits = sjcl.codec.bytes.toBits(encryptedBytes)
|
||||||
@@ -202,10 +204,17 @@ export const sm4Decrypt = (encrypted: string, key: string, mode: string, iv?: st
|
|||||||
}
|
}
|
||||||
return bytesToHex(decryptedBytes)
|
return bytesToHex(decryptedBytes)
|
||||||
} else if (modeLower === 'gcm') {
|
} else if (modeLower === 'gcm') {
|
||||||
// GCM 模式
|
// GCM 模式使用 gmsm-sm4js
|
||||||
if (!iv) {
|
if (!iv) {
|
||||||
throw new Error('IV is required for GCM mode')
|
throw new Error('IV is required for GCM mode')
|
||||||
}
|
}
|
||||||
|
// 转换为字节数组
|
||||||
|
const encryptedBytes = hexToBytes(encrypted)
|
||||||
|
const keyBytes = hexToBytes(key)
|
||||||
|
|
||||||
|
// 创建 SM4 密钥
|
||||||
|
const sm4Key = new sjcl.cipher.sm4(sjcl.codec.bytes.toBits(keyBytes))
|
||||||
|
|
||||||
const ivBytes = hexToBytes(iv)
|
const ivBytes = hexToBytes(iv)
|
||||||
const ivBits = sjcl.codec.bytes.toBits(ivBytes)
|
const ivBits = sjcl.codec.bytes.toBits(ivBytes)
|
||||||
const encryptedBits = sjcl.codec.bytes.toBits(encryptedBytes)
|
const encryptedBits = sjcl.codec.bytes.toBits(encryptedBytes)
|
||||||
|
|||||||
Reference in New Issue
Block a user