sm2 验签通过
This commit is contained in:
+143
-7
@@ -3,6 +3,7 @@ const sm3 = require('sm-crypto').sm3
|
||||
const sm2 = require('sm-crypto').sm2
|
||||
const crypto = require('crypto')
|
||||
const forge = require('node-forge')
|
||||
const rs = require('jsrsasign')
|
||||
|
||||
|
||||
module.exports.sm4en3 = function (key, data){
|
||||
@@ -23,29 +24,50 @@ module.exports.sm4de3 = function (key, data){
|
||||
|
||||
/**
|
||||
* 从 PKCS7 签名中获取信息(包含证书、公钥和签名值)
|
||||
* 支持 RSA 和 SM2 算法
|
||||
* @param {Buffer|string} p7sign - PKCS7 签名数据
|
||||
* @returns {Object} 包含证书、公钥和签名值的对象
|
||||
*/
|
||||
module.exports.getInfoFromPKCS7 = function (p7sign) {
|
||||
// 处理输入数据
|
||||
let pemData
|
||||
let base64Data
|
||||
if (Buffer.isBuffer(p7sign)) {
|
||||
pemData = p7sign.toString('ascii')
|
||||
base64Data = p7sign.toString('base64')
|
||||
} else if (typeof p7sign === 'string') {
|
||||
// 判断是否是 PEM 格式
|
||||
if (p7sign.includes('-----BEGIN PKCS7-----')) {
|
||||
pemData = p7sign
|
||||
// 提取 base64 部分
|
||||
base64Data = p7sign
|
||||
.replace(/-----BEGIN PKCS7-----\n?/, '')
|
||||
.replace(/-----END PKCS7-----\n?/, '')
|
||||
.replace(/\n/g, '')
|
||||
} else {
|
||||
// 如果不是 PEM 格式,假设是 base64,包装成 PEM
|
||||
pemData = `-----BEGIN PKCS7-----\n${p7sign}\n-----END PKCS7-----`
|
||||
// 如果不是 PEM 格式,假设是 base64
|
||||
base64Data = p7sign
|
||||
}
|
||||
} else {
|
||||
throw new Error('Invalid input type for p7sign')
|
||||
}
|
||||
|
||||
// 解析 PKCS7 数据
|
||||
const p7 = forge.pkcs7.messageFromPem(pemData)
|
||||
// 尝试使用 node-forge 解析(支持 RSA)
|
||||
try {
|
||||
const pemData = `-----BEGIN PKCS7-----\n${base64Data}\n-----END PKCS7-----`
|
||||
const p7 = forge.pkcs7.messageFromPem(pemData)
|
||||
return extractInfoFromForgeP7(p7)
|
||||
} catch (forgeError) {
|
||||
// 如果 node-forge 解析失败,尝试使用 jsrsasign(支持 SM2)
|
||||
try {
|
||||
return extractInfoFromJsrsasignP7(base64Data)
|
||||
} catch (jsrsasignError) {
|
||||
throw new Error(`Failed to parse PKCS7: ${forgeError.message}, ${jsrsasignError.message}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 node-forge 解析的 PKCS7 中提取信息(RSA)
|
||||
*/
|
||||
function extractInfoFromForgeP7(p7) {
|
||||
// 辅助函数:将属性数组转换为 DN 字符串
|
||||
function attrsToDN(attrs) {
|
||||
if (!attrs || attrs.length === 0) return ''
|
||||
@@ -108,6 +130,120 @@ module.exports.getInfoFromPKCS7 = function (p7sign) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 jsrsasign 解析的 PKCS7 中提取信息(支持 SM2)
|
||||
*/
|
||||
function extractInfoFromJsrsasignP7(base64Data) {
|
||||
const parser = new rs.KJUR.asn1.cms.CMSParser()
|
||||
const p7 = parser.getCMSSignedData(rs.b64tohex(base64Data))
|
||||
|
||||
// 提取证书
|
||||
let cert = null
|
||||
let publicKey = null
|
||||
let publicKeyHex = null
|
||||
|
||||
// p7.certs 是一个对象,包含 array 属性
|
||||
const certs = p7.certs && p7.certs.array ? p7.certs.array : []
|
||||
|
||||
if (certs.length > 0) {
|
||||
const certPem = certs[0]
|
||||
|
||||
// 解析证书获取详细信息
|
||||
const x509 = new rs.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)
|
||||
|
||||
// 转换为 DN 字符串
|
||||
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
|
||||
}
|
||||
|
||||
// 提取公钥 - 使用 ASN.1 解析提取原始公钥字节
|
||||
try {
|
||||
// 将 PEM 转换为 hex
|
||||
const certHex = rs.pemtohex(certPem)
|
||||
|
||||
// 获取 tbsCertificate
|
||||
const tbsCert = rs.ASN1HEX.getTLVbyList(certHex, 0, [0])
|
||||
|
||||
// 获取 subjectPublicKeyInfo (在 tbsCertificate 中的索引是 6)
|
||||
const spki = rs.ASN1HEX.getTLVbyList(tbsCert, 0, [6])
|
||||
|
||||
// 提取公钥值 (BIT STRING)
|
||||
const publicKeyBitString = rs.ASN1HEX.getTLVbyList(spki, 0, [1])
|
||||
|
||||
// BIT STRING 的内容:跳过 tag 03 (2 hex), 跳过 length (2 hex), 跳过 unused bits (2 hex)
|
||||
// 所以从第 6 个字符开始
|
||||
const publicKeyHexContent = publicKeyBitString.substring(6)
|
||||
|
||||
// 提取算法 OID 来检测是否是 SM2
|
||||
const algorithmSeq = rs.ASN1HEX.getTLVbyList(spki, 0, [0])
|
||||
const oidTlv = rs.ASN1HEX.getTLVbyList(algorithmSeq, 0, [0])
|
||||
const oidHex = oidTlv.substring(4)
|
||||
|
||||
// SM2 OID: 1.2.156.10197.1.301 -> hex 2A811CCF550101301
|
||||
const isSM2 = oidHex.includes('2A811CCF55') || oidHex.includes('2a811ccf55')
|
||||
|
||||
// 创建完整的 subjectPublicKeyInfo PEM
|
||||
publicKey = rs.KJUR.asn1.ASN1Util.getPEMStringFromHex(spki, 'PUBLIC KEY')
|
||||
// publicKeyHex 就是 subjectPublicKeyInfo 的完整 hex
|
||||
publicKeyHex = spki
|
||||
} catch (e) {
|
||||
// SM2 公钥可能无法通过标准方式获取
|
||||
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 = rs.hextob64(signer.sighex)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
cert,
|
||||
publicKey,
|
||||
publicKeyHex,
|
||||
signature,
|
||||
signatureHex
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验签 PKCS7 签名(自动判断 RSA 或 SM2 算法)
|
||||
|
||||
Reference in New Issue
Block a user