【问题标题】:Java Bouncy Castle generated ES256 key doesn't work with JWT.ioJava Bouncy Castle 生成的 ES256 密钥不适用于 JWT.io
【发布时间】:2020-08-23 20:50:02
【问题描述】:

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

 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 生成的密钥有什么问题。

【问题讨论】:

    标签: java security openssl jwt bouncycastle


    【解决方案1】:

    jwt.io 使用的任何代码都是不必要的脆弱。

    当将 PKCS8 用于“EC”(X9.62 样式、ECDSA 和/或 ECDH 和/或相关)私钥时,算法相关部分使用@987654321 的 SEC1 的附录 C.4 定义的结构@:

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

    如您所见,第三个和第四个元素是可选的。当 OpenSSL 编写此结构时,它省略了第三个元素(参数),因为它与外部 PKCS8 的 AlgorithmIdentifier 冗余,但包含第四个元素(publicKey),因为虽然在技术上是多余的,但它可能很有用。

    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 正在运行的任何代码——它没有说,我也没有试图弄清楚——被编码为解析 EC 私钥文件,假设 OpenSSL 仅使用而不使用其他组合,因此不适用于 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();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多