JuneAndGreen / sm-crypto

国密算法js版
MIT License
943 stars 255 forks source link

hi, 这个工具又从证书获取公钥或私钥的方法吗? #109

Open meguoe opened 5 months ago

meguoe commented 5 months ago

hi, 这个工具有类似node-forge中的 forge.pki.certificateFromPem,forge.pki.privateKeyFromPem 方法吗?

changhr2013 commented 5 months ago

没有,SM2 属于 ECC 体系,我理解解析 PEM 结构走 ECC 的标准即可,你可以直接使用 forge 解析 ECC 的方法解析 SM2 的私钥和证书。

meguoe commented 5 months ago

好,谢谢,另外可以帮忙看一下吗?我从pem获取公私钥后验证不通过,第一次接触国密,有点迷茫。。。

try {
  const crtPem = fs.readFileSync('./ssl/sm2.cnsec.enc.crt.pem', 'utf8');
  const publicKey = crypto.createPublicKey(crtPem).export({type: 'spki', format: 'der'}).toString('hex')
  console.log(publicKey)

  const keyPem = fs.readFileSync('./ssl/sm2.cnsec.enc.key.pem', 'utf8');
  const privateKey = crypto.createPrivateKey(keyPem).export({type: 'pkcs8', format: 'der'}).toString('hex')
  console.log(privateKey)

  // 用publicKey加密数据
  const cipherMode = 1
  const encrypted = sm2.doEncrypt('hello world', publicKey, cipherMode);

  // 用privateKey解密数据
  const decrypted = sm2.doDecrypt(encrypted, privateKey, cipherMode);
  console.log(decrypted.toString());
} catch (e) {
  console.error(e)
}

// 这是从www.gmcrt.cn申请的测试证书

-----BEGIN CERTIFICATE----- MIICezCCAh6gAwIBAgIGAY7MO36xMAwGCCqBHM9VAYN1BQAwSzELMAkGA1UEBhMC Q04xDjAMBgNVBAoTBUdNU1NMMRAwDgYDVQQLEwdQS0kvU00yMRowGAYDVQQDExFN aWRkbGVDQSBmb3IgVGVzdDAiGA8yMDI0MDQxMDE2MDAwMFoYDzIwMjUwNDEwMTYw MDAwWjCBljELMAkGA1UEBhMCQ04xDzANBgNVBAgMBuWMl+S6rDEjMCEGA1UEBwwa 6LSi5pm65Zu96ZmF5aSn5Y6mQ+W6pzExMTAxDjAMBgNVBAoTBWNuc2VjMRIwEAYD VQQLEwljbnNlYy5mdW4xDjAMBgNVBAMTBWNuc2VjMR0wGwYJKoZIhvcNAQkBFg5t ZWd1b2VAMTYzLmNvbTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABMb0Z97yXn9K DyH9kxhO9BK4LespdHUzpK92c0YhOXfKjESseVHOVPWa645vnQFWWX76Aa6WsK2j POT/zkezoHSjgZswgZgwGwYDVR0jBBQwEoAQ+X9VtCeUM2KmVspvzF0a/zAZBgNV HQ4EEgQQIPdnNRr/XL2fFBz5rAveHTAQBgNVHREECTAHggVjbnNlYzAxBggrBgEF BQcBAQQlMCMwIQYIKwYBBQUHMAGGFWh0dHBzOi8vb2NzcC5nbXNzbC5jbjAJBgNV HRMEAjAAMA4GA1UdDwEB/wQEAwIAODAMBggqgRzPVQGDdQUAA0kAMEYCIQC23Isq Hr45RqQ2Hz06uLUfNTUH8QdCp0R2i3tnkDXZswIhAIzHCrcZ3oOROWcq/hQiPA4F 6hWD2qxbswP0Q0rG5REL -----END CERTIFICATE-----

-----BEGIN PRIVATE KEY----- MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgxkERNjUtWAtJ1IBy Gsl+0ZyQAbHyaRGoq8L88AMnL/GgCgYIKoEcz1UBgi2hRANCAATG9Gfe8l5/Sg8h /ZMYTvQSuC3rKXR1M6SvdnNGITl3yoxErHlRzlT1muuOb50BVll++gGulrCtozzk /85Hs6B0 -----END PRIVATE KEY-----

meguoe commented 5 months ago

sm2.verifyPublicKey(publicKey) 结果为 false

// 加解密测试执行异常 TypeError: Cannot read properties of null (reading 'multiply') at Object.doEncrypt (/Users/meguoe/Downloads/demo-project/node_modules/.pnpm/sm-crypto@0.3.13/node_modules/sm-crypto/src/sm2/index.js:25:23) at Object. (/Users/meguoe/Downloads/demo-project/sm-crypto.js:39:25) at Module._compile (node:internal/modules/cjs/loader:1256:14) at Module._extensions..js (node:internal/modules/cjs/loader:1310:10) at Module.load (node:internal/modules/cjs/loader:1119:32) at Module._load (node:internal/modules/cjs/loader:960:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) at node:internal/main/run_main_module:23:47

changhr2013 commented 5 months ago

js 解 asn1 好难搞,累死我了 😂

import crypto from 'crypto';
import asn1 from 'asn1.js';
import {sm2} from "sm-crypto";

const crtPEM = "-----BEGIN CERTIFICATE-----\n" +
    "MIICezCCAh6gAwIBAgIGAY7MO36xMAwGCCqBHM9VAYN1BQAwSzELMAkGA1UEBhMC\n" +
    "Q04xDjAMBgNVBAoTBUdNU1NMMRAwDgYDVQQLEwdQS0kvU00yMRowGAYDVQQDExFN\n" +
    "aWRkbGVDQSBmb3IgVGVzdDAiGA8yMDI0MDQxMDE2MDAwMFoYDzIwMjUwNDEwMTYw\n" +
    "MDAwWjCBljELMAkGA1UEBhMCQ04xDzANBgNVBAgMBuWMl+S6rDEjMCEGA1UEBwwa\n" +
    "6LSi5pm65Zu96ZmF5aSn5Y6mQ+W6pzExMTAxDjAMBgNVBAoTBWNuc2VjMRIwEAYD\n" +
    "VQQLEwljbnNlYy5mdW4xDjAMBgNVBAMTBWNuc2VjMR0wGwYJKoZIhvcNAQkBFg5t\n" +
    "ZWd1b2VAMTYzLmNvbTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABMb0Z97yXn9K\n" +
    "DyH9kxhO9BK4LespdHUzpK92c0YhOXfKjESseVHOVPWa645vnQFWWX76Aa6WsK2j\n" +
    "POT/zkezoHSjgZswgZgwGwYDVR0jBBQwEoAQ+X9VtCeUM2KmVspvzF0a/zAZBgNV\n" +
    "HQ4EEgQQIPdnNRr/XL2fFBz5rAveHTAQBgNVHREECTAHggVjbnNlYzAxBggrBgEF\n" +
    "BQcBAQQlMCMwIQYIKwYBBQUHMAGGFWh0dHBzOi8vb2NzcC5nbXNzbC5jbjAJBgNV\n" +
    "HRMEAjAAMA4GA1UdDwEB/wQEAwIAODAMBggqgRzPVQGDdQUAA0kAMEYCIQC23Isq\n" +
    "Hr45RqQ2Hz06uLUfNTUH8QdCp0R2i3tnkDXZswIhAIzHCrcZ3oOROWcq/hQiPA4F\n" +
    "6hWD2qxbswP0Q0rG5REL\n" +
    "-----END CERTIFICATE-----";

const privateKeyPEM = "-----BEGIN PRIVATE KEY-----\n" +
    "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgxkERNjUtWAtJ1IBy\n" +
    "Gsl+0ZyQAbHyaRGoq8L88AMnL/GgCgYIKoEcz1UBgi2hRANCAATG9Gfe8l5/Sg8h\n" +
    "/ZMYTvQSuC3rKXR1M6SvdnNGITl3yoxErHlRzlT1muuOb50BVll++gGulrCtozzk\n" +
    "/85Hs6B0\n" +
    "-----END PRIVATE KEY-----";

// 定义 ASN.1 结构
// 定义 AlgorithmIdentifier
const AlgorithmIdentifier = asn1.define('AlgorithmIdentifier', function () {
    this.seq().obj(
        this.key('algorithm').objid(),
        this.key('parameters').any()
    );
});
// 定义 SubjectPublicKeyInfo
const SubjectPublicKeyInfo = asn1.define('SubjectPublicKeyInfo', function () {
    this.seq().obj(
        this.key('algorithm').use(AlgorithmIdentifier),
        this.key('subjectPublicKey').bitstr()
    );
});
const Attribute = asn1.define('Attribute', function () {
    this.seq().obj(
        this.key('type').objid(),
        this.key('values').set().of(this.any())  // 根据实际的 Attribute 类型具体定义
    );
});
// 定义 PrivateKeyInfo
const PrivateKeyInfo = asn1.define('PrivateKeyInfo', function () {
    this.seq().obj(
        this.key('version').int(),
        this.key('privateKeyAlgorithm').use(AlgorithmIdentifier),
        this.key('privateKey').octstr(),
        this.key('attributes').implicit(0).optional().setof(Attribute)  // 如果需要定义具体的 Attribute 结构
    );
});
// 定义 ECPrivateKey
const ECPrivateKey = asn1.define('ECPrivateKey', function () {
    this.seq().obj(
        this.key('version').int(),
        this.key('privateKey').octstr(),
        this.key('publicKey').explicit(1).optional().bitstr()
    );
});

// 抽取裸公钥
let publicKeyBuffer = new crypto.X509Certificate(crtPEM).publicKey.export({type: 'spki', format: 'der'});
let plainPublicKeyHex = SubjectPublicKeyInfo.decode(publicKeyBuffer, 'der').subjectPublicKey.data.toString('hex');
console.log(plainPublicKeyHex);

// 抽取裸私钥
const privateKeyBuffer = crypto.createPrivateKey(privateKeyPEM).export({type: 'pkcs8', format: 'der'})
let plainPrivateKeyHex = ECPrivateKey.decode(PrivateKeyInfo.decode(privateKeyBuffer, 'der').privateKey, 'der').privateKey.toString('hex');
console.log(plainPrivateKeyHex);

// 加密模式
const cipherMode = 1

// 加密
const encrypted = sm2.doEncrypt('hello world', plainPublicKeyHex, cipherMode);
console.log(encrypted);

// 解密
const decrypted = sm2.doDecrypt(encrypted, plainPrivateKeyHex, cipherMode);
console.log(decrypted);

示例输出:

// 公钥
04c6f467def25e7f4a0f21fd93184ef412b82deb29747533a4af767346213977ca8c44ac7951ce54f59aeb8e6f9d0156597efa01ae96b0ada33ce4ffce47b3a074
// 私钥
c6411136352d580b49d480721ac97ed19c9001b1f26911a8abc2fcf003272ff1
// 加密后的密文
7c2b06673b0aad4cb2d27c6f19d72d338eced762a3f9bb09c7780731066d1f4995994995480fe6767e3494ab2509896b169db948df4ffe6efbf3471f425b43e288163ae216f225f3e1fedd5f2e48f863d213e4174c434cc523a4d94c731fd37f9588a20f53c46c3fe14401
// 解密后的明文
hello world
meguoe commented 5 months ago

太牛了,非常感谢。

ValueLan commented 4 months ago

@changhr2013
-----BEGIN PUBLIC KEY----- .....= -----END PUBLIC KEY----- 请问这个如何转换

changhr2013 commented 4 months ago

@changhr2013 -----BEGIN PUBLIC KEY----- .....= -----END PUBLIC KEY----- 请问这个如何转换

公钥一般使用的是 SubjectPublicKeyInfo 结构,按照上面 SubjectPublicKeyInfo 结构的方法解析一下应该就 ok。