hotfox
This commit is contained in:
parent
32626c84a6
commit
4b3d5b762f
123
src/App.tsx
123
src/App.tsx
@ -3,7 +3,7 @@ import { VERSION } from './version'
|
||||
|
||||
import { sm3Digest, sm3Hmac } from './utils/algorithm/sm3'
|
||||
import { sm4Encrypt, sm4Decrypt } from './utils/algorithm/sm4'
|
||||
import { sm2GenerateKeyPair, sm2GetPublicKeyFromPrivateKey, sm2Encrypt, sm2Decrypt, sm2Sign, sm2Verify } from './utils/algorithm/sm2'
|
||||
import { sm2GenerateKeyPair, sm2GetPublicKeyFromPrivateKey, sm2Encrypt, sm2Decrypt, sm2Sign, sm2Verify, sm2DerToC1C3C2, sm2C1C3C2ToDer } from './utils/algorithm/sm2'
|
||||
import { parseSm2Cert, parseCertFromFile, generateRootCert, generateAppCert } from './utils/algorithm/cert'
|
||||
import { hexToUtf8, utf8ToHex, hexToBase64, base64ToHex } from './utils/convert'
|
||||
|
||||
@ -172,6 +172,8 @@ function App() {
|
||||
const [hexInput, setHexInput] = useState('')
|
||||
const [utf8Input, setUtf8Input] = useState('')
|
||||
const [base64Input, setBase64Input] = useState('')
|
||||
const [derInput, setDerInput] = useState('')
|
||||
const [c1c3c2Input, setC1c3c2Input] = useState('')
|
||||
|
||||
// 证书工具状态
|
||||
const [certInfo, setCertInfo] = useState<any>(null)
|
||||
@ -262,6 +264,26 @@ function App() {
|
||||
}
|
||||
}
|
||||
|
||||
// DER 转 C1C3C2
|
||||
const handleDerToC1C3C2 = () => {
|
||||
try {
|
||||
const c1c3c2 = sm2DerToC1C3C2(derInput)
|
||||
setC1c3c2Input(c1c3c2)
|
||||
} catch (error) {
|
||||
alert('转换失败: ' + (error instanceof Error ? error.message : error))
|
||||
}
|
||||
}
|
||||
|
||||
// C1C3C2 转 DER
|
||||
const handleC1C3C2ToDer = () => {
|
||||
try {
|
||||
const der = sm2C1C3C2ToDer(c1c3c2Input)
|
||||
setDerInput(der)
|
||||
} catch (error) {
|
||||
alert('转换失败: ' + (error instanceof Error ? error.message : error))
|
||||
}
|
||||
}
|
||||
|
||||
// 处理证书文件上传
|
||||
const handleCertFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0]
|
||||
@ -353,7 +375,10 @@ function App() {
|
||||
const handleDecryptSm2 = () => {
|
||||
try {
|
||||
const decrypted = sm2Decrypt(sm2Encrypted, sm2PrivateKey)
|
||||
setSm2Decrypted(decrypted)
|
||||
const hexResult = Array.from(decrypted)
|
||||
.map(byte => byte.toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
setSm2Decrypted(hexResult)
|
||||
} catch (error) {
|
||||
alert('解密失败: ' + (error instanceof Error ? error.message : error))
|
||||
}
|
||||
@ -1470,6 +1495,100 @@ function App() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* DER / C1C3C2 互转卡片 */}
|
||||
<div style={{ backgroundColor: '#2d2d2d', padding: '15px', borderRadius: '8px', marginTop: '20px' }}>
|
||||
<h3 style={{ color: '#e0e0e0', marginBottom: '15px' }}>SM2 DER / C1C3C2 密文格式互转</h3>
|
||||
<div style={{ display: 'flex', gap: '1rem', flexDirection: isMobile ? 'column' : 'row' }}>
|
||||
<div className="form-group" style={{ flex: 1 }}>
|
||||
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>DER 格式输入 (hex)</label>
|
||||
<textarea
|
||||
value={derInput}
|
||||
onChange={(e) => setDerInput(e.target.value)}
|
||||
onContextMenu={(e) => openMenu(e, derInput, setDerInput, false)}
|
||||
onTouchStart={(e) => {
|
||||
const touchTimer = setTimeout(() => {
|
||||
openMenu(e, derInput, setDerInput, false)
|
||||
}, 500)
|
||||
return () => clearTimeout(touchTimer)
|
||||
}}
|
||||
placeholder="输入 DER 格式密文 (hex)"
|
||||
style={{
|
||||
height: '150px',
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #333',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#1e1e1e',
|
||||
color: '#e0e0e0',
|
||||
fontSize: '14px',
|
||||
resize: 'vertical'
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
onClick={handleDerToC1C3C2}
|
||||
style={{
|
||||
marginTop: '0.5rem',
|
||||
padding: '8px 16px',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
background: '#f59e0b',
|
||||
color: '#121212',
|
||||
fontSize: '14px',
|
||||
fontWeight: 'bold',
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.3s ease'
|
||||
}}
|
||||
>
|
||||
DER → C1C3C2
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="form-group" style={{ flex: 1 }}>
|
||||
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>C1C3C2 格式输入 (hex)</label>
|
||||
<textarea
|
||||
value={c1c3c2Input}
|
||||
onChange={(e) => setC1c3c2Input(e.target.value)}
|
||||
onContextMenu={(e) => openMenu(e, c1c3c2Input, setC1c3c2Input, false)}
|
||||
onTouchStart={(e) => {
|
||||
const touchTimer = setTimeout(() => {
|
||||
openMenu(e, c1c3c2Input, setC1c3c2Input, false)
|
||||
}, 500)
|
||||
return () => clearTimeout(touchTimer)
|
||||
}}
|
||||
placeholder="输入 C1C3C2 格式密文 (hex)"
|
||||
style={{
|
||||
height: '150px',
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #333',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#1e1e1e',
|
||||
color: '#e0e0e0',
|
||||
fontSize: '14px',
|
||||
resize: 'vertical'
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
onClick={handleC1C3C2ToDer}
|
||||
style={{
|
||||
marginTop: '0.5rem',
|
||||
padding: '8px 16px',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
background: '#f59e0b',
|
||||
color: '#121212',
|
||||
fontSize: '14px',
|
||||
fontWeight: 'bold',
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.3s ease'
|
||||
}}
|
||||
>
|
||||
C1C3C2 → DER
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@ -51,7 +51,7 @@ export const sm2Encrypt = (message: string, publicKey: string, cipherMode: numbe
|
||||
}
|
||||
|
||||
// SM2 解密
|
||||
export const sm2Decrypt = (ciphertext: string, privateKey: string, cipherMode: number = 1): string => {
|
||||
export const sm2Decrypt = (ciphertext: string, privateKey: string, cipherMode: number = 1): Uint8Array => {
|
||||
// 验证密文是否为有效的 hex 字符串
|
||||
if (!isValidHex(ciphertext)) {
|
||||
throw new Error('Invalid ciphertext: must be hex string')
|
||||
@ -61,13 +61,15 @@ export const sm2Decrypt = (ciphertext: string, privateKey: string, cipherMode: n
|
||||
throw new Error('Invalid private key: must be hex string')
|
||||
}
|
||||
|
||||
// 解密
|
||||
const decrypted = sm2Extended.doDecrypt(ciphertext, privateKey, cipherMode)
|
||||
// 将解密结果转换为 hex 字符串
|
||||
const decryptedBytes = new TextEncoder().encode(decrypted)
|
||||
return Array.from(decryptedBytes)
|
||||
.map(byte => byte.toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
// 解密,指定 output 为 'bytes' 以获取 Uint8Array 格式
|
||||
const decrypted = sm2Extended.doDecrypt(ciphertext, privateKey, cipherMode, { output: 'bytes' })
|
||||
|
||||
// 如果返回的是字符串,则转为 Uint8Array
|
||||
if (typeof decrypted === 'string') {
|
||||
return new TextEncoder().encode(decrypted)
|
||||
}
|
||||
|
||||
return decrypted
|
||||
}
|
||||
|
||||
// SM2 签名
|
||||
|
||||
@ -1 +1 @@
|
||||
export const VERSION = "V1.0-20260424025112";
|
||||
export const VERSION = "V1.0-20260428031400";
|
||||
Loading…
Reference in New Issue
Block a user