本文介绍了Java Bouncy Castle生成的ES256密钥不适用于JWT.io的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在生成如下所示的密钥对:

 public static void main(String args[]) throws Exception{

    StringWriter pemStrWriter = new StringWriter();
    JcaPEMWriter pemWriter = new JcaPEMWriter(pemStrWriter);


    Security.addProvider(new BouncyCastleProvider());
    KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
    ECGenParameterSpec spec = new ECGenParameterSpec("secp256r1");
    g.initialize(spec);
    KeyPair keyPair = g.generateKeyPair();

    pemWriter.writeObject(new JcaPKCS8Generator(keyPair.getPrivate(), null));
    pemWriter.close();
    BufferedWriter writer = new BufferedWriter(new FileWriter("privatekeyjca.pem"));
    writer.write(pemStrWriter.toString());
    writer.close();

    BufferedWriter writer2 = new BufferedWriter(new FileWriter("publickeyjca.pem"));
    StringWriter pemStrWriter2 = new StringWriter();
    JcaPEMWriter pemWriter2 = new JcaPEMWriter(pemStrWriter2);
    pemWriter2.writeObject(keyPair.getPublic());
    pemWriter2.close();
    writer2.write(pemStrWriter2.toString());
    writer2.close();
}

以下是我生成的私钥:

-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgVBnFvRMRyO418Oeb
z1YI778gLVNZJn0YI+atgDhTsPagCgYIKoZIzj0DAQehRANCAAQxzPBfVxJfosNl
3tJc+pD0tpftsEy2hWmLc5EK7QbSAtXqqVL2/Zn6JxMbkueRpvIl1/Ag0NvBbnv+
OJfWY2ws
-----END PRIVATE KEY-----

以下是我生成的公钥:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMczwX1cSX6LDZd7SXPqQ9LaX7bBM
toVpi3ORCu0G0gLV6qlS9v2Z+icTG5LnkabyJdfwINDbwW57/jiX1mNsLA==
-----END PUBLIC KEY-----

当我转到JWT.io并尝试生成JWT时,我选择算法为ES256,并放入我的私钥,但它没有任何作用.但是,如果我使用通过openssl命令生成的私钥,它确实会给我一个JWT.

您能告诉我使用Java生成的密钥有什么问题吗?

解决方案

jwt.io正在使用的任何代码都是易碎的.

当将PKCS8用于"EC"(X9.62样式,ECDSA和/或ECDH和/或相关)私钥时,算法相关部分使用SEC1附录C.4定义的结构,该结构由 https://secg.org :

ECPrivateKey ::= SEQUENCE {
    version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
    privateKey OCTET STRING,
    parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL,
    publicKey [1] BIT STRING OPTIONAL
}

如您所见,第三个和第四个元素是可选的.当 OpenSSL 编写此结构时,它省略了第三个元素(参数),因为它与外部PKCS8的AlgorithmIdentifier冗余,但包含了第四个元素(publicKey),因为尽管从技术上来说它还是有用的. /p>

Java中的BouncyCastle包括两者,而(Oracle/OpenJDK)标准提供者SunEC都不包括.比较 https://crypto.stackexchange.com/questions/80275/converting-raw-ecc-private-key-into-asn-1-der-encoded-key/#80290 ,包括我的评论.似乎正在运行的任何代码jwt.io -都没有说,我也没有想清楚-假设OpenSSL仅使用组合,而没有使用其他组合,则编码为解析EC私钥文件适用于Bouncy或SunEC格式.

将SunEC格式转换为OpenSSL有点工作-您实际上需要进行dG的标量乘法,尽管使用Bouncy并不难. OTOH,因为您具有Bouncy,只需简单地从SEC1结构中省略参数即可将Bouncy格式转换为OpenSSL:

//nopackage
import java.io.OutputStreamWriter;
import java.security.*;
import java.security.spec.ECGenParameterSpec;

import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.sec.ECPrivateKey;
import org.bouncycastle.openssl.PKCS8Generator;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
import org.bouncycastle.util.io.pem.PemObject;

public class SO61676744ECKeyNoParam {
    public static void main (String[] args) throws Exception {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        JcaPEMWriter wr = new JcaPEMWriter(new OutputStreamWriter(System.out));

        KeyPairGenerator gen = KeyPairGenerator.getInstance("EC","BC");
        gen.initialize(new ECGenParameterSpec("secp256r1"));
        KeyPair key = gen.generateKeyPair();
        PrivateKeyInfo badp8 = PrivateKeyInfo.getInstance(key.getPrivate().getEncoded());
        ECPrivateKey badsec = ECPrivateKey.getInstance(badp8.parsePrivateKey());
        ECPrivateKey goodsec = new ECPrivateKey(256, badsec.getKey(), badsec.getPublicKey(), null);
        PrivateKeyInfo goodp8 = new PrivateKeyInfo(badp8.getPrivateKeyAlgorithm(), goodsec);
        wr.writeObject(new PemObject("PRIVATE KEY", goodp8.getEncoded()));
        wr.writeObject(key.getPublic());

        wr.close();
    }
}

I am generating a keypair like below:

 public static void main(String args[]) throws Exception{

    StringWriter pemStrWriter = new StringWriter();
    JcaPEMWriter pemWriter = new JcaPEMWriter(pemStrWriter);


    Security.addProvider(new BouncyCastleProvider());
    KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
    ECGenParameterSpec spec = new ECGenParameterSpec("secp256r1");
    g.initialize(spec);
    KeyPair keyPair = g.generateKeyPair();

    pemWriter.writeObject(new JcaPKCS8Generator(keyPair.getPrivate(), null));
    pemWriter.close();
    BufferedWriter writer = new BufferedWriter(new FileWriter("privatekeyjca.pem"));
    writer.write(pemStrWriter.toString());
    writer.close();

    BufferedWriter writer2 = new BufferedWriter(new FileWriter("publickeyjca.pem"));
    StringWriter pemStrWriter2 = new StringWriter();
    JcaPEMWriter pemWriter2 = new JcaPEMWriter(pemStrWriter2);
    pemWriter2.writeObject(keyPair.getPublic());
    pemWriter2.close();
    writer2.write(pemStrWriter2.toString());
    writer2.close();
}

Below is my private key generated:

-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgVBnFvRMRyO418Oeb
z1YI778gLVNZJn0YI+atgDhTsPagCgYIKoZIzj0DAQehRANCAAQxzPBfVxJfosNl
3tJc+pD0tpftsEy2hWmLc5EK7QbSAtXqqVL2/Zn6JxMbkueRpvIl1/Ag0NvBbnv+
OJfWY2ws
-----END PRIVATE KEY-----

Below is my public key generated:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMczwX1cSX6LDZd7SXPqQ9LaX7bBM
toVpi3ORCu0G0gLV6qlS9v2Z+icTG5LnkabyJdfwINDbwW57/jiX1mNsLA==
-----END PUBLIC KEY-----

When I go to JWT.io, and try to generate a JWT, I select the algorithm as ES256, and put my private key, it doesn't give anything. But if I use a private key generated via openssl commands, it does give me a JWT.

Can you please tell me what is wrong with my keys generated using Java.

解决方案

Whatever code jwt.io is using is unnecessarily fragile.

When using PKCS8 for an 'EC' (X9.62-style, ECDSA and/or ECDH and/or related) private key, the algorithm-dependent part uses the structure defined by appendix C.4 of SEC1 from https://secg.org :

ECPrivateKey ::= SEQUENCE {
    version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
    privateKey OCTET STRING,
    parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL,
    publicKey [1] BIT STRING OPTIONAL
}

As you can see, the third and fourth elements are optional. When OpenSSL writes this structure, it omits the third element (parameters) because it's redundant with the AlgorithmIdentifier of the outer PKCS8, but includes the fourth element (publicKey) because although technically redundant it can be useful.

BouncyCastle in Java includes both, while the (Oracle/OpenJDK) standard provider SunEC includes neither. Compare https://crypto.stackexchange.com/questions/80275/converting-raw-ecc-private-key-into-asn-1-der-encoded-key/#80290 including my comment. It appears that whatever code jwt.io is running -- it doesn't say and I didn't try to figure out -- is coded to parse EC privatekey files assuming the combination OpenSSL uses only and nothing else, and thus doesn't work for either the Bouncy or SunEC format.

It's a little work to convert the SunEC format to the OpenSSL -- you need to actually do the scalar multiplication of dG, although with Bouncy this isn't too hard. OTOH since you have Bouncy, converting the Bouncy format to the OpenSSL, by simply omitting the parameters from the SEC1 structure, is fairly easy:

//nopackage
import java.io.OutputStreamWriter;
import java.security.*;
import java.security.spec.ECGenParameterSpec;

import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.sec.ECPrivateKey;
import org.bouncycastle.openssl.PKCS8Generator;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
import org.bouncycastle.util.io.pem.PemObject;

public class SO61676744ECKeyNoParam {
    public static void main (String[] args) throws Exception {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        JcaPEMWriter wr = new JcaPEMWriter(new OutputStreamWriter(System.out));

        KeyPairGenerator gen = KeyPairGenerator.getInstance("EC","BC");
        gen.initialize(new ECGenParameterSpec("secp256r1"));
        KeyPair key = gen.generateKeyPair();
        PrivateKeyInfo badp8 = PrivateKeyInfo.getInstance(key.getPrivate().getEncoded());
        ECPrivateKey badsec = ECPrivateKey.getInstance(badp8.parsePrivateKey());
        ECPrivateKey goodsec = new ECPrivateKey(256, badsec.getKey(), badsec.getPublicKey(), null);
        PrivateKeyInfo goodp8 = new PrivateKeyInfo(badp8.getPrivateKeyAlgorithm(), goodsec);
        wr.writeObject(new PemObject("PRIVATE KEY", goodp8.getEncoded()));
        wr.writeObject(key.getPublic());

        wr.close();
    }
}

这篇关于Java Bouncy Castle生成的ES256密钥不适用于JWT.io的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-03 13:01