sm2 加密解密通过
This commit is contained in:
parent
9f6df6bbd8
commit
0cb442eaa0
1064
package-lock.json
generated
1064
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,12 +11,18 @@
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@peculiar/webcrypto": "^1.5.0",
|
||||
"buffer": "^6.0.3",
|
||||
"crypto-browserify": "^3.12.1",
|
||||
"gm-crypto": "*",
|
||||
"gmsm-sm2js": "^0.7.1",
|
||||
"gmsm-sm3js": "^0.2.0",
|
||||
"gmsm-sm4js": "^0.7.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"sjcl-with-all": "^1.0.8"
|
||||
"sjcl-with-all": "^1.0.8",
|
||||
"sm-crypto": "^0.4.0",
|
||||
"stream-browserify": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^30.0.0",
|
||||
|
||||
351
src/App.tsx
351
src/App.tsx
@ -3,6 +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 { hexToUtf8, utf8ToHex } from './utils/convert'
|
||||
|
||||
function App() {
|
||||
@ -52,6 +53,15 @@ function App() {
|
||||
const [sm4Encrypted, setSm4Encrypted] = useState('')
|
||||
const [sm4Decrypted, setSm4Decrypted] = useState('')
|
||||
|
||||
// SM2 状态
|
||||
const [sm2PrivateKey, setSm2PrivateKey] = useState('')
|
||||
const [sm2PublicKey, setSm2PublicKey] = useState('')
|
||||
const [sm2Message, setSm2Message] = useState('')
|
||||
const [sm2Encrypted, setSm2Encrypted] = useState('')
|
||||
const [sm2Decrypted, setSm2Decrypted] = useState('')
|
||||
const [sm2Signature, setSm2Signature] = useState('')
|
||||
const [sm2VerifyResult, setSm2VerifyResult] = useState('')
|
||||
|
||||
// 转换工具状态
|
||||
const [hexInput, setHexInput] = useState('')
|
||||
const [utf8Input, setUtf8Input] = useState('')
|
||||
@ -118,6 +128,67 @@ function App() {
|
||||
}
|
||||
}
|
||||
|
||||
// SM2 生成密钥对
|
||||
const handleGenerateSm2KeyPair = () => {
|
||||
try {
|
||||
const keypair = sm2GenerateKeyPair()
|
||||
setSm2PrivateKey(keypair.privateKey)
|
||||
setSm2PublicKey(keypair.publicKey)
|
||||
} catch (error) {
|
||||
alert('生成密钥对失败: ' + (error instanceof Error ? error.message : error))
|
||||
}
|
||||
}
|
||||
|
||||
// SM2 从私钥计算公钥
|
||||
const handleCalculateSm2PublicKey = () => {
|
||||
try {
|
||||
const publicKey = sm2GetPublicKeyFromPrivateKey(sm2PrivateKey)
|
||||
setSm2PublicKey(publicKey)
|
||||
} catch (error) {
|
||||
alert('计算公钥失败: ' + (error instanceof Error ? error.message : 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))
|
||||
}
|
||||
}
|
||||
|
||||
// SM2 签名
|
||||
const handleSignSm2 = () => {
|
||||
try {
|
||||
const signature = sm2Sign(sm2Message, sm2PrivateKey)
|
||||
setSm2Signature(signature)
|
||||
} catch (error) {
|
||||
alert('签名失败: ' + (error instanceof Error ? error.message : error))
|
||||
}
|
||||
}
|
||||
|
||||
// SM2 验证签名
|
||||
const handleVerifySm2 = () => {
|
||||
try {
|
||||
const result = sm2Verify(sm2Message, sm2Signature, sm2PublicKey)
|
||||
setSm2VerifyResult(result ? '验证成功' : '验证失败')
|
||||
} catch (error) {
|
||||
alert('验证签名失败: ' + (error instanceof Error ? error.message : error))
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="app" style={{ maxWidth: '800px', margin: '0 auto', padding: '20px', fontFamily: 'Arial, sans-serif', backgroundColor: '#121212', color: '#e0e0e0', minHeight: '100vh' }}>
|
||||
<h1 style={{ textAlign: 'center', color: '#f59e0b' }}>安全算法计算工具</h1>
|
||||
@ -125,6 +196,24 @@ function App() {
|
||||
{/* 桌面端 Tab 选择 */}
|
||||
{!isMobile && (
|
||||
<div className="tabs" style={{ display: 'flex', borderBottom: '1px solid #333', marginBottom: '20px', backgroundColor: '#1e1e1e', borderRadius: '8px 8px 0 0', overflow: 'hidden' }}>
|
||||
<button
|
||||
className={`tab ${activeTab === 'sm2' ? 'active' : ''}`}
|
||||
onClick={() => handleTabChange('sm2')}
|
||||
style={{
|
||||
padding: '12px 24px',
|
||||
border: 'none',
|
||||
background: activeTab === 'sm2' ? '#333' : '#1e1e1e',
|
||||
cursor: 'pointer',
|
||||
fontSize: '16px',
|
||||
fontWeight: activeTab === 'sm2' ? 'bold' : 'normal',
|
||||
color: activeTab === 'sm2' ? '#f59e0b' : '#e0e0e0',
|
||||
outline: 'none',
|
||||
transition: 'all 0.3s ease',
|
||||
borderBottom: activeTab === 'sm2' ? '3px solid #f59e0b' : 'none'
|
||||
}}
|
||||
>
|
||||
SM2
|
||||
</button>
|
||||
<button
|
||||
className={`tab ${activeTab === 'sm3' ? 'active' : ''}`}
|
||||
onClick={() => handleTabChange('sm3')}
|
||||
@ -202,6 +291,7 @@ function App() {
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
<option value="sm2" style={{ background: '#1e1e1e', color: '#e0e0e0' }}>SM2</option>
|
||||
<option value="sm3" style={{ background: '#1e1e1e', color: '#e0e0e0' }}>SM3</option>
|
||||
<option value="sm4" style={{ background: '#1e1e1e', color: '#e0e0e0' }}>SM4</option>
|
||||
<option value="convert" style={{ background: '#1e1e1e', color: '#e0e0e0' }}>转换工具</option>
|
||||
@ -211,6 +301,267 @@ function App() {
|
||||
|
||||
|
||||
|
||||
{/* SM2 面板 */}
|
||||
{activeTab === 'sm2' && (
|
||||
<div className="panel" style={{ backgroundColor: '#1e1e1e', padding: '20px', borderRadius: '8px', boxShadow: '0 2px 4px rgba(0, 0, 0, 0.3)' }}>
|
||||
<h2 style={{ color: '#f59e0b', marginBottom: '20px' }}>SM2 算法</h2>
|
||||
|
||||
{/* 密钥对生成 */}
|
||||
<div style={{ marginBottom: '20px', padding: '15px', backgroundColor: '#2d2d2d', borderRadius: '8px' }}>
|
||||
<h3 style={{ color: '#e0e0e0', marginBottom: '10px' }}>密钥对生成</h3>
|
||||
<div style={{ display: 'flex', gap: '10px', marginBottom: '10px' }}>
|
||||
<button
|
||||
onClick={handleGenerateSm2KeyPair}
|
||||
style={{
|
||||
padding: '10px 20px',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
background: '#f59e0b',
|
||||
color: '#121212',
|
||||
fontSize: '16px',
|
||||
fontWeight: 'bold',
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.3s ease'
|
||||
}}
|
||||
>
|
||||
生成密钥对
|
||||
</button>
|
||||
<button
|
||||
onClick={handleCalculateSm2PublicKey}
|
||||
style={{
|
||||
padding: '10px 20px',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
background: '#f59e0b',
|
||||
color: '#121212',
|
||||
fontSize: '16px',
|
||||
fontWeight: 'bold',
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.3s ease'
|
||||
}}
|
||||
>
|
||||
从私钥计算公钥
|
||||
</button>
|
||||
</div>
|
||||
<div className="form-group" style={{ marginBottom: '10px' }}>
|
||||
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>私钥</label>
|
||||
<textarea
|
||||
value={sm2PrivateKey}
|
||||
onChange={(e) => setSm2PrivateKey(e.target.value)}
|
||||
placeholder="输入私钥 (hex) 或点击生成密钥对"
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #333',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#3d3d3d',
|
||||
color: '#e0e0e0',
|
||||
fontSize: '14px',
|
||||
resize: 'vertical',
|
||||
minHeight: '80px'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group" style={{ marginBottom: '10px' }}>
|
||||
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>公钥</label>
|
||||
<textarea
|
||||
value={sm2PublicKey}
|
||||
readOnly
|
||||
placeholder="公钥将显示在这里"
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #333',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#3d3d3d',
|
||||
color: '#e0e0e0',
|
||||
fontSize: '14px',
|
||||
resize: 'vertical',
|
||||
minHeight: '80px'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 加密/解密 */}
|
||||
<div style={{ marginBottom: '20px', padding: '15px', backgroundColor: '#2d2d2d', borderRadius: '8px' }}>
|
||||
<h3 style={{ color: '#e0e0e0', marginBottom: '10px' }}>加密/解密</h3>
|
||||
<div className="form-group" style={{ marginBottom: '10px' }}>
|
||||
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>消息</label>
|
||||
<textarea
|
||||
value={sm2Message}
|
||||
onChange={(e) => setSm2Message(e.target.value)}
|
||||
placeholder="输入要加密的消息 (hex)"
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #333',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#3d3d3d',
|
||||
color: '#e0e0e0',
|
||||
fontSize: '14px',
|
||||
resize: 'vertical',
|
||||
minHeight: '100px'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group" style={{ marginBottom: '10px' }}>
|
||||
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>加密结果</label>
|
||||
<textarea
|
||||
value={sm2Encrypted}
|
||||
onChange={(e) => setSm2Encrypted(e.target.value)}
|
||||
placeholder="输入加密结果 (hex)"
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #333',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#3d3d3d',
|
||||
color: '#e0e0e0',
|
||||
fontSize: '14px',
|
||||
resize: 'vertical',
|
||||
minHeight: '100px'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group" style={{ marginBottom: '10px' }}>
|
||||
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>解密结果</label>
|
||||
<textarea
|
||||
value={sm2Decrypted}
|
||||
readOnly
|
||||
placeholder="解密结果将显示在这里"
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #333',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#3d3d3d',
|
||||
color: '#e0e0e0',
|
||||
fontSize: '14px',
|
||||
resize: 'vertical',
|
||||
minHeight: '100px'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="button-group" style={{ display: 'flex', gap: '10px' }}>
|
||||
<button
|
||||
onClick={handleEncryptSm2}
|
||||
style={{
|
||||
padding: '10px 20px',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
background: '#f59e0b',
|
||||
color: '#121212',
|
||||
fontSize: '16px',
|
||||
fontWeight: 'bold',
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.3s ease',
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
加密
|
||||
</button>
|
||||
<button
|
||||
onClick={handleDecryptSm2}
|
||||
style={{
|
||||
padding: '10px 20px',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
background: '#f59e0b',
|
||||
color: '#121212',
|
||||
fontSize: '16px',
|
||||
fontWeight: 'bold',
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.3s ease',
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
解密
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 签名/验证 */}
|
||||
<div style={{ marginBottom: '20px', padding: '15px', backgroundColor: '#2d2d2d', borderRadius: '8px' }}>
|
||||
<h3 style={{ color: '#e0e0e0', marginBottom: '10px' }}>签名/验证</h3>
|
||||
<div className="form-group" style={{ marginBottom: '10px' }}>
|
||||
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>签名</label>
|
||||
<textarea
|
||||
value={sm2Signature}
|
||||
onChange={(e) => setSm2Signature(e.target.value)}
|
||||
placeholder="输入签名 (hex)"
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #333',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#3d3d3d',
|
||||
color: '#e0e0e0',
|
||||
fontSize: '14px',
|
||||
resize: 'vertical',
|
||||
minHeight: '100px'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group" style={{ marginBottom: '10px' }}>
|
||||
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>验证结果</label>
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px',
|
||||
border: '1px solid #333',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: '#3d3d3d',
|
||||
color: sm2VerifyResult === '验证成功' ? '#4ade80' : sm2VerifyResult === '验证失败' ? '#f87171' : '#e0e0e0',
|
||||
fontSize: '14px',
|
||||
minHeight: '40px',
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
{sm2VerifyResult || '验证结果将显示在这里'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="button-group" style={{ display: 'flex', gap: '10px' }}>
|
||||
<button
|
||||
onClick={handleSignSm2}
|
||||
style={{
|
||||
padding: '10px 20px',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
background: '#f59e0b',
|
||||
color: '#121212',
|
||||
fontSize: '16px',
|
||||
fontWeight: 'bold',
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.3s ease',
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
签名
|
||||
</button>
|
||||
<button
|
||||
onClick={handleVerifySm2}
|
||||
style={{
|
||||
padding: '10px 20px',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
background: '#f59e0b',
|
||||
color: '#121212',
|
||||
fontSize: '16px',
|
||||
fontWeight: 'bold',
|
||||
cursor: 'pointer',
|
||||
transition: 'background 0.3s ease',
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
验证签名
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* SM3 面板 */}
|
||||
{activeTab === 'sm3' && (
|
||||
<div className="panel" style={{ backgroundColor: '#1e1e1e', padding: '20px', borderRadius: '8px', boxShadow: '0 2px 4px rgba(0, 0, 0, 0.3)' }}>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { generateSm2KeyPair, sm2Encrypt, sm2Decrypt } from '../../utils/algorithm/sm2'
|
||||
import { sm2GenerateKeyPair, sm2Encrypt, sm2Decrypt } from '../../utils/algorithm/sm2'
|
||||
|
||||
// UTF8 转 Hex
|
||||
const utf8ToHex = (utf8: string): string => {
|
||||
@ -8,7 +8,7 @@ const utf8ToHex = (utf8: string): string => {
|
||||
|
||||
describe('SM2 算法测试', () => {
|
||||
test('生成密钥对', () => {
|
||||
const { privateKey, publicKey } = generateSm2KeyPair()
|
||||
const { privateKey, publicKey } = sm2GenerateKeyPair()
|
||||
expect(privateKey).toBeTruthy()
|
||||
expect(publicKey).toBeTruthy()
|
||||
expect(privateKey.length).toBeGreaterThan(0)
|
||||
@ -16,7 +16,7 @@ describe('SM2 算法测试', () => {
|
||||
})
|
||||
|
||||
test('加密和解密', () => {
|
||||
const { privateKey, publicKey } = generateSm2KeyPair()
|
||||
const { privateKey, publicKey } = sm2GenerateKeyPair()
|
||||
const originalMessage = '测试消息'
|
||||
const messageHex = utf8ToHex(originalMessage)
|
||||
|
||||
|
||||
288
src/__tests__/algorithm/sm2_test.js
Normal file
288
src/__tests__/algorithm/sm2_test.js
Normal file
@ -0,0 +1,288 @@
|
||||
const test = require('tape')
|
||||
const rs = require('jsrsasign')
|
||||
const sm2 = require('../src/sm2')
|
||||
const util = require('../src/util')
|
||||
|
||||
const publicKeyPemFromAliKmsForSign = `-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAERrsLH25zLm2LIo6tivZM9afLprSX
|
||||
6TCKAmQJArAO7VOtZyW4PQwfaTsUIF7IXEFG4iI8bNuTQwMykUzLu2ypEA==
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
const sm2PKIXPublicKeyHex = '3059301306072a8648ce3d020106082a811ccf5501822d03420004ef7db908af06082ef4a30e0ec28623371c106a53296a7b0e1a9b5717bd9cb81beb20d094aba685fd0f6a7ecc007ccf797ba634476326723b303d9dec873f440b'
|
||||
|
||||
const signatureHex = '30450220757984e0a063394ee0792b52172dd4273c05e2a66d734ff804a37b9ac639c098022100d9739a8d7a37fc88a1b4210998da489ad5b0dee1c8cb9097e532318aded5d204'
|
||||
|
||||
const csrFromAli = `-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBYjCCAQkCAQAwRzELMAkGA1UEBhMCQ04xEzARBgNVBAMMCkNhcmdvU21hcnQx
|
||||
DzANBgNVBAcMBlpodWhhaTESMBAGA1UECAwJR3Vhbmdkb25nMFkwEwYHKoZIzj0C
|
||||
AQYIKoEcz1UBgi0DQgAERrsLH25zLm2LIo6tivZM9afLprSX6TCKAmQJArAO7VOt
|
||||
ZyW4PQwfaTsUIF7IXEFG4iI8bNuTQwMykUzLu2ypEKBgMC4GCSqGSIb3DQEJDjEh
|
||||
MB8wHQYDVR0OBBYEFA3FO8vT+8qZBfGZa2TRhLRbme+9MC4GCSqGSIb3DQEJDjEh
|
||||
MB8wHQYDVR0RBBYwFIESZW1tYW4uc3VuQGlxYXguY29tMAoGCCqBHM9VAYN1A0cA
|
||||
MEQCIBQx6yv3rzfWCkKqDZQOfNKESQc6NtpQbeVvcxfBrciwAiAj78kkrF5R3g4l
|
||||
bxIHjKZHc2sztHCXe7cseWGiLq0syg==
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
`
|
||||
// CA was from https://www.gmcert.org/
|
||||
const CA_CERT = `-----BEGIN CERTIFICATE-----
|
||||
MIICIzCCAcigAwIBAgIJAKun/ZLoSXfeMAoGCCqBHM9VAYN1MGcxCzAJBgNVBAYT
|
||||
AkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdIYWlEaWFuMRMwEQYDVQQK
|
||||
DApHTUNlcnQub3JnMR8wHQYDVQQDDBZHTUNlcnQgR00gUm9vdCBDQSAtIDAxMB4X
|
||||
DTE5MTAyNDEyMzEzM1oXDTM5MDcxMTEyMzEzM1owZzELMAkGA1UEBhMCQ04xEDAO
|
||||
BgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0hhaURpYW4xEzARBgNVBAoMCkdNQ2Vy
|
||||
dC5vcmcxHzAdBgNVBAMMFkdNQ2VydCBHTSBSb290IENBIC0gMDEwWTATBgcqhkjO
|
||||
PQIBBggqgRzPVQGCLQNCAASXWWtv+ifV7dJHqPNXwcmioh/48Wg3IuI+o11nLEOD
|
||||
zljxL2yMxoQM6xfNJHuqadXXNZv3D2rml5Pk0W/tmfHEo10wWzAdBgNVHQ4EFgQU
|
||||
f1peOwCEWSoPmL6hDm85lUMQTQcwHwYDVR0jBBgwFoAUf1peOwCEWSoPmL6hDm85
|
||||
lUMQTQcwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwCgYIKoEcz1UBg3UDSQAw
|
||||
RgIhAJ7AZAC0i+4OyfxDuvPIg0I7ZtqL2kII2f1syaIW4C6iAiEAlHuUu0TMrOAr
|
||||
sU47scL1B9BhyEh5tbEjsKLHia3K0YU=
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
// cert was generated from https://www.gmcert.org/
|
||||
const cert = `-----BEGIN CERTIFICATE-----
|
||||
MIICDTCCAbOgAwIBAgIJAOWoGwJCnVw5MAoGCCqBHM9VAYN1MGcxCzAJBgNVBAYT
|
||||
AkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdIYWlEaWFuMRMwEQYDVQQK
|
||||
DApHTUNlcnQub3JnMR8wHQYDVQQDDBZHTUNlcnQgR00gUm9vdCBDQSAtIDAxMB4X
|
||||
DTIxMDIyNDA3NTgxMloXDTIyMDIyNDA3NTgxMlowIjELMAkGA1UEBhMCQ04xEzAR
|
||||
BgNVBAMMCkNhcmdvU21hcnQwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAATi93H1
|
||||
6+sN4/e6ksqPb/yAaR5/ewgO0PVAtAqMXV3IIZsug/VgFrduCzE71PKHHKKrY3MA
|
||||
d1pP8ozvDIGpoYJ8o4GMMIGJMAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgeAMCwG
|
||||
CWCGSAGG+EIBDQQfFh1HTUNlcnQub3JnIFNpZ25lZCBDZXJ0aWZpY2F0ZTAdBgNV
|
||||
HQ4EFgQUPY0wMfEXn8wNhQTy7bL/dNJcA1UwHwYDVR0jBBgwFoAUf1peOwCEWSoP
|
||||
mL6hDm85lUMQTQcwCgYIKoEcz1UBg3UDSAAwRQIgQsJ/kjgsc5cDavOvLvAOn2c9
|
||||
u1EHM5QIWn58/xlMu1gCIQDk7Kp4A/c+W2lr93yFHiTPxwtKIz/nwtH4GRAcxeiM
|
||||
iA==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
const sm2PrivateKeyEncryptedPKCS8 = `
|
||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIH2MGEGCSqGSIb3DQEFDTBUMDQGCSqGSIb3DQEFDDAnBBDa6ckWJNP3QBD7MIF8
|
||||
4nVqAgEQAgEQMA0GCSqBHM9VAYMRAgUAMBwGCCqBHM9VAWgCBBDMUgr+5Y/XN2g9
|
||||
mPGiISzGBIGQytwK98/ET4WrS0H7AsUri6FTqztrzAvgzFl3+s9AsaYtUlzE3EzE
|
||||
x6RWxo8kpKO2yj0a/Jh9WZCD4XAcoZ9aMopiWlOdpXJr/iQlMGdirCYIoF37lHMc
|
||||
jZHNffmk4ii7NxCfjrzpiFq4clYsNMXeSEnq1tuOEur4kYcjHYSIFc9bPG656a60
|
||||
+SIJsJuPFi0f
|
||||
-----END ENCRYPTED PRIVATE KEY-----`
|
||||
|
||||
const sm2PrivateKeyPlainPKCS8 = `-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgbFoKCy7tPL7D5PEl
|
||||
K/4OKMUEoca/GZnuuwr57w+ObIWhRANCAASDVuZCpA69GNKbo1MvvZ87vujwJ8P2
|
||||
85pbovhwNp+ZiJgfXv5V0cXN9sDvKwcIR6FPf99CcqjfCcRC8wWK+Uuh
|
||||
-----END PRIVATE KEY-----`
|
||||
|
||||
const pkcs8SM2P256PrivateKeyHex = '308187020100301306072a8648ce3d020106082a811ccf5501822d046d306b0201010420b26da57ba53004ddcd387ad46a361b51b308481f2327d47fb10c5fb3a8c86b92a144034200040d5365bfdbdc564c5b0eda0a85ddbd753821a709de90efe0666ba2544766acf1100ac0484d166842011da5cd6139e53dedb99ce37cea9edf4941628066e861bf'
|
||||
|
||||
const sec1SM2PrivateKeyHex = '30770201010420857dd87970aab4328dad891c781e3b270742aa9cf5d3d3764efe77f6c3d6e33aa00a06082a811ccf5501822da14403420004ced963a5705a0490ff13dde893cbda6de61f41fcaf917a5b4007d30cdec46426bc39b9c18d15b2a68a64dc333f262e600b675856285b42296f24741ee6f562a0'
|
||||
|
||||
test('SM2 P-256 encrypt/decrypt local', function (t) {
|
||||
const ec = new rs.ECDSA({ curve: sm2.getCurveName() })
|
||||
const plainText = 'send reinforcements, we\'re going to advance'
|
||||
const expected = '73656e64207265696e666f7263656d656e74732c20776527726520676f696e6720746f20616476616e6365'
|
||||
const keypair = ec.generateKeyPairHex()
|
||||
|
||||
const ciphertext = sm2.encrypt(keypair.ecpubhex, plainText)
|
||||
const asn1text = sm2.plainCiphertext2ASN1(ciphertext)
|
||||
console.log('ciphertext=' + ciphertext)
|
||||
console.log('asn.1 ciphertext=' + asn1text)
|
||||
const ciphertext1 = sm2.asn1Ciphertext2Plain(asn1text)
|
||||
t.equal(ciphertext1, ciphertext)
|
||||
const result = sm2.decryptHex(keypair.ecprvhex, ciphertext)
|
||||
t.equal(result, expected)
|
||||
const result2 = sm2.decryptHex(keypair.ecprvhex, asn1text)
|
||||
t.equal(result2, expected)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('SM2 P-256 encrypt, output hex asn.1 ciphertext', function (t) {
|
||||
const ec = new rs.ECDSA({ curve: sm2.getCurveName() })
|
||||
const plainText = 'send reinforcements, we\'re going to advance'
|
||||
const expected = '73656e64207265696e666f7263656d656e74732c20776527726520676f696e6720746f20616476616e6365'
|
||||
const keypair = ec.generateKeyPairHex()
|
||||
const ciphertext = sm2.encrypt(keypair.ecpubhex, plainText, sm2.asn1EncrypterOptions())
|
||||
const tag = ciphertext.substring(0, 2)
|
||||
t.equal(tag, '30')
|
||||
const result = sm2.decryptHex(keypair.ecprvhex, ciphertext)
|
||||
t.equal(result, expected)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('SM2 P-256 sign/verify local', function (t) {
|
||||
const ec = new rs.ECDSA({ curve: sm2.getCurveName() })
|
||||
|
||||
const keypair = ec.generateKeyPairHex()
|
||||
|
||||
const sig1 = sm2.createSM2Signature()
|
||||
sig1.init({ curve: sm2.getCurveName(), d: keypair.ecprvhex })
|
||||
sig1.updateString('emmansun')
|
||||
const hSig = sig1.sign()
|
||||
console.log('hSig=' + hSig)
|
||||
const sig2 = sm2.createSM2Signature()
|
||||
sig2.init({ curve: sm2.getCurveName(), xy: keypair.ecpubhex })
|
||||
sig2.updateString('emmansun')
|
||||
t.true(sig2.verify(hSig))
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('SM2 P-256 sm2 specific sign/verify', function (t) {
|
||||
const ec = new rs.ECDSA({ curve: sm2.getCurveName() })
|
||||
|
||||
const keypair = ec.generateKeyPairHex()
|
||||
|
||||
const sig1 = sm2.createSM2Signature()
|
||||
sig1.init({ curve: sm2.getCurveName(), d: keypair.ecprvhex })
|
||||
|
||||
const hSig = sig1.sm2Sign('emmansun')
|
||||
const hSig1 = sig1.sm2Sign('emmansun 1')
|
||||
console.log('hSig=' + hSig)
|
||||
console.log('hSig1=' + hSig1)
|
||||
const sig2 = sm2.createSM2Signature()
|
||||
sig2.init({ curve: sm2.getCurveName(), xy: keypair.ecpubhex })
|
||||
t.true(sig2.sm2Verify(hSig, 'emmansun'))
|
||||
t.true(sig2.sm2Verify(hSig1, 'emmansun 1'))
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('NIST P-256 sign/verify local', function (t) {
|
||||
const ec = new rs.ECDSA({ curve: sm2.getCurveName() })
|
||||
|
||||
const keypair = ec.generateKeyPairHex()
|
||||
const sig1 = new sm2.Signature({ alg: 'SHA256withECDSA' })
|
||||
sig1.init({ curve: sm2.getCurveName(), d: keypair.ecprvhex })
|
||||
sig1.updateString('emmansun')
|
||||
const hSig = sig1.sign()
|
||||
console.log('hSig=' + hSig)
|
||||
const sig2 = new sm2.Signature({ alg: 'SHA256withECDSA' })
|
||||
sig2.init({ curve: sm2.getCurveName(), xy: keypair.ecpubhex })
|
||||
sig2.updateString('emmansun')
|
||||
t.true(sig2.verify(hSig))
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('SM2 parse public key pem, verify signature, both from ali KMS', function (t) {
|
||||
const sig = sm2.createSM2Signature()
|
||||
sig.init(publicKeyPemFromAliKmsForSign)
|
||||
t.equal(sig.pubKey.curveName, sm2.getCurveName())
|
||||
t.true(sig.verifyWithMessageHash('66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0', signatureHex))
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('SM2 calculate ZA', function (t) {
|
||||
const sig = sm2.createSM2Signature()
|
||||
sig.init(publicKeyPemFromAliKmsForSign)
|
||||
const za = util.toHex(sig.pubKey.calculateZA())
|
||||
t.equal(za, '17e7fc071f1418200aeead3c5118a2f18381431d92b808a3bd1ba2d8270c2914')
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('SM2 parse CSR from ALI KMS', function (t) {
|
||||
const result = rs.asn1.csr.CSRUtil.getParam(csrFromAli)
|
||||
t.equal(result.sigalg, sm2.getSignAlg())
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('SM2 gen CSR', function (t) {
|
||||
const kp = rs.KEYUTIL.generateKeypair('EC', sm2.getCurveName())
|
||||
const prvKey = kp.prvKeyObj
|
||||
const pubKey = kp.pubKeyObj
|
||||
|
||||
const csr = rs.asn1.csr.CSRUtil.newCSRPEM({
|
||||
subject: { str: '/C=US/O=TEST' },
|
||||
sbjpubkey: pubKey,
|
||||
sigalg: sm2.getSignAlg(),
|
||||
sbjprvkey: prvKey
|
||||
})
|
||||
console.log(csr)
|
||||
const result = rs.asn1.csr.CSRUtil.getParam(csr)
|
||||
console.log(JSON.stringify(result))
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('SM2 read cert', function (t) {
|
||||
const x = sm2.createX509()
|
||||
x.readCertPEM(cert)
|
||||
t.equal(x.getSignatureAlgorithmField(), sm2.getSignAlg())
|
||||
t.true(x.verifySignature(rs.KEYUTIL.getKey(CA_CERT)))
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('Parse PKCS8 encrypted SM2 private key', function (t) {
|
||||
const key = rs.KEYUTIL.getKeyFromEncryptedPKCS8PEM(sm2PrivateKeyEncryptedPKCS8, 'Password1')
|
||||
t.equal(key.curveName, 'sm2p256v1')
|
||||
t.equal(key.prvKeyHex, '6c5a0a0b2eed3cbec3e4f1252bfe0e28c504a1c6bf1999eebb0af9ef0f8e6c85')
|
||||
t.equal(key.pubKeyHex, '048356e642a40ebd18d29ba3532fbd9f3bbee8f027c3f6f39a5ba2f870369f9988981f5efe55d1c5cdf6c0ef2b070847a14f7fdf4272a8df09c442f3058af94ba1')
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('Parse PKCS8 unencrypted SM2 private key', function (t) {
|
||||
const key = rs.KEYUTIL.getKeyFromPlainPrivatePKCS8PEM(sm2PrivateKeyPlainPKCS8)
|
||||
t.equal(key.curveName, 'sm2p256v1')
|
||||
t.equal(key.prvKeyHex, '6c5a0a0b2eed3cbec3e4f1252bfe0e28c504a1c6bf1999eebb0af9ef0f8e6c85')
|
||||
t.equal(key.pubKeyHex, '048356e642a40ebd18d29ba3532fbd9f3bbee8f027c3f6f39a5ba2f870369f9988981f5efe55d1c5cdf6c0ef2b070847a14f7fdf4272a8df09c442f3058af94ba1')
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('Parse PKCS8 unencrypted SM2 private key with Signature', function (t) {
|
||||
const sig1 = sm2.createSM2Signature()
|
||||
sig1.init(sm2PrivateKeyPlainPKCS8)
|
||||
|
||||
const hSig = sig1.sm2Sign('emmansun')
|
||||
console.log('hSig=' + hSig)
|
||||
|
||||
const sig2 = sm2.createSM2Signature()
|
||||
sig2.init({ curve: sm2.getCurveName(), xy: sig1.prvKey.pubKeyHex })
|
||||
t.true(sig2.sm2Verify(hSig, 'emmansun'))
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('Parse PKCS8 encrypted SM2 private key with Signature', function (t) {
|
||||
const sig1 = sm2.createSM2Signature()
|
||||
sig1.init(sm2PrivateKeyEncryptedPKCS8, 'Password1')
|
||||
|
||||
const hSig = sig1.sm2Sign('emmansun')
|
||||
console.log('hSig=' + hSig)
|
||||
|
||||
const sig2 = sm2.createSM2Signature()
|
||||
sig2.init({ curve: sm2.getCurveName(), xy: sig1.prvKey.pubKeyHex })
|
||||
t.true(sig2.sm2Verify(hSig, 'emmansun'))
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('Parse PKCS8 unencrypted SM2 private key (hex) with Signature', function (t) {
|
||||
const sig1 = sm2.createSM2Signature()
|
||||
sig1.init(pkcs8SM2P256PrivateKeyHex, undefined, 'pkcs8prv')
|
||||
|
||||
const hSig = sig1.sm2Sign('emmansun')
|
||||
console.log('hSig=' + hSig)
|
||||
|
||||
const sig2 = sm2.createSM2Signature()
|
||||
sig2.init({ curve: sm2.getCurveName(), xy: sig1.prvKey.pubKeyHex })
|
||||
t.true(sig2.sm2Verify(hSig, 'emmansun'))
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('Parse SEC1 SM2 private key (hex) with Signature', function (t) {
|
||||
const sig1 = sm2.createSM2Signature()
|
||||
sig1.init(sec1SM2PrivateKeyHex, undefined, 'pkcs5prv')
|
||||
|
||||
const hSig = sig1.sm2Sign('emmansun')
|
||||
console.log('hSig=' + hSig)
|
||||
|
||||
const sig2 = sm2.createSM2Signature()
|
||||
sig2.init({ curve: sm2.getCurveName(), xy: sig1.prvKey.pubKeyHex })
|
||||
t.true(sig2.sm2Verify(hSig, 'emmansun'))
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('Parse SM2 PKIX public key (hex) with Signature', function (t) {
|
||||
const sig1 = sm2.createSM2Signature()
|
||||
sig1.init(sm2PKIXPublicKeyHex, undefined, 'pkcs8pub')
|
||||
t.equals(sig1.pubKey.pubKeyHex, '04ef7db908af06082ef4a30e0ec28623371c106a53296a7b0e1a9b5717bd9cb81beb20d094aba685fd0f6a7ecc007ccf797ba634476326723b303d9dec873f440b')
|
||||
t.end()
|
||||
})
|
||||
7
src/types/gmsm-sm2js.d.ts
vendored
Normal file
7
src/types/gmsm-sm2js.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
declare module 'gmsm-sm2js' {
|
||||
export function generateKeyPairHex(privateKey?: string): { privateKey: string; publicKey: string };
|
||||
export function encrypt(publicKey: string, message: string): string;
|
||||
export function decrypt(privateKey: string, ciphertext: string): string;
|
||||
export function sign(privateKey: string, message: string): string;
|
||||
export function verify(publicKey: string, message: string, signature: string): boolean;
|
||||
}
|
||||
23
src/types/sm-crypto.d.ts
vendored
Normal file
23
src/types/sm-crypto.d.ts
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
declare module 'sm-crypto' {
|
||||
export interface SM2KeyPair {
|
||||
privateKey: string;
|
||||
publicKey: string;
|
||||
}
|
||||
|
||||
export const sm2: {
|
||||
generateKeyPairHex: (privateKey?: string) => SM2KeyPair;
|
||||
doEncrypt: (message: string, publicKey: string) => string;
|
||||
doDecrypt: (ciphertext: string, privateKey: string) => string;
|
||||
doSign: (message: string, privateKey: string) => string;
|
||||
doVerify: (message: string, signature: string, publicKey: string) => boolean;
|
||||
};
|
||||
|
||||
export const sm3: {
|
||||
digest: (message: string) => string;
|
||||
};
|
||||
|
||||
export const sm4: {
|
||||
encrypt: (message: string, key: string, options?: any) => string;
|
||||
decrypt: (ciphertext: string, key: string, options?: any) => string;
|
||||
};
|
||||
}
|
||||
@ -1,16 +1,29 @@
|
||||
import { SM2 } from 'gm-crypto'
|
||||
import { isValidHex, hexToArrayBuffer } from '../convert/hex'
|
||||
|
||||
// 验证是否为有效的 hex 字符串
|
||||
const isValidHex = (hex: string): boolean => {
|
||||
return /^[0-9a-fA-F]*$/.test(hex) && hex.length % 2 === 0
|
||||
// 导入 sm-crypto 库,它在浏览器环境中可用
|
||||
import { sm2 } from 'sm-crypto'
|
||||
|
||||
// SM2 密钥对生成
|
||||
export const sm2GenerateKeyPair = (): { privateKey: string; publicKey: string } => {
|
||||
const keypair = sm2.generateKeyPairHex()
|
||||
return {
|
||||
privateKey: keypair.privateKey,
|
||||
publicKey: keypair.publicKey
|
||||
}
|
||||
}
|
||||
|
||||
// 生成 SM2 密钥对
|
||||
export const generateSm2KeyPair = () => {
|
||||
return SM2.generateKeyPair()
|
||||
// SM2 从私钥计算公钥
|
||||
export const sm2GetPublicKeyFromPrivateKey = (privateKey: string): string => {
|
||||
// 验证私钥是否为有效的 hex 字符串
|
||||
if (!isValidHex(privateKey)) {
|
||||
throw new Error('Invalid private key: must be hex string')
|
||||
}
|
||||
|
||||
const keypair = sm2.generateKeyPairHex(privateKey)
|
||||
return keypair.publicKey
|
||||
}
|
||||
|
||||
// SM2 公钥加密
|
||||
// SM2 加密
|
||||
export const sm2Encrypt = (message: string, publicKey: string): string => {
|
||||
// 验证消息是否为有效的 hex 字符串
|
||||
if (!isValidHex(message)) {
|
||||
@ -21,23 +34,78 @@ export const sm2Encrypt = (message: string, publicKey: string): string => {
|
||||
throw new Error('Invalid public key: must be hex string')
|
||||
}
|
||||
|
||||
// 使用 hex 输入模式加密
|
||||
const encrypted = SM2.encrypt(message, publicKey, { inputEncoding: 'hex' })
|
||||
return typeof encrypted === 'string' ? encrypted : Buffer.from(encrypted).toString('hex')
|
||||
// 将 hex 字符串转换为字节数组
|
||||
const messageBytes = new Uint8Array(hexToArrayBuffer(message))
|
||||
// 将字节数组转换为字符串
|
||||
const messageString = new TextDecoder().decode(messageBytes)
|
||||
|
||||
// 加密
|
||||
const encrypted = sm2.doEncrypt(messageString, publicKey)
|
||||
return encrypted
|
||||
}
|
||||
|
||||
// SM2 私钥解密
|
||||
export const sm2Decrypt = (encrypted: string, privateKey: string): string => {
|
||||
// 验证加密数据是否为有效的 hex 字符串
|
||||
if (!isValidHex(encrypted)) {
|
||||
throw new Error('Invalid encrypted data: must be hex string')
|
||||
// SM2 解密
|
||||
export const sm2Decrypt = (ciphertext: string, privateKey: string): string => {
|
||||
// 验证密文是否为有效的 hex 字符串
|
||||
if (!isValidHex(ciphertext)) {
|
||||
throw new Error('Invalid ciphertext: must be hex string')
|
||||
}
|
||||
// 验证私钥是否为有效的 hex 字符串
|
||||
if (!isValidHex(privateKey)) {
|
||||
throw new Error('Invalid private key: must be hex string')
|
||||
}
|
||||
|
||||
// 使用 hex 输入模式解密
|
||||
const decrypted = SM2.decrypt(encrypted, privateKey, { inputEncoding: 'hex' })
|
||||
return typeof decrypted === 'string' ? decrypted : new TextDecoder('utf-8').decode(decrypted)
|
||||
// 解密
|
||||
const decrypted = sm2.doDecrypt(ciphertext, privateKey)
|
||||
// 将解密结果转换为 hex 字符串
|
||||
const decryptedBytes = new TextEncoder().encode(decrypted)
|
||||
return Array.from(decryptedBytes)
|
||||
.map(byte => byte.toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
}
|
||||
|
||||
// SM2 签名
|
||||
export const sm2Sign = (message: string, privateKey: string): string => {
|
||||
// 验证消息是否为有效的 hex 字符串
|
||||
if (!isValidHex(message)) {
|
||||
throw new Error('Invalid message: must be hex string')
|
||||
}
|
||||
// 验证私钥是否为有效的 hex 字符串
|
||||
if (!isValidHex(privateKey)) {
|
||||
throw new Error('Invalid private key: must be hex string')
|
||||
}
|
||||
|
||||
// 将 hex 字符串转换为字节数组
|
||||
const messageBytes = new Uint8Array(hexToArrayBuffer(message))
|
||||
// 将字节数组转换为字符串
|
||||
const messageString = new TextDecoder().decode(messageBytes)
|
||||
|
||||
// 签名
|
||||
const signature = sm2.doSign(messageString, privateKey)
|
||||
return signature
|
||||
}
|
||||
|
||||
// SM2 验证签名
|
||||
export const sm2Verify = (message: string, signature: string, publicKey: string): boolean => {
|
||||
// 验证消息是否为有效的 hex 字符串
|
||||
if (!isValidHex(message)) {
|
||||
throw new Error('Invalid message: must be hex string')
|
||||
}
|
||||
// 验证签名是否为有效的 hex 字符串
|
||||
if (!isValidHex(signature)) {
|
||||
throw new Error('Invalid signature: must be hex string')
|
||||
}
|
||||
// 验证公钥是否为有效的 hex 字符串
|
||||
if (!isValidHex(publicKey)) {
|
||||
throw new Error('Invalid public key: must be hex string')
|
||||
}
|
||||
|
||||
// 将 hex 字符串转换为字节数组
|
||||
const messageBytes = new Uint8Array(hexToArrayBuffer(message))
|
||||
// 将字节数组转换为字符串
|
||||
const messageString = new TextDecoder().decode(messageBytes)
|
||||
|
||||
// 验证签名
|
||||
const result = sm2.doVerify(messageString, signature, publicKey)
|
||||
return result
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
export const VERSION = "V1.0-20260417024927";
|
||||
export const VERSION = "V1.0-20260418023820";
|
||||
@ -4,5 +4,11 @@ import react from '@vitejs/plugin-react'
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
base: './'
|
||||
resolve: {
|
||||
alias: {
|
||||
crypto: 'crypto-browserify',
|
||||
stream: 'stream-browserify',
|
||||
buffer: 'buffer'
|
||||
}
|
||||
}
|
||||
})
|
||||
Loading…
Reference in New Issue
Block a user