297 lines
8.9 KiB
TypeScript
297 lines
8.9 KiB
TypeScript
import { useState } from 'react'
|
|
|
|
import { sm3Digest, sm3Hmac } from './utils/algorithm/sm3'
|
|
import { sm4Encrypt, sm4Decrypt } from './utils/algorithm/sm4'
|
|
import { hexToUtf8, utf8ToHex } from './utils/convert'
|
|
|
|
function App() {
|
|
const [activeTab, setActiveTab] = useState('sm3')
|
|
|
|
|
|
|
|
// SM3 状态
|
|
const [sm3Message, setSm3Message] = useState('')
|
|
const [sm3Result, setSm3Result] = useState('')
|
|
const [sm3HmacKey, setSm3HmacKey] = useState('')
|
|
const [sm3HmacResult, setSm3HmacResult] = useState('')
|
|
|
|
// SM4 状态
|
|
const [sm4Key, setSm4Key] = useState('')
|
|
const [sm4Iv, setSm4Iv] = useState('')
|
|
const [sm4Message, setSm4Message] = useState('')
|
|
const [sm4Mode, setSm4Mode] = useState('ecb')
|
|
const [sm4PaddingMode, setSm4PaddingMode] = useState<'pkcs7' | 'none'>('pkcs7')
|
|
const [sm4Encrypted, setSm4Encrypted] = useState('')
|
|
const [sm4Decrypted, setSm4Decrypted] = useState('')
|
|
|
|
// 转换工具状态
|
|
const [hexInput, setHexInput] = useState('')
|
|
const [utf8Input, setUtf8Input] = useState('')
|
|
|
|
|
|
|
|
// SM3 计算
|
|
const handleCalculateSm3 = () => {
|
|
try {
|
|
const result = sm3Digest(sm3Message)
|
|
setSm3Result(result)
|
|
} catch (error) {
|
|
alert('计算失败: ' + error)
|
|
}
|
|
}
|
|
|
|
// SM3 HMAC 计算
|
|
const calculateSm3Hmac = () => {
|
|
try {
|
|
const result = sm3Hmac(sm3HmacKey, sm3Message)
|
|
setSm3HmacResult(result)
|
|
} catch (error) {
|
|
alert('HMAC 计算失败: ' + (error instanceof Error ? error.message : error))
|
|
}
|
|
}
|
|
|
|
// SM4 加密
|
|
const handleEncryptSm4 = () => {
|
|
try {
|
|
const encrypted = sm4Encrypt(sm4Message, sm4Key, sm4Mode, sm4Iv, sm4PaddingMode)
|
|
setSm4Encrypted(encrypted)
|
|
} catch (error) {
|
|
alert('加密失败: ' + (error instanceof Error ? error.message : error))
|
|
}
|
|
}
|
|
|
|
// SM4 解密
|
|
const handleDecryptSm4 = () => {
|
|
try {
|
|
const decrypted = sm4Decrypt(sm4Encrypted, sm4Key, sm4Mode, sm4Iv, sm4PaddingMode)
|
|
setSm4Decrypted(decrypted)
|
|
} catch (error) {
|
|
alert('解密失败: ' + (error instanceof Error ? error.message : error))
|
|
}
|
|
}
|
|
|
|
// Hex 转 UTF8
|
|
const handleHexToUtf8 = () => {
|
|
try {
|
|
const utf8 = hexToUtf8(hexInput)
|
|
setUtf8Input(utf8)
|
|
} catch (error) {
|
|
alert('转换失败: ' + error)
|
|
}
|
|
}
|
|
|
|
// UTF8 转 Hex
|
|
const handleUtf8ToHex = () => {
|
|
try {
|
|
const hex = utf8ToHex(utf8Input)
|
|
setHexInput(hex)
|
|
} catch (error) {
|
|
alert('转换失败: ' + error)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="app">
|
|
<h1>安全算法计算工具</h1>
|
|
|
|
<div className="tabs">
|
|
<button
|
|
className={`tab ${activeTab === 'sm3' ? 'active' : ''}`}
|
|
onClick={() => setActiveTab('sm3')}
|
|
>
|
|
SM3
|
|
</button>
|
|
<button
|
|
className={`tab ${activeTab === 'sm4' ? 'active' : ''}`}
|
|
onClick={() => setActiveTab('sm4')}
|
|
>
|
|
SM4
|
|
</button>
|
|
<button
|
|
className={`tab ${activeTab === 'convert' ? 'active' : ''}`}
|
|
onClick={() => setActiveTab('convert')}
|
|
>
|
|
转换工具
|
|
</button>
|
|
</div>
|
|
|
|
|
|
|
|
{/* SM3 面板 */}
|
|
{activeTab === 'sm3' && (
|
|
<div className="panel">
|
|
<h2>SM3 算法</h2>
|
|
|
|
<div className="form-group">
|
|
<label>消息</label>
|
|
<textarea
|
|
value={sm3Message}
|
|
onChange={(e) => setSm3Message(e.target.value)}
|
|
placeholder="输入要计算的消息"
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label>SM3 结果</label>
|
|
<textarea
|
|
value={sm3Result}
|
|
readOnly
|
|
placeholder="SM3 计算结果将显示在这里"
|
|
/>
|
|
</div>
|
|
|
|
<button onClick={handleCalculateSm3}>计算 SM3</button>
|
|
|
|
<div className="form-group" style={{ marginTop: '1.5rem' }}>
|
|
<label>HMAC 密钥</label>
|
|
<input
|
|
type="text"
|
|
value={sm3HmacKey}
|
|
onChange={(e) => setSm3HmacKey(e.target.value)}
|
|
placeholder="输入 HMAC 密钥 (hex)"
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label>SM3 HMAC 结果</label>
|
|
<textarea
|
|
value={sm3HmacResult}
|
|
readOnly
|
|
placeholder="SM3 HMAC 计算结果将显示在这里"
|
|
/>
|
|
</div>
|
|
|
|
<button onClick={calculateSm3Hmac}>计算 SM3 HMAC</button>
|
|
</div>
|
|
)}
|
|
|
|
{/* SM4 面板 */}
|
|
{activeTab === 'sm4' && (
|
|
<div className="panel">
|
|
<h2>SM4 算法</h2>
|
|
|
|
<div className="form-group">
|
|
<label>密钥 (16 字节 hex)</label>
|
|
<input
|
|
type="text"
|
|
value={sm4Key}
|
|
onChange={(e) => setSm4Key(e.target.value)}
|
|
placeholder="输入 16 字节密钥 (32 位 hex)"
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label>IV (16 字节 hex, CBC/CFB/GCM 模式需要)</label>
|
|
<input
|
|
type="text"
|
|
value={sm4Iv}
|
|
onChange={(e) => setSm4Iv(e.target.value)}
|
|
placeholder="输入 16 字节 IV (32 位 hex)"
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label>模式</label>
|
|
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
|
|
{['ecb', 'cbc', 'gcm'].map(mode => (
|
|
<button
|
|
key={mode}
|
|
className={sm4Mode === mode ? 'primary' : ''}
|
|
onClick={() => setSm4Mode(mode)}
|
|
>
|
|
{mode.toUpperCase()}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label>填充模式</label>
|
|
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
|
|
{(['pkcs7', 'none'] as const).map(paddingMode => (
|
|
<button
|
|
key={paddingMode}
|
|
className={sm4PaddingMode === paddingMode ? 'primary' : ''}
|
|
onClick={() => setSm4PaddingMode(paddingMode)}
|
|
>
|
|
{paddingMode}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label>消息</label>
|
|
<textarea
|
|
value={sm4Message}
|
|
onChange={(e) => setSm4Message(e.target.value)}
|
|
placeholder="输入要加密的消息"
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label>加密结果</label>
|
|
<textarea
|
|
value={sm4Encrypted}
|
|
onChange={(e) => setSm4Encrypted(e.target.value)}
|
|
placeholder="输入加密结果 (hex)"
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label>解密结果</label>
|
|
<textarea
|
|
value={sm4Decrypted}
|
|
readOnly
|
|
placeholder="解密结果将显示在这里"
|
|
/>
|
|
</div>
|
|
|
|
<div className="button-group">
|
|
<button onClick={handleEncryptSm4}>加密</button>
|
|
<button onClick={handleDecryptSm4}>解密</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 转换工具面板 */}
|
|
{activeTab === 'convert' && (
|
|
<div className="panel">
|
|
<h2>Hex / UTF8 转换</h2>
|
|
|
|
<div style={{ display: 'flex', gap: '1rem', marginBottom: '1rem' }}>
|
|
<div className="form-group" style={{ flex: 1 }}>
|
|
<label>UTF8 输入</label>
|
|
<textarea
|
|
value={utf8Input}
|
|
onChange={(e) => setUtf8Input(e.target.value)}
|
|
placeholder="输入 UTF8 字符串"
|
|
style={{ height: '200px' }}
|
|
/>
|
|
<button onClick={handleUtf8ToHex} style={{ marginTop: '0.5rem' }}>UTF8 → Hex</button>
|
|
</div>
|
|
|
|
<div className="form-group" style={{ flex: 1 }}>
|
|
<label>Hex 输入</label>
|
|
<textarea
|
|
value={hexInput}
|
|
onChange={(e) => setHexInput(e.target.value)}
|
|
placeholder="输入 hex 字符串"
|
|
style={{ height: '200px' }}
|
|
/>
|
|
<button onClick={handleHexToUtf8} style={{ marginTop: '0.5rem' }}>UTF8 ← Hex</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 页脚 */}
|
|
<footer style={{ marginTop: '2rem', padding: '1rem', borderTop: '1px solid #ccc', textAlign: 'center' }}>
|
|
<p>© 2026 安全算法计算工具</p>
|
|
<p>版本号: V1.0-{new Date().toISOString().slice(0, 19).replace(/[-:T]/g, '')}</p>
|
|
</footer>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default App |