diff --git a/kit/package-lock.json b/kit/package-lock.json index c0f2535..04dea82 100644 --- a/kit/package-lock.json +++ b/kit/package-lock.json @@ -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", diff --git a/kit/package.json b/kit/package.json index d86c5ce..b2fe7fd 100644 --- a/kit/package.json +++ b/kit/package.json @@ -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", diff --git a/kit/src/crypto.js b/kit/src/crypto.js index b044c2f..276539f 100644 --- a/kit/src/crypto.js +++ b/kit/src/crypto.js @@ -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 算法) diff --git a/kit/test/crypto_test.js b/kit/test/crypto_test.js index 1b47d46..281fff7 100644 --- a/kit/test/crypto_test.js +++ b/kit/test/crypto_test.js @@ -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) diff --git a/kit/test/debug_sm2_cert.js b/kit/test/debug_sm2_cert.js new file mode 100644 index 0000000..73540e4 --- /dev/null +++ b/kit/test/debug_sm2_cert.js @@ -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); +} diff --git a/kit/test/debug_sm2_pubkey.js b/kit/test/debug_sm2_pubkey.js new file mode 100644 index 0000000..0d9dd81 --- /dev/null +++ b/kit/test/debug_sm2_pubkey.js @@ -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); + } +} diff --git a/kit/test/debug_sm2_pubkey2.js b/kit/test/debug_sm2_pubkey2.js new file mode 100644 index 0000000..ecc3621 --- /dev/null +++ b/kit/test/debug_sm2_pubkey2.js @@ -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); + } +} diff --git a/kit/test/generate_sm2_p7.js b/kit/test/generate_sm2_p7.js new file mode 100644 index 0000000..368ecdf --- /dev/null +++ b/kit/test/generate_sm2_p7.js @@ -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 测试。'); diff --git a/kit/test/test_jsrsasign_sm2.js b/kit/test/test_jsrsasign_sm2.js new file mode 100644 index 0000000..b7cb2ef --- /dev/null +++ b/kit/test/test_jsrsasign_sm2.js @@ -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); +} diff --git a/kit/test/test_sm2_p7.js b/kit/test/test_sm2_p7.js new file mode 100644 index 0000000..93fa588 --- /dev/null +++ b/kit/test/test_sm2_p7.js @@ -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); +}