sm2 验签通过
This commit is contained in:
parent
9d826fb985
commit
6e95e8a1b1
10
kit/package-lock.json
generated
10
kit/package-lock.json
generated
@ -13,6 +13,7 @@
|
||||
"extract-zip": "^2.0.1",
|
||||
"get-port": "^7.0.0",
|
||||
"json5": "^2.2.3",
|
||||
"jsrsasign": "^11.1.1",
|
||||
"log4js": "^6.9.1",
|
||||
"node-forge": "^1.3.1",
|
||||
"physical-cpu-count": "^2.0.0",
|
||||
@ -339,6 +340,15 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsrsasign": {
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-11.1.1.tgz",
|
||||
"integrity": "sha512-6w95OOXH8DNeGxakqLndBEqqwQ6A70zGaky1oxfg8WVLWOnghTfJsc5Tknx+Z88MHSb1bGLcqQHImOF8Lk22XA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/kjur/jsrsasign#donations"
|
||||
}
|
||||
},
|
||||
"node_modules/layerr": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/layerr/-/layerr-2.0.1.tgz",
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
"extract-zip": "^2.0.1",
|
||||
"get-port": "^7.0.0",
|
||||
"json5": "^2.2.3",
|
||||
"jsrsasign": "^11.1.1",
|
||||
"log4js": "^6.9.1",
|
||||
"node-forge": "^1.3.1",
|
||||
"physical-cpu-count": "^2.0.0",
|
||||
|
||||
@ -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 算法)
|
||||
|
||||
@ -56,14 +56,29 @@ X9ncPP4VRERPw8U=
|
||||
// 测试 2: getInfoFromPKCS7 接口(SM2)
|
||||
try {
|
||||
console.log('\n2. 测试 getInfoFromPKCS7 接口(SM2)');
|
||||
// 这里应该使用实际的 PKCS7 签名数据进行测试
|
||||
// 由于没有实际数据,这里使用模拟数据
|
||||
const mockP7Sign = 'MIIBpAYJKoZIhvcNAQcCoIIBmzCCAZ8CAQExADALBgkqhkiG9w0BBwGgggLuMIIC3wIBADCCAbkGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM9Q7c7X7X7X7X7X7X7X7X7AgEQgIID+DCCA+SgAwIBAgIUY1234567890abcdefghijklmnopqrstuvwxyzMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFI1234567890abcdefghijklmnopqrstuvwxyzMB8GA1UdIwQYMBaAFJ1234567890abcdefghijklmnopqrstuvwxyzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAj1234567890abcdefghijklmnopqrstuvwxyz';
|
||||
const result = crypto.getInfoFromPKCS7(mockP7Sign);
|
||||
console.log('测试结果:', result);
|
||||
// 使用用户提供的真实 SM2 PKCS7 签名数据
|
||||
const sm2P7Base64 = 'MIIDKgYKKoEcz1UGAQQCAqCCAxowggMWAgEBMQ4wDAYIKoEcz1UBgxEFADAMBgoqgRzPVQYBBAIBoIICNzCCAjMwggHWoAMCAQICBgD1tfRoKTAMBggqgRzPVQGDdQUAMEAxCzAJBgNVBAYTAkNOMQ8wDQYDVQQKDAZQQkNDUkMxIDAeBgNVBAMMF1BCQ0NSQyBTTTIgT3BlcmF0aW9uIENBMB4XDTI2MDQwMjA4MDEzOVoXDTM2MDMzMDA4MDEzOVowTTELMAkGA1UEBhMCQ04xDzANBgNVBAoMBlBCQ0NSQzEtMCsGA1UEAwwkOTRmMWY5MzgtZjBiZC00NzYyLTkyOGUtZjY4YTdjY2UxNDFkMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEu3dU5Vn20VjVL9xGCCRPB/EpATY6HW24+zIZv46Cfv0R2PGM+ZJs/dYarw7qJCnpIbe5T0PKmyyJITNVrVnnMaOBrDCBqTAfBgNVHSMEGDAWgBRUqfVQHMpD+o+YdrPK1JlRJL757zAJBgNVHRMEAjAAME8GA1UdHwRIMEYwRKBCoECkPjA8MQ4wDAYDVQQDDAVjcmw3MTEMMAoGA1UECwwDY3JsMQ8wDQYDVQQKDAZQQkNDUkMxCzAJBgNVBAYTAkNOMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQU5bG6U0lYzWtkPJE74M/3HdafKKcwDAYIKoEcz1UBg3UFAANJADBGAiEAuLTE36/Eobcui23bFiPvOU4MOT0O0r9SyyHZ1JuvLwMCIQCejuSk03jyUAkbh1SaPx5n7WlDjI6tx9v9RJ7iw5rlojGBtzCBtAIBATBKMEAxCzAJBgNVBAYTAkNOMQ8wDQYDVQQKDAZQQkNDUkMxIDAeBgNVBAMMF1BCQ0NSQyBTTTIgT3BlcmF0aW9uIENBAgYA9bX0aCkwDAYIKoEcz1UBgxEFADANBgkqgRzPVQGCLQEFAARGMEQCIALTgWMD7io1JHfzInBwzDQpe9bonchuLFSED//JLDtnAiBmEUtr+ssAQIzFG4wY0CAj9QZxhENyCU2UYL1fhaysUA==';
|
||||
|
||||
const result = crypto.getInfoFromPKCS7(sm2P7Base64);
|
||||
console.log('测试结果:');
|
||||
console.log(' cert:');
|
||||
console.log(' subject:', result.cert?.subjectDN);
|
||||
console.log(' issuer:', result.cert?.issuerDN);
|
||||
console.log(' serialNumber:', result.cert?.serialNumber);
|
||||
console.log(' notBefore:', result.cert?.notBefore);
|
||||
console.log(' notAfter:', result.cert?.notAfter);
|
||||
console.log(' pem:');
|
||||
console.log(result.cert?.pem);
|
||||
console.log(' publicKey:');
|
||||
console.log(result.publicKey);
|
||||
console.log(' publicKey (hex):');
|
||||
console.log(result.publicKeyHex);
|
||||
console.log(' signature (hex):');
|
||||
console.log(result.signatureHex);
|
||||
console.log('✓ getInfoFromPKCS7 接口(SM2)测试通过');
|
||||
} catch (error) {
|
||||
console.error('✗ getInfoFromPKCS7 接口(SM2)测试失败:', error.message);
|
||||
console.error(error.stack);
|
||||
}
|
||||
|
||||
// 测试 3: verifyPKCS7 接口(RSA)
|
||||
|
||||
37
kit/test/debug_sm2_cert.js
Normal file
37
kit/test/debug_sm2_cert.js
Normal file
@ -0,0 +1,37 @@
|
||||
const rs = require('jsrsasign');
|
||||
|
||||
// 用户提供的 SM2 PKCS7 签名数据(没有头尾)
|
||||
const sm2P7Base64 = 'MIIDKgYKKoEcz1UGAQQCAqCCAxowggMWAgEBMQ4wDAYIKoEcz1UBgxEFADAMBgoqgRzPVQYBBAIBoIICNzCCAjMwggHWoAMCAQICBgD1tfRoKTAMBggqgRzPVQGDdQUAMEAxCzAJBgNVBAYTAkNOMQ8wDQYDVQQKDAZQQkNDUkMxIDAeBgNVBAMMF1BCQ0NSQyBTTTIgT3BlcmF0aW9uIENBMB4XDTI2MDQwMjA4MDEzOVoXDTM2MDMzMDA4MDEzOVowTTELMAkGA1UEBhMCQ04xDzANBgNVBAoMBlBCQ0NSQzEtMCsGA1UEAwwkOTRmMWY5MzgtZjBiZC00NzYyLTkyOGUtZjY4YTdjY2UxNDFkMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEu3dU5Vn20VjVL9xGCCRPB/EpATY6HW24+zIZv46Cfv0R2PGM+ZJs/dYarw7qJCnpIbe5T0PKmyyJITNVrVnnMaOBrDCBqTAfBgNVHSMEGDAWgBRUqfVQHMpD+o+YdrPK1JlRJL757zAJBgNVHRMEAjAAME8GA1UdHwRIMEYwRKBCoECkPjA8MQ4wDAYDVQQDDAVjcmw3MTEMMAoGA1UECwwDY3JsMQ8wDQYDVQQKDAZQQkNDUkMxCzAJBgNVBAYTAkNOMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQU5bG6U0lYzWtkPJE74M/3HdafKKcwDAYIKoEcz1UBg3UFAANJADBGAiEAuLTE36/Eobcui23bFiPvOU4MOT0O0r9SyyHZ1JuvLwMCIQCejuSk03jyUAkbh1SaPx5n7WlDjI6tx9v9RJ7iw5rlojGBtzCBtAIBATBKMEAxCzAJBgNVBAYTAkNOMQ8wDQYDVQQKDAZQQkNDUkMxIDAeBgNVBAMMF1BCQ0NSQyBTTTIgT3BlcmF0aW9uIENBAgYA9bX0aCkwDAYIKoEcz1UBgxEFADANBgkqgRzPVQGCLQEFAARGMEQCIALTgWMD7io1JHfzInBwzDQpe9bonchuLFSED//JLDtnAiBmEUtr+ssAQIzFG4wY0CAj9QZxhENyCU2UYL1fhaysUA==';
|
||||
|
||||
const parser = new rs.KJUR.asn1.cms.CMSParser();
|
||||
const p7 = parser.getCMSSignedData(rs.b64tohex(sm2P7Base64));
|
||||
|
||||
console.log('=== 调试 SM2 证书提取 ===');
|
||||
console.log('p7 keys:', Object.keys(p7));
|
||||
console.log('p7.certs:', p7.certs);
|
||||
console.log('p7.sinfos:', p7.sinfos);
|
||||
|
||||
// 尝试直接解析证书
|
||||
console.log('\n=== 直接解析证书 ===');
|
||||
try {
|
||||
// 从 hex 中提取证书
|
||||
const hex = rs.b64tohex(sm2P7Base64);
|
||||
console.log('hex length:', hex.length);
|
||||
|
||||
// 使用 ASN1HEX 解析
|
||||
const hCert = rs.ASN1HEX.getTLVbyList(hex, 0, [0, 3, 0, 0]);
|
||||
console.log('hCert:', hCert ? hCert.substring(0, 50) + '...' : 'null');
|
||||
|
||||
if (hCert) {
|
||||
const certPem = rs.KJUR.asn1.ASN1Util.getPEMStringFromHex(hCert, 'CERTIFICATE');
|
||||
console.log('certPem:', certPem.substring(0, 200));
|
||||
|
||||
const x509 = new rs.X509();
|
||||
x509.readCertPEM(certPem);
|
||||
|
||||
console.log('\n主题:', x509.getSubjectString());
|
||||
console.log('颁发者:', x509.getIssuerString());
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('错误:', e.message);
|
||||
}
|
||||
67
kit/test/debug_sm2_pubkey.js
Normal file
67
kit/test/debug_sm2_pubkey.js
Normal file
@ -0,0 +1,67 @@
|
||||
const rs = require('jsrsasign');
|
||||
|
||||
// 用户提供的 SM2 PKCS7 签名数据(没有头尾)
|
||||
const sm2P7Base64 = 'MIIDKgYKKoEcz1UGAQQCAqCCAxowggMWAgEBMQ4wDAYIKoEcz1UBgxEFADAMBgoqgRzPVQYBBAIBoIICNzCCAjMwggHWoAMCAQICBgD1tfRoKTAMBggqgRzPVQGDdQUAMEAxCzAJBgNVBAYTAkNOMQ8wDQYDVQQKDAZQQkNDUkMxIDAeBgNVBAMMF1BCQ0NSQyBTTTIgT3BlcmF0aW9uIENBMB4XDTI2MDQwMjA4MDEzOVoXDTM2MDMzMDA4MDEzOVowTTELMAkGA1UEBhMCQ04xDzANBgNVBAoMBlBCQ0NSQzEtMCsGA1UEAwwkOTRmMWY5MzgtZjBiZC00NzYyLTkyOGUtZjY4YTdjY2UxNDFkMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEu3dU5Vn20VjVL9xGCCRPB/EpATY6HW24+zIZv46Cfv0R2PGM+ZJs/dYarw7qJCnpIbe5T0PKmyyJITNVrVnnMaOBrDCBqTAfBgNVHSMEGDAWgBRUqfVQHMpD+o+YdrPK1JlRJL757zAJBgNVHRMEAjAAME8GA1UdHwRIMEYwRKBCoECkPjA8MQ4wDAYDVQQDDAVjcmw3MTEMMAoGA1UECwwDY3JsMQ8wDQYDVQQKDAZQQkNDUkMxCzAJBgNVBAYTAkNOMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQU5bG6U0lYzWtkPJE74M/3HdafKKcwDAYIKoEcz1UBg3UFAANJADBGAiEAuLTE36/Eobcui23bFiPvOU4MOT0O0r9SyyHZ1JuvLwMCIQCejuSk03jyUAkbh1SaPx5n7WlDjI6tx9v9RJ7iw5rlojGBtzCBtAIBATBKMEAxCzAJBgNVBAYTAkNOMQ8wDQYDVQQKDAZQQkNDUkMxIDAeBgNVBAMMF1BCQ0NSQyBTTTIgT3BlcmF0aW9uIENBAgYA9bX0aCkwDAYIKoEcz1UBgxEFADANBgkqgRzPVQGCLQEFAARGMEQCIALTgWMD7io1JHfzInBwzDQpe9bonchuLFSED//JLDtnAiBmEUtr+ssAQIzFG4wY0CAj9QZxhENyCU2UYL1fhaysUA==';
|
||||
|
||||
const parser = new rs.KJUR.asn1.cms.CMSParser();
|
||||
const p7 = parser.getCMSSignedData(rs.b64tohex(sm2P7Base64));
|
||||
|
||||
console.log('=== 调试 SM2 公钥提取 ===');
|
||||
|
||||
if (p7.certs && p7.certs.array && p7.certs.array.length > 0) {
|
||||
const certPem = p7.certs.array[0];
|
||||
console.log('证书 PEM 前200字符:', certPem.substring(0, 200));
|
||||
|
||||
const x509 = new rs.X509();
|
||||
x509.readCertPEM(certPem);
|
||||
|
||||
console.log('\n证书信息:');
|
||||
console.log('主题:', x509.getSubjectString());
|
||||
console.log('颁发者:', x509.getIssuerString());
|
||||
console.log('序列号:', x509.getSerialNumberHex());
|
||||
|
||||
// 尝试获取公钥
|
||||
console.log('\n尝试获取公钥:');
|
||||
try {
|
||||
const pubKey = x509.getPublicKey();
|
||||
console.log('getPublicKey() 结果:', pubKey);
|
||||
|
||||
if (pubKey) {
|
||||
console.log('公钥类型:', typeof pubKey);
|
||||
console.log('公钥 keys:', Object.keys(pubKey));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('getPublicKey() 错误:', e.message);
|
||||
}
|
||||
|
||||
// 尝试直接从 ASN.1 解析
|
||||
console.log('\n尝试从 ASN.1 解析公钥:');
|
||||
try {
|
||||
const hex = rs.pemtohex(certPem);
|
||||
// 证书结构: SEQUENCE -> tbsCertificate -> subjectPublicKeyInfo
|
||||
const hTBS = rs.ASN1HEX.getTLVbyList(hex, 0, [0]);
|
||||
const hSPKI = rs.ASN1HEX.getTLVbyList(hTBS, 0, [6]); // subjectPublicKeyInfo 通常在第6个位置
|
||||
|
||||
if (hSPKI) {
|
||||
console.log('subjectPublicKeyInfo hex:', hSPKI.substring(0, 100) + '...');
|
||||
|
||||
// 提取公钥值
|
||||
const hAlgorithm = rs.ASN1HEX.getTLVbyList(hSPKI, 0, [0]);
|
||||
const hPublicKey = rs.ASN1HEX.getTLVbyList(hSPKI, 0, [1]);
|
||||
|
||||
if (hAlgorithm) {
|
||||
console.log('算法 OID:', rs.ASN1HEX.getOIDbyList(hAlgorithm, 0, [0]));
|
||||
}
|
||||
|
||||
if (hPublicKey) {
|
||||
console.log('公钥值 hex:', hPublicKey.substring(4) + '...'); // 跳过 tag 和 length
|
||||
|
||||
// 尝试创建 PEM
|
||||
const pubKeyPem = rs.KJUR.asn1.ASN1Util.getPEMStringFromHex(hSPKI, 'PUBLIC KEY');
|
||||
console.log('公钥 PEM:', pubKeyPem.substring(0, 200) + '...');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('ASN.1 解析错误:', e.message);
|
||||
}
|
||||
}
|
||||
95
kit/test/debug_sm2_pubkey2.js
Normal file
95
kit/test/debug_sm2_pubkey2.js
Normal file
@ -0,0 +1,95 @@
|
||||
const rs = require('jsrsasign');
|
||||
|
||||
// 用户提供的 SM2 PKCS7 签名数据
|
||||
const sm2P7Base64 = 'MIIDKgYKKoEcz1UGAQQCAqCCAxowggMWAgEBMQ4wDAYIKoEcz1UBgxEFADAMBgoqgRzPVQYBBAIBoIICNzCCAjMwggHWoAMCAQICBgD1tfRoKTAMBggqgRzPVQGDdQUAMEAxCzAJBgNVBAYTAkNOMQ8wDQYDVQQKDAZQQkNDUkMxIDAeBgNVBAMMF1BCQ0NSQyBTTTIgT3BlcmF0aW9uIENBMB4XDTI2MDQwMjA4MDEzOVoXDTM2MDMzMDA4MDEzOVowTTELMAkGA1UEBhMCQ04xDzANBgNVBAoMBlBCQ0NSQzEtMCsGA1UEAwwkOTRmMWY5MzgtZjBiZC00NzYyLTkyOGUtZjY4YTdjY2UxNDFkMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEu3dU5Vn20VjVL9xGCCRPB/EpATY6HW24+zIZv46Cfv0R2PGM+ZJs/dYarw7qJCnpIbe5T0PKmyyJITNVrVnnMaOBrDCBqTAfBgNVHSMEGDAWgBRUqfVQHMpD+o+YdrPK1JlRJL757zAJBgNVHRMEAjAAME8GA1UdHwRIMEYwRKBCoECkPjA8MQ4wDAYDVQQDDAVjcmw3MTEMMAoGA1UECwwDY3JsMQ8wDQYDVQQKDAZQQkNDUkMxCzAJBgNVBAYTAkNOMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQU5bG6U0lYzWtkPJE74M/3HdafKKcwDAYIKoEcz1UBg3UFAANJADBGAiEAuLTE36/Eobcui23bFiPvOU4MOT0O0r9SyyHZ1JuvLwMCIQCejuSk03jyUAkbh1SaPx5n7WlDjI6tx9v9RJ7iw5rlojGBtzCBtAIBATBKMEAxCzAJBgNVBAYTAkNOMQ8wDQYDVQQKDAZQQkNDUkMxIDAeBgNVBAMMF1BCQ0NSQyBTTTIgT3BlcmF0aW9uIENBAgYA9bX0aCkwDAYIKoEcz1UBgxEFADANBgkqgRzPVQGCLQEFAARGMEQCIALTgWMD7io1JHfzInBwzDQpe9bonchuLFSED//JLDtnAiBmEUtr+ssAQIzFG4wY0CAj9QZxhENyCU2UYL1fhaysUA==';
|
||||
|
||||
const parser = new rs.KJUR.asn1.cms.CMSParser();
|
||||
const p7 = parser.getCMSSignedData(rs.b64tohex(sm2P7Base64));
|
||||
|
||||
console.log('=== 调试 SM2 公钥提取 ===');
|
||||
|
||||
if (p7.certs && p7.certs.array && p7.certs.array.length > 0) {
|
||||
const certPem = p7.certs.array[0];
|
||||
|
||||
// 将 PEM 转换为 hex
|
||||
const certHex = rs.pemtohex(certPem);
|
||||
console.log('证书 hex 长度:', certHex.length);
|
||||
console.log('证书 hex 前100字符:', certHex.substring(0, 100));
|
||||
|
||||
// 证书结构: SEQUENCE { tbsCertificate, signatureAlgorithm, signatureValue }
|
||||
// tbsCertificate 结构: SEQUENCE { version, serialNumber, signature, issuer, validity, subject, subjectPublicKeyInfo, ... }
|
||||
|
||||
try {
|
||||
// 获取 tbsCertificate
|
||||
const tbsCert = rs.ASN1HEX.getTLVbyList(certHex, 0, [0]);
|
||||
console.log('\ntbsCertificate 长度:', tbsCert.length);
|
||||
|
||||
// 获取 subjectPublicKeyInfo (在 tbsCertificate 中的索引通常是 6)
|
||||
// 但我们需要先确定正确的索引
|
||||
let spkiIndex = 6;
|
||||
let spki = null;
|
||||
|
||||
// 尝试不同的索引
|
||||
for (let i = 3; i <= 8; i++) {
|
||||
try {
|
||||
const tlv = rs.ASN1HEX.getTLVbyList(tbsCert, 0, [i]);
|
||||
if (tlv && tlv.startsWith('30')) { // SEQUENCE
|
||||
// 检查是否是 subjectPublicKeyInfo
|
||||
// subjectPublicKeyInfo 应该包含 algorithm 和 subjectPublicKey
|
||||
const inner = rs.ASN1HEX.getTLVbyList(tlv, 0, [0]);
|
||||
if (inner && inner.startsWith('30')) {
|
||||
console.log(`索引 ${i} 可能是 subjectPublicKeyInfo`);
|
||||
spki = tlv;
|
||||
spkiIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 忽略错误
|
||||
}
|
||||
}
|
||||
|
||||
if (spki) {
|
||||
console.log('\nsubjectPublicKeyInfo hex:', spki.substring(0, 100));
|
||||
|
||||
// 提取算法 OID
|
||||
try {
|
||||
const algorithmSeq = rs.ASN1HEX.getTLVbyList(spki, 0, [0]);
|
||||
console.log('algorithmSeq:', algorithmSeq);
|
||||
|
||||
// 提取 OID
|
||||
const oidTlv = rs.ASN1HEX.getTLVbyList(algorithmSeq, 0, [0]);
|
||||
console.log('OID TLV:', oidTlv);
|
||||
|
||||
// 解析 OID
|
||||
if (oidTlv && oidTlv.startsWith('06')) {
|
||||
const oidHex = oidTlv.substring(4);
|
||||
console.log('OID hex:', oidHex);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('提取算法 OID 错误:', e.message);
|
||||
}
|
||||
|
||||
// 提取公钥值 (BIT STRING)
|
||||
try {
|
||||
const publicKeyBitString = rs.ASN1HEX.getTLVbyList(spki, 0, [1]);
|
||||
console.log('\n公钥 BIT STRING:', publicKeyBitString.substring(0, 100));
|
||||
|
||||
// BIT STRING 的内容从第4个字符开始 (跳过 tag 03, length, 和 unused bits 00)
|
||||
const publicKeyContent = publicKeyBitString.substring(4);
|
||||
console.log('公钥内容:', publicKeyContent.substring(0, 100));
|
||||
|
||||
// 对于 SM2,公钥通常是 65 字节 (0x04 + 32字节 X + 32字节 Y)
|
||||
console.log('公钥内容长度:', publicKeyContent.length / 2, '字节');
|
||||
|
||||
// 创建公钥 hex
|
||||
console.log('\n公钥 hex (完整):', publicKeyContent);
|
||||
} catch (e) {
|
||||
console.error('提取公钥值错误:', e.message);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('ASN.1 解析错误:', e.message);
|
||||
console.error(e.stack);
|
||||
}
|
||||
}
|
||||
31
kit/test/generate_sm2_p7.js
Normal file
31
kit/test/generate_sm2_p7.js
Normal file
@ -0,0 +1,31 @@
|
||||
const sm2 = require('sm-crypto').sm2;
|
||||
const sm3 = require('sm-crypto').sm3;
|
||||
const forge = require('node-forge');
|
||||
|
||||
console.log('=== 生成 SM2 测试数据 ===');
|
||||
|
||||
// 生成 SM2 密钥对
|
||||
const keypair = sm2.generateKeyPairHex();
|
||||
console.log('私钥:', keypair.privateKey);
|
||||
console.log('公钥:', keypair.publicKey);
|
||||
|
||||
// 测试数据
|
||||
const originalData = 'This is test data for SM2 signature.';
|
||||
console.log('\n原始数据:', originalData);
|
||||
|
||||
// 计算 SM3 哈希
|
||||
const hash = sm3(originalData);
|
||||
console.log('SM3 哈希:', hash);
|
||||
|
||||
// SM2 签名
|
||||
const sig = sm2.doSignature(hash, keypair.privateKey);
|
||||
console.log('SM2 签名:', sig);
|
||||
|
||||
// 验证签名
|
||||
const verifyResult = sm2.doVerify(hash, sig, keypair.publicKey);
|
||||
console.log('签名验证结果:', verifyResult);
|
||||
|
||||
console.log('\n=== 注意 ===');
|
||||
console.log('由于 node-forge 不支持 SM2 算法,无法生成标准的 SM2 PKCS7 签名。');
|
||||
console.log('SM2 PKCS7 签名需要特殊的 OID 和格式,目前仅支持 RSA PKCS7。');
|
||||
console.log('建议:测试2可以使用 RSA 的 PKCS7 数据,或者跳过 SM2 测试。');
|
||||
42
kit/test/test_jsrsasign_sm2.js
Normal file
42
kit/test/test_jsrsasign_sm2.js
Normal file
@ -0,0 +1,42 @@
|
||||
const rs = require('jsrsasign');
|
||||
|
||||
// 用户提供的 SM2 PKCS7 签名数据(没有头尾)
|
||||
const sm2P7Base64 = 'MIIDKgYKKoEcz1UGAQQCAqCCAxowggMWAgEBMQ4wDAYIKoEcz1UBgxEFADAMBgoqgRzPVQYBBAIBoIICNzCCAjMwggHWoAMCAQICBgD1tfRoKTAMBggqgRzPVQGDdQUAMEAxCzAJBgNVBAYTAkNOMQ8wDQYDVQQKDAZQQkNDUkMxIDAeBgNVBAMMF1BCQ0NSQyBTTTIgT3BlcmF0aW9uIENBMB4XDTI2MDQwMjA4MDEzOVoXDTM2MDMzMDA4MDEzOVowTTELMAkGA1UEBhMCQ04xDzANBgNVBAoMBlBCQ0NSQzEtMCsGA1UEAwwkOTRmMWY5MzgtZjBiZC00NzYyLTkyOGUtZjY4YTdjY2UxNDFkMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEu3dU5Vn20VjVL9xGCCRPB/EpATY6HW24+zIZv46Cfv0R2PGM+ZJs/dYarw7qJCnpIbe5T0PKmyyJITNVrVnnMaOBrDCBqTAfBgNVHSMEGDAWgBRUqfVQHMpD+o+YdrPK1JlRJL757zAJBgNVHRMEAjAAME8GA1UdHwRIMEYwRKBCoECkPjA8MQ4wDAYDVQQDDAVjcmw3MTEMMAoGA1UECwwDY3JsMQ8wDQYDVQQKDAZQQkNDUkMxCzAJBgNVBAYTAkNOMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQU5bG6U0lYzWtkPJE74M/3HdafKKcwDAYIKoEcz1UBg3UFAANJADBGAiEAuLTE36/Eobcui23bFiPvOU4MOT0O0r9SyyHZ1JuvLwMCIQCejuSk03jyUAkbh1SaPx5n7WlDjI6tx9v9RJ7iw5rlojGBtzCBtAIBATBKMEAxCzAJBgNVBAYTAkNOMQ8wDQYDVQQKDAZQQkNDUkMxIDAeBgNVBAMMF1BCQ0NSQyBTTTIgT3BlcmF0aW9uIENBAgYA9bX0aCkwDAYIKoEcz1UBgxEFADANBgkqgRzPVQGCLQEFAARGMEQCIALTgWMD7io1JHfzInBwzDQpe9bonchuLFSED//JLDtnAiBmEUtr+ssAQIzFG4wY0CAj9QZxhENyCU2UYL1fhaysUA==';
|
||||
|
||||
// 转换为 hex
|
||||
const sm2P7Hex = rs.hextorstr(rs.b64tohex(sm2P7Base64));
|
||||
|
||||
console.log('=== 使用 jsrsasign 测试 SM2 PKCS7 ===');
|
||||
console.log('数据长度:', sm2P7Base64.length);
|
||||
|
||||
try {
|
||||
// 使用 CMSParser 解析
|
||||
const parser = new rs.KJUR.asn1.cms.CMSParser();
|
||||
const result = parser.getCMSSignedData(rs.b64tohex(sm2P7Base64));
|
||||
|
||||
console.log('\n解析成功!');
|
||||
console.log('版本:', result.version);
|
||||
console.log('哈希算法:', result.hashalgs);
|
||||
console.log('内容类型:', result.econtent?.type);
|
||||
console.log('证书数量:', result.certs?.length);
|
||||
console.log('签名者数量:', result.sinfos?.length);
|
||||
|
||||
if (result.sinfos && result.sinfos.length > 0) {
|
||||
const signer = result.sinfos[0];
|
||||
console.log('\n签名者信息:');
|
||||
console.log(' 版本:', signer.version);
|
||||
console.log(' 哈希算法:', signer.hashalg);
|
||||
console.log(' 签名算法:', signer.sigalg);
|
||||
console.log(' 签名值 (hex):', signer.sighex?.substring(0, 50) + '...');
|
||||
}
|
||||
|
||||
if (result.certs && result.certs.length > 0) {
|
||||
console.log('\n证书信息:');
|
||||
const cert = result.certs[0];
|
||||
console.log(' 证书 PEM:', cert.substring(0, 100) + '...');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n解析失败:', error.message);
|
||||
console.error(error.stack);
|
||||
}
|
||||
26
kit/test/test_sm2_p7.js
Normal file
26
kit/test/test_sm2_p7.js
Normal file
@ -0,0 +1,26 @@
|
||||
const forge = require('node-forge');
|
||||
|
||||
// 用户提供的 SM2 PKCS7 签名数据(没有头尾)
|
||||
const sm2P7Base64 = 'MIIDKgYKKoEcz1UGAQQCAqCCAxowggMWAgEBMQ4wDAYIKoEcz1UBgxEFADAMBgoqgRzPVQYBBAIBoIICNzCCAjMwggHWoAMCAQICBgD1tfRoKTAMBggqgRzPVQGDdQUAMEAxCzAJBgNVBAYTAkNOMQ8wDQYDVQQKDAZQQkNDUkMxIDAeBgNVBAMMF1BCQ0NSQyBTTTIgT3BlcmF0aW9uIENBMB4XDTI2MDQwMjA4MDEzOVoXDTM2MDMzMDA4MDEzOVowTTELMAkGA1UEBhMCQ04xDzANBgNVBAoMBlBCQ0NSQzEtMCsGA1UEAwwkOTRmMWY5MzgtZjBiZC00NzYyLTkyOGUtZjY4YTdjY2UxNDFkMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEu3dU5Vn20VjVL9xGCCRPB/EpATY6HW24+zIZv46Cfv0R2PGM+ZJs/dYarw7qJCnpIbe5T0PKmyyJITNVrVnnMaOBrDCBqTAfBgNVHSMEGDAWgBRUqfVQHMpD+o+YdrPK1JlRJL757zAJBgNVHRMEAjAAME8GA1UdHwRIMEYwRKBCoECkPjA8MQ4wDAYDVQQDDAVjcmw3MTEMMAoGA1UECwwDY3JsMQ8wDQYDVQQKDAZQQkNDUkMxCzAJBgNVBAYTAkNOMAsGA1UdDwQEAwIHgDAdBgNVHQ4EFgQU5bG6U0lYzWtkPJE74M/3HdafKKcwDAYIKoEcz1UBg3UFAANJADBGAiEAuLTE36/Eobcui23bFiPvOU4MOT0O0r9SyyHZ1JuvLwMCIQCejuSk03jyUAkbh1SaPx5n7WlDjI6tx9v9RJ7iw5rlojGBtzCBtAIBATBKMEAxCzAJBgNVBAYTAkNOMQ8wDQYDVQQKDAZQQkNDUkMxIDAeBgNVBAMMF1BCQ0NSQyBTTTIgT3BlcmF0aW9uIENBAgYA9bX0aCkwDAYIKoEcz1UBgxEFADANBgkqgRzPVQGCLQEFAARGMEQCIALTgWMD7io1JHfzInBwzDQpe9bonchuLFSED//JLDtnAiBmEUtr+ssAQIzFG4wY0CAj9QZxhENyCU2UYL1fhaysUA==';
|
||||
|
||||
// 包装成 PEM 格式
|
||||
const pemData = `-----BEGIN PKCS7-----\n${sm2P7Base64}\n-----END PKCS7-----`;
|
||||
|
||||
console.log('=== 测试 SM2 PKCS7 数据 ===');
|
||||
console.log('数据长度:', sm2P7Base64.length);
|
||||
console.log('PEM 格式前200字符:', pemData.substring(0, 200));
|
||||
|
||||
try {
|
||||
const p7 = forge.pkcs7.messageFromPem(pemData);
|
||||
console.log('\n解析成功!');
|
||||
console.log('类型:', p7.type);
|
||||
console.log('证书数量:', p7.certificates ? p7.certificates.length : 0);
|
||||
|
||||
if (p7.certificates && p7.certificates.length > 0) {
|
||||
const cert = p7.certificates[0];
|
||||
console.log('证书主题:', cert.subject ? cert.subject.attributes.map(a => `${a.name}=${a.value}`).join(', ') : 'N/A');
|
||||
console.log('证书颁发者:', cert.issuer ? cert.issuer.attributes.map(a => `${a.name}=${a.value}`).join(', ') : 'N/A');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('\n解析失败:', error.message);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user