Merge branch 'main' of http://git.honor3.com/Fullstack/seckit
All checks were successful
Build and Deploy / build (push) Successful in 2m40s
All checks were successful
Build and Deploy / build (push) Successful in 2m40s
This commit is contained in:
commit
ab9441982f
1
package-lock.json
generated
1
package-lock.json
generated
@ -16,6 +16,7 @@
|
|||||||
"gmsm-sm2js": "^0.7.1",
|
"gmsm-sm2js": "^0.7.1",
|
||||||
"gmsm-sm3js": "^0.2.0",
|
"gmsm-sm3js": "^0.2.0",
|
||||||
"gmsm-sm4js": "^0.7.0",
|
"gmsm-sm4js": "^0.7.0",
|
||||||
|
"jsrsasign": "^11.1.3",
|
||||||
"node-forge": "^1.4.0",
|
"node-forge": "^1.4.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
"gmsm-sm2js": "^0.7.1",
|
"gmsm-sm2js": "^0.7.1",
|
||||||
"gmsm-sm3js": "^0.2.0",
|
"gmsm-sm3js": "^0.2.0",
|
||||||
"gmsm-sm4js": "^0.7.0",
|
"gmsm-sm4js": "^0.7.0",
|
||||||
|
"jsrsasign": "^11.1.3",
|
||||||
"node-forge": "^1.4.0",
|
"node-forge": "^1.4.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|||||||
180
src/App.tsx
180
src/App.tsx
@ -6,6 +6,7 @@ import { sm4Encrypt, sm4Decrypt } from './utils/algorithm/sm4'
|
|||||||
import { sm2GenerateKeyPair, sm2GetPublicKeyFromPrivateKey, sm2Encrypt, sm2Decrypt, sm2Sign, sm2Verify, sm2DerToC1C3C2, sm2C1C3C2ToDer } 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 { parseSm2Cert, parseCertFromFile, generateRootCert, generateAppCert } from './utils/algorithm/cert'
|
||||||
import { hexToUtf8, utf8ToHex, hexToBase64, base64ToHex } from './utils/convert'
|
import { hexToUtf8, utf8ToHex, hexToBase64, base64ToHex } from './utils/convert'
|
||||||
|
import { getInfoFromPKCS7, verifyPKCS7 } from './utils/crypto.js'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
// 从localStorage读取当前分类,默认sm3
|
// 从localStorage读取当前分类,默认sm3
|
||||||
@ -182,6 +183,12 @@ function App() {
|
|||||||
const [commonName, setCommonName] = useState<string>('test.example.com')
|
const [commonName, setCommonName] = useState<string>('test.example.com')
|
||||||
const [certError, setCertError] = useState<string>('')
|
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>('')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// SM3 计算
|
// SM3 计算
|
||||||
@ -340,6 +347,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 生成密钥对
|
// SM2 生成密钥对
|
||||||
const handleGenerateSm2KeyPair = () => {
|
const handleGenerateSm2KeyPair = () => {
|
||||||
try {
|
try {
|
||||||
@ -1822,6 +1853,155 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
19
src/utils/crypto.d.ts
vendored
Normal file
19
src/utils/crypto.d.ts
vendored
Normal 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
354
src/utils/crypto.js
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1 +1 @@
|
|||||||
export const VERSION = "V1.0-20260428031400";
|
export const VERSION = "V1.0-20260428031400"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user