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); } }