把 kit 中的证书模块拿过来了。
All checks were successful
Build and Deploy / build (push) Successful in 2m14s

This commit is contained in:
cheney 2026-04-26 11:55:15 +08:00
parent 32626c84a6
commit 045e939dcd
6 changed files with 556 additions and 1 deletions

1
package-lock.json generated
View File

@ -16,6 +16,7 @@
"gmsm-sm2js": "^0.7.1",
"gmsm-sm3js": "^0.2.0",
"gmsm-sm4js": "^0.7.0",
"jsrsasign": "^11.1.3",
"node-forge": "^1.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@ -19,6 +19,7 @@
"gmsm-sm2js": "^0.7.1",
"gmsm-sm3js": "^0.2.0",
"gmsm-sm4js": "^0.7.0",
"jsrsasign": "^11.1.3",
"node-forge": "^1.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@ -6,6 +6,7 @@ import { sm4Encrypt, sm4Decrypt } from './utils/algorithm/sm4'
import { sm2GenerateKeyPair, sm2GetPublicKeyFromPrivateKey, sm2Encrypt, sm2Decrypt, sm2Sign, sm2Verify } from './utils/algorithm/sm2'
import { parseSm2Cert, parseCertFromFile, generateRootCert, generateAppCert } from './utils/algorithm/cert'
import { hexToUtf8, utf8ToHex, hexToBase64, base64ToHex } from './utils/convert'
import { getInfoFromPKCS7, verifyPKCS7 } from './utils/crypto.js'
function App() {
// 从localStorage读取当前分类默认sm3
@ -179,6 +180,12 @@ function App() {
const [appCert, setAppCert] = useState<string>('')
const [commonName, setCommonName] = useState<string>('test.example.com')
const [certError, setCertError] = useState<string>('')
// PKCS7 签名工具状态
const [pkcs7Signature, setPkcs7Signature] = useState<string>('')
const [pkcs7Info, setPkcs7Info] = useState<any>(null)
const [originalData, setOriginalData] = useState<string>('')
const [verifyResult, setVerifyResult] = useState<string>('')
@ -318,6 +325,30 @@ function App() {
}
}
// 处理 PKCS7 签名信息提取
const handleGetInfoFromPKCS7 = () => {
try {
const info = getInfoFromPKCS7(pkcs7Signature)
setPkcs7Info(info)
setCertError('')
} catch (error) {
setCertError((error as Error).message)
setPkcs7Info(null)
}
}
// 处理 PKCS7 签名验证
const handleVerifyPKCS7 = () => {
try {
const result = verifyPKCS7(pkcs7Signature, originalData)
setVerifyResult(result ? '验证成功' : '验证失败')
setCertError('')
} catch (error) {
setCertError((error as Error).message)
setVerifyResult('')
}
}
// SM2 生成密钥对
const handleGenerateSm2KeyPair = () => {
try {
@ -1703,6 +1734,155 @@ function App() {
</div>
)}
</div>
{/* PKCS7 签名工具 */}
<div style={{ backgroundColor: '#2d2d2d', padding: '15px', borderRadius: '8px', marginTop: '20px' }}>
<h3 style={{ color: '#f59e0b', marginBottom: '15px' }}>4. PKCS7 </h3>
<div className="form-group" style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}>PKCS7 </label>
<textarea
value={pkcs7Signature}
onChange={(e) => setPkcs7Signature(e.target.value)}
onContextMenu={(e) => openMenu(e, pkcs7Signature, setPkcs7Signature, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, pkcs7Signature, setPkcs7Signature, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
placeholder="输入 PKCS7 签名数据 (PEM 或 Base64 格式)"
style={{
width: '100%',
padding: '10px',
border: '1px solid #333',
borderRadius: '4px',
backgroundColor: '#3d3d3d',
color: '#e0e0e0',
fontSize: '14px',
resize: 'vertical',
minHeight: '150px'
}}
/>
</div>
<div className="form-group" style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0' }}> detech </label>
<textarea
value={originalData}
onChange={(e) => setOriginalData(e.target.value)}
onContextMenu={(e) => openMenu(e, originalData, setOriginalData, false)}
onTouchStart={(e) => {
const touchTimer = setTimeout(() => {
openMenu(e, originalData, setOriginalData, false)
}, 500)
return () => clearTimeout(touchTimer)
}}
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', marginBottom: '20px' }}>
<button
onClick={handleGetInfoFromPKCS7}
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={handleVerifyPKCS7}
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>
{verifyResult && (
<div style={{ marginBottom: '20px', padding: '10px', borderRadius: '4px', backgroundColor: verifyResult === '验证成功' ? '#166534' : '#7f1d1d' }}>
<div style={{ color: verifyResult === '验证成功' ? '#dcfce7' : '#fecaca', fontWeight: 'bold' }}>
: {verifyResult}
</div>
</div>
)}
{pkcs7Info && (
<div style={{ backgroundColor: '#3d3d3d', padding: '15px', borderRadius: '8px' }}>
<h4 style={{ color: '#f59e0b', marginBottom: '15px' }}>PKCS7 </h4>
{pkcs7Info.cert && (
<div style={{ marginBottom: '15px' }}>
<h5 style={{ color: '#e0e0e0', marginBottom: '10px' }}></h5>
<div style={{ marginBottom: '10px' }}>
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0', fontWeight: 'bold' }}> DN:</label>
<div style={{ color: '#e0e0e0' }}>{pkcs7Info.cert.subjectDN}</div>
</div>
<div style={{ marginBottom: '10px' }}>
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0', fontWeight: 'bold' }}> DN:</label>
<div style={{ color: '#e0e0e0' }}>{pkcs7Info.cert.issuerDN}</div>
</div>
<div style={{ marginBottom: '10px' }}>
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0', fontWeight: 'bold' }}>:</label>
<div style={{ color: '#e0e0e0', wordBreak: 'break-all' }}>{pkcs7Info.cert.serialNumber}</div>
</div>
<div style={{ marginBottom: '10px' }}>
<label style={{ display: 'block', marginBottom: '5px', color: '#e0e0e0', fontWeight: 'bold' }}>:</label>
<div style={{ color: '#e0e0e0' }}>
<div>: {pkcs7Info.cert.notBefore}</div>
<div>: {pkcs7Info.cert.notAfter}</div>
</div>
</div>
</div>
)}
{pkcs7Info.publicKey && (
<div style={{ marginBottom: '15px' }}>
<h5 style={{ color: '#e0e0e0', marginBottom: '10px' }}></h5>
<div style={{ color: '#e0e0e0', wordBreak: 'break-all', whiteSpace: 'pre-wrap' }}>{pkcs7Info.publicKey}</div>
</div>
)}
{pkcs7Info.signature && (
<div style={{ marginBottom: '15px' }}>
<h5 style={{ color: '#e0e0e0', marginBottom: '10px' }}></h5>
<div style={{ color: '#e0e0e0', wordBreak: 'break-all' }}>{pkcs7Info.signature}</div>
</div>
)}
</div>
)}
</div>
</div>
)}

19
src/utils/crypto.d.ts vendored Normal file
View File

@ -0,0 +1,19 @@
export function sm4en3(key: string, data: string): string;
export function sm4de3(key: string, data: string): string;
export function getInfoFromPKCS7(p7sign: Buffer | string): {
cert: {
subject: Array<{ name: string; value: string }>;
subjectDN: string;
issuer: Array<{ name: string; value: string }>;
issuerDN: string;
serialNumber: string;
notBefore: string;
notAfter: string;
pem: string;
} | null;
publicKey: string | null;
publicKeyHex: string | null;
signature: string | null;
signatureHex: string | null;
};
export function verifyPKCS7(p7sign: Buffer | string, originalData?: Buffer | string): boolean;

354
src/utils/crypto.js Normal file
View File

@ -0,0 +1,354 @@
import sm4 from 'sm-crypto'
import sm3 from 'sm-crypto'
import sm2 from 'sm-crypto'
import forge from 'node-forge'
import { KJUR, ASN1HEX, X509,pemtohex } from 'jsrsasign'
export function sm4en3(key, data) {
let sm3key = sm3(key)
sm3key = sm3key.substring(0, 32)
let encryptData = sm4.encrypt(data, sm3key)
return encryptData
}
export function sm4de3(key, data) {
let sm3key = sm3(key)
sm3key = sm3key.substring(0, 32)
let decryptData = sm4.decrypt(data, sm3key)
return decryptData
}
export function getInfoFromPKCS7(p7sign) {
let base64Data
if (Buffer.isBuffer(p7sign)) {
base64Data = p7sign.toString('base64')
} else if (typeof p7sign === 'string') {
if (p7sign.includes('-----BEGIN PKCS7-----')) {
base64Data = p7sign
.replace(/-----BEGIN PKCS7-----\n?/, '')
.replace(/-----END PKCS7-----\n?/, '')
.replace(/\n/g, '')
} else {
base64Data = p7sign
}
} else {
throw new Error('Invalid input type for p7sign')
}
try {
const pemData = `-----BEGIN PKCS7-----\n${base64Data}\n-----END PKCS7-----`
const p7 = forge.pkcs7.messageFromPem(pemData)
return extractInfoFromForgeP7(p7)
} catch (forgeError) {
try {
return extractInfoFromJsrsasignP7(base64Data)
} catch (jsrsasignError) {
throw new Error(`Failed to parse PKCS7: ${forgeError.message}, ${jsrsasignError.message}`)
}
}
}
function extractInfoFromForgeP7(p7) {
function attrsToDN(attrs) {
if (!attrs || attrs.length === 0) return ''
return attrs.map(attr => `${attr.name}=${attr.value}`).join(', ')
}
let cert = null
if (p7.certificates && p7.certificates.length > 0) {
const certObj = p7.certificates[0]
cert = {
subject: certObj.subject.attributes.map(attr => ({
name: attr.name,
value: attr.value
})),
subjectDN: attrsToDN(certObj.subject.attributes),
issuer: certObj.issuer.attributes.map(attr => ({
name: attr.name,
value: attr.value
})),
issuerDN: attrsToDN(certObj.issuer.attributes),
serialNumber: certObj.serialNumber,
notBefore: certObj.validity.notBefore,
notAfter: certObj.validity.notAfter,
pem: forge.pki.certificateToPem(certObj)
}
}
let publicKey = null
let publicKeyHex = null
if (p7.certificates && p7.certificates.length > 0) {
const certObj = p7.certificates[0]
if (certObj.publicKey) {
publicKey = forge.pki.publicKeyToPem(certObj.publicKey)
const publicKeyAsn1 = forge.pki.publicKeyToAsn1(certObj.publicKey)
const publicKeyDer = forge.asn1.toDer(publicKeyAsn1)
publicKeyHex = forge.util.bytesToHex(publicKeyDer.getBytes())
}
}
let signature = null
let signatureHex = null
if (p7.rawCapture && p7.rawCapture.signature) {
signature = forge.util.encode64(p7.rawCapture.signature)
signatureHex = forge.util.bytesToHex(p7.rawCapture.signature)
}
return {
cert,
publicKey,
publicKeyHex,
signature,
signatureHex
}
}
function extractInfoFromJsrsasignP7(base64Data) {
const parser = new KJUR.asn1.cms.CMSParser()
const p7 = parser.getCMSSignedData(ASN1HEX.b64tohex(base64Data))
let cert = null
let publicKey = null
let publicKeyHex = null
const certs = p7.certs && p7.certs.array ? p7.certs.array : []
if (certs.length > 0) {
const certPem = certs[0]
const x509 = new X509()
x509.readCertPEM(certPem)
const subjectStr = x509.getSubjectString()
const issuerStr = x509.getIssuerString()
function parseDNString(dnStr) {
if (!dnStr || dnStr === '/') return []
const parts = dnStr.substring(1).split('/')
return parts.map(part => {
const [name, value] = part.split('=')
return { name, value }
})
}
const subjectAttrs = parseDNString(subjectStr)
const issuerAttrs = parseDNString(issuerStr)
function attrsToDN(attrs) {
if (!attrs || attrs.length === 0) return ''
return attrs.map(attr => `${attr.name}=${attr.value}`).join(', ')
}
cert = {
subject: subjectAttrs,
subjectDN: attrsToDN(subjectAttrs),
issuer: issuerAttrs,
issuerDN: attrsToDN(issuerAttrs),
serialNumber: x509.getSerialNumberHex(),
notBefore: x509.getNotBefore(),
notAfter: x509.getNotAfter(),
pem: certPem
}
try {
const certHex = pemtohex(certPem)
const tbsCert = ASN1HEX.getTLVbyList(certHex, 0, [0])
const spki = ASN1HEX.getTLVbyList(tbsCert, 0, [6])
const publicKeyBitString = ASN1HEX.getTLVbyList(spki, 0, [1])
const publicKeyHexContent = publicKeyBitString.substring(6)
const algorithmSeq = ASN1HEX.getTLVbyList(spki, 0, [0])
const oidTlv = ASN1HEX.getTLVbyList(algorithmSeq, 0, [0])
const oidHex = oidTlv.substring(4)
const isSM2 = oidHex.includes('2A811CCF55') || oidHex.includes('2a811ccf55')
publicKey = KJUR.asn1.ASN1Util.getPEMStringFromHex(spki, 'PUBLIC KEY')
publicKeyHex = spki
} catch (e) {
console.error('提取 SM2 公钥错误:', e.message)
publicKey = null
publicKeyHex = null
}
}
let signature = null
let signatureHex = null
if (p7.sinfos && p7.sinfos.length > 0) {
const signer = p7.sinfos[0]
if (signer.sighex) {
signatureHex = signer.sighex
signature = ASN1HEX.hextob64(signer.sighex)
}
}
return {
cert,
publicKey,
publicKeyHex,
signature,
signatureHex
}
}
export function verifyPKCS7(p7sign, originalData) {
let base64Data
if (Buffer.isBuffer(p7sign)) {
base64Data = p7sign.toString('base64')
} else if (typeof p7sign === 'string') {
if (p7sign.includes('-----BEGIN PKCS7-----')) {
base64Data = p7sign
.replace(/-----BEGIN PKCS7-----\n?/, '')
.replace(/-----END PKCS7-----\n?/, '')
.replace(/\n/g, '')
} else {
base64Data = p7sign
}
} else {
throw new Error('Invalid input type for p7sign')
}
let verifyData
if (originalData) {
if (Buffer.isBuffer(originalData)) {
verifyData = originalData
} else if (typeof originalData === 'string') {
verifyData = Buffer.from(originalData)
} else {
throw new Error('Invalid input type for originalData')
}
}
try {
const pemData = `-----BEGIN PKCS7-----\n${base64Data}\n-----END PKCS7-----`
const p7 = forge.pkcs7.messageFromPem(pemData)
if (!verifyData) {
if (p7.content) {
verifyData = Buffer.from(p7.content.getBytes(), 'binary')
} else {
throw new Error('No content found in PKCS7 signature for attach mode')
}
}
if (p7.certificates && p7.certificates.length > 0) {
const publicKey = p7.certificates[0].publicKey
if (publicKey && p7.rawCapture && p7.rawCapture.signature) {
const md = forge.md.sha256.create()
md.update(verifyData.toString(), 'utf8')
const digest = md.digest().getBytes()
return publicKey.verify(digest, p7.rawCapture.signature)
}
}
return p7.verify()
} catch (forgeError) {
try {
return verifyPKCS7WithJsrsasign(base64Data, verifyData)
} catch (jsrsasignError) {
return false
}
}
}
function verifyPKCS7WithJsrsasign(base64Data, verifyData) {
const parser = new KJUR.asn1.cms.CMSParser()
const p7 = parser.getCMSSignedData(ASN1HEX.b64tohex(base64Data))
if (!verifyData) {
if (p7.econtent && p7.econtent.hex) {
verifyData = Buffer.from(p7.econtent.hex, 'hex')
} else {
throw new Error('No content found in PKCS7 signature for attach mode')
}
}
if (!p7.sinfos || p7.sinfos.length === 0) {
throw new Error('No signer info found in PKCS7')
}
const signer = p7.sinfos[0]
if (!p7.certs || !p7.certs.array || p7.certs.array.length === 0) {
throw new Error('No certificate found in PKCS7')
}
const certPem = p7.certs.array[0]
const x509 = new X509()
x509.readCertPEM(certPem)
const sigAlg = signer.sigalg
if (sigAlg === '1.2.156.10197.1.301.1' || sigAlg === '1.2.156.10197.1.501' || sigAlg.includes('sm2')) {
const certHex = pemtohex(certPem)
const tbsCert = ASN1HEX.getTLVbyList(certHex, 0, [0])
const spki = ASN1HEX.getTLVbyList(tbsCert, 0, [6])
const publicKeyBitString = ASN1HEX.getTLVbyList(spki, 0, [1])
let publicKeyHex = publicKeyBitString.substring(6)
if (!publicKeyHex.startsWith('04')) {
publicKeyHex = '04' + publicKeyHex
}
const hash = sm3(verifyData.toString())
let signatureHex = signer.sighex
const userId = '1234567812345678'
try {
const result1 = sm2.doVerifySignature(verifyData.toString(), signatureHex, publicKeyHex, { der: true, hash: true, userId })
if (result1) return true
const result2 = sm2.doVerifySignature(verifyData.toString(), signatureHex, publicKeyHex, { der: true, hash: true })
if (result2) return true
const result3 = sm2.doVerifySignature(verifyData.toString(), signatureHex, publicKeyHex, { der: true })
if (result3) return true
const result4 = sm2.doVerifySignature(hash, signatureHex, publicKeyHex, { der: true, hash: true, userId })
if (result4) return true
const result5 = sm2.doVerifySignature(hash, signatureHex, publicKeyHex, { der: true, hash: true })
if (result5) return true
const result6 = sm2.doVerifySignature(hash, signatureHex, publicKeyHex, { der: true })
if (result6) return true
const result7 = sm2.doVerifySignature(verifyData.toString(), signatureHex, publicKeyHex)
if (result7) return true
const result8 = sm2.doVerifySignature(verifyData.toString(), signatureHex, publicKeyHex, { hash: true, userId })
if (result8) return true
if (signatureHex.startsWith('30')) {
try {
const rStart = 8
const r = signatureHex.substring(rStart, rStart + 64)
const sStart = rStart + 64 + 4
const s = signatureHex.substring(sStart, sStart + 64)
const rawSignature = r + s
const result9 = sm2.doVerifySignature(verifyData.toString(), rawSignature, publicKeyHex)
if (result9) return true
const result10 = sm2.doVerifySignature(verifyData.toString(), rawSignature, publicKeyHex, { hash: true, userId })
if (result10) return true
} catch (e) {
}
}
} catch (e) {
}
return false
} else {
try {
const pubKey = x509.getPublicKey()
const sig = new KJUR.crypto.Signature({ alg: 'SHA256withRSA' })
sig.init(pubKey)
sig.updateString(verifyData.toString())
return sig.verify(signer.sighex)
} catch (e) {
throw new Error('RSA verification failed: ' + e.message)
}
}
}

View File

@ -1 +1 @@
export const VERSION = "V1.0-20260424025112";
export const VERSION = "V1.0-20260426034359";