代码之家  ›  专栏  ›  技术社区  ›  Sudheesh Singanamalla

Nodejs Crypto ECDH公钥十六进制as X.509

  •  0
  • Sudheesh Singanamalla  · 技术社区  · 6 年前

    我用的是 prime256v1 nodejs 使用默认值 crypto

    使用

    let crypto = require('crypto');
    let e = crypto.createECDH('prime256v1');
    e.generateKeys();
    privateKey = e.getPrivateKey();
    privateKeyHex = privateKey.toString('hex');
    publicKey = e.getPublicKey();
    publicKeyHex = publicKey.toString('hex');
    

    我得到一个公钥,看起来像下面的十六进制字符串:

    '049a6b0ac242afe41128cf59736412686ca83c9e902ee3fa0f13810b9d59ebfe5e49204427c23b630be12ae33815b0bda6ed8d0603386c6ea5f1906cdb0e731286'
    

    Usign公司 jsrsasign

    let jsrsa = require('jsrsasign');
    let KEYUTIL = jsrsa.KEYUTIL;
    let kp = KEYUTIL.generateKeypair("EC", "prime256v1");
    let pkHex = kp.pubKeyObj.pubKeyHex
    

    '04f36e41189420db05dd8a73e3cb310b0c55809190bdedd89bf19769ac8df3cd06c1380f646e9e65e31c24affff79e43516b37e0186c3753cfdfd29894c2becc84'
    

    在Java中将publickeyhex转换为PublicKey对象

    PublicKey 中的对象 java . 使用 EC byte[] 试着建造 公钥 对象,该对象需要X.509格式编码。

    public PublicKey getPublicKey(byte[] pk) throws NoSuchAlgorithmException, InvalidKeySpecException {
        EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pk);
        KeyFactory kf = KeyFactory.getInstance("EC");
        PublicKey pub = kf.generatePublic(publicKeySpec);
        return pub;
    }
    

    将十六进制字符串转换为 我使用以下方法:

    public byte[] hexStringToByteArray(String hexString) {
        byte[] bytes = new byte[hexString.length() / 2];
    
        for(int i = 0; i < hexString.length(); i += 2) {
            String sub = hexString.substring(i, i + 2);
            Integer intVal = Integer.parseInt(sub, 16);
            bytes[i / 2] = intVal.byteValue();
            String hex = "".format("0x%x", bytes[i / 2]);
        }
        return bytes;
    }
    

    尝试使用以下测试用例执行相同的操作会导致 InvalidKeySpecException

    @Test
    public void pkConversionTest() throws NoSuchAlgorithmException, InvalidKeySpecException {
        ECDSA.setDebug(true);
        byte[] pk = hexStringToByteArray("049a6b0ac242afe41128cf59736412686ca83c9e902ee3fa0f13810b9d59ebfe5e49204427c23b630be12ae33815b0bda6ed8d0603386c6ea5f1906cdb0e731286");
        PublicKey pub = ECDSA.getPublicKey(pk);
        System.out.println(pub);
    }
    

    java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): lengthTag=26, too big.
    

    不过,我能够产生一个 KeyPair 使用 并使用 verify

    3059301306072a8648ce3d020106082a8648ce3d0301070342000425a321d5a1a74e6c04a6e3cab030401f3dbc04d5242f9bc629175c3d3988799175eb80cd96d7e76ea924630a8d86b93c54dec7cb965b58de31705eb3343846a1
    

    如何将nodejs生成的公钥格式化为X.509格式,以便在java端使用?

    编辑:

    3059301306072a8648ce3d020106082a8648ce3d030107034200 java . 由 Prefix 节点 由于长度较小,似乎可以解决问题。但是有人能解释为什么吗?

    非常感谢。

    1 回复  |  直到 6 年前
        1
  •  2
  •   dave_thompson_085    6 年前

    但是有人能解释为什么吗?

    Java以“X.509”格式对公钥进行编码 SubjectPublicKeyInfo 结构(SPKI)由X.509/PKIX定义;参见rfc5280、rfc3279,对于ECC,特别是rfc5480。这就是为什么传递给密钥工厂的数据位于名为 X509EncodedKeySpec . 这个ASN.1结构包含一个 AlgorithmIdentifier 它标识所使用的算法及其参数(对于ECC,它是所使用的曲线/组,在您的例子中是标识prime256 aka P-256 aka secp256r1的OID)加上包含实际编码公钥值的位字符串类型(对于ECC,它是X9.62格式的点,它有几个变体;这里您使用的是未压缩的;根据 the doc

    您的“前缀”是ASN.1外部序列的DER编码、AlgorithmIdentifier、标记长度和padcount,它们从包含publickey点的位字符串开始。

    基本上是欺骗:
    * How can I get a PublicKey object from EC public key bytes?
    * Loading raw 64-byte long ECDSA public key in Java
    * How can I generate a valid ECDSA EC key pair?

    仅供参考:实际上,同样的问题也发生在RSA身上,而且在这方面有更多的Qs。 与特定于算法的格式相比,通用PKCS8格式的私钥也存在类似的问题,但由于公钥通常与其他系统和/或程序交换,而私钥通常不具有互操作性,因此私钥编码的互操作性就不太常受到关注。