【问题标题】:Generate JWT token with ECDSA private key使用 ECDSA 私钥生成 JWT 令牌
【发布时间】:2022-11-18 18:37:45
【问题描述】:

尝试使用 ECDSA 私钥生成签名的 JWT 令牌但出错

使用链接生成公钥和私钥

https://techdocs.akamai.com/iot-token-access-control/docs/generate-jwt-ecdsa-keys

 private String doGenerateToken(Map<String, Object> claims, String subject)
      throws NoSuchAlgorithmException, InvalidKeySpecException {
    Security.addProvider(new BouncyCastleProvider());
    String EC_PRIVATE_KEY_STR = "-----BEGIN EC PRIVATE KEY-----\n"
        + "MHQCAQEEIBuSmY4MFZ938j0sno1nOICb0ScfIebC1O7DXkvf6UDMoAcGBSuBBAAK\n"
        + "oUQDQgAELAWORZuUv+lpO34bVoYHv6T3Gey+GtuHFB+TH1+l0tRKfKELHcmHlDOK\n"
        + "ebiIegDVhHd6jYx2yT1nOBddjDHCVw==\n"
        + "-----END EC PRIVATE KEY-----\n";
    final KeyFactory keyPairGenerator = KeyFactory.getInstance("EC"); // EC is ECDSA in Java
    ECPrivateKey EC_PRIVATE_KEY = (ECPrivateKey) keyPairGenerator.generatePrivate(
        new PKCS8EncodedKeySpec(
            Base64.decodeBase64(removeEncapsulationBoundaries(EC_PRIVATE_KEY_STR))));

    var currentDateTime = new Date(System.currentTimeMillis());
    final String jwt = Jwts.builder()
        .setHeaderParam("kid", "any")
        .signWith(SignatureAlgorithm.ES256, EC_PRIVATE_KEY)
        .compact();
    return jwt;
  }

例外 :

Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : version mismatch: (supported:     00, parsed:     01

【问题讨论】:

    标签: java java-8 jwt


    【解决方案1】:

    Java PKCS8EncodedKeySpec 需要 PKCS8 格式的密钥(特别是 PKCS8-clear);这就是为什么名字说 PKCS8。这不是 PKCS8 格式的密钥所以它失败了。

    要读取密钥:

    如果您使用 OpenSSL 生成密钥,根据您链接的网站(尽管在上一页),最简单的方法是使用 OpenSSL 进行转换:

    $ cat ecprivate-sec1
    -----BEGIN EC PRIVATE KEY-----
    MHQCAQEEIBuSmY4MFZ938j0sno1nOICb0ScfIebC1O7DXkvf6UDMoAcGBSuBBAAK
    oUQDQgAELAWORZuUv+lpO34bVoYHv6T3Gey+GtuHFB+TH1+l0tRKfKELHcmHlDOK
    ebiIegDVhHd6jYx2yT1nOBddjDHCVw==
    -----END EC PRIVATE KEY-----
    $ openssl pkey <ecprivate-sec1 >ecprivate-pkcs8
    $ cat ecprivate-pkcs8
    -----BEGIN PRIVATE KEY-----
    MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgG5KZjgwVn3fyPSyejWc4
    gJvRJx8h5sLU7sNeS9/pQMyhRANCAAQsBY5Fm5S/6Wk7fhtWhge/pPcZ7L4a24cU
    H5MfX6XS1Ep8oQsdyYeUM4p5uIh6ANWEd3qNjHbJPWc4F12MMcJX
    -----END PRIVATE KEY-----
    

    如果您有旧版 (0.9.x) 的 OpenSSL,请改用 openssl pkcs8 -topk8 -nocrypt

    Java 将接受删除 BEGIN/END 行并取消 base64 的该值。

    或者OpenSSL可以直接生成这种格式;而不是 ecparam -name secp256k1 -genkey -noout ... 使用:

    openssl genpkey -algorithm ec -pkeyopt ec_paramgen_curve:secp256k1 >ecprivate-pkcs8
    # but see below
    

    (你也可以使用任何一个ec -puboutpkey -pubout 获取公钥,以及 OpenSSL 用于公钥的格式, 在 de-PEM-ing 之后,Java 想要的“X.509”格式。)

    或者,如果 BouncyCastle 可用包含bcpkix,可以处理“传统的”SEC1/rfc5915 格式在质子交换膜中直接地。使用上面的 EC_PRIVATE_KEY_STR 值只需执行以下操作:

    PrivateKey privkey = new JcaPEMKeyConverter().getKeyPair (
        (PEMKeyPair) new PEMParser(new StringReader(EC_PRIVATE_KEY_STR)).readObject() 
        ).getPrivate();
    

    (如果你愿意但不需要,你可以声明并转换为ECPrivateKey)。

    但这仍然是错误的。

    我不知道是否有人在该网站上做过质量控制,但是JWS ES256 uses ECDSA with P-256,曲线也称为 secp256r1 和 prime256v1,不是 secp256k1这是一条完全不同且错误的曲线。相反,当使用 OpenSSL 生成时,您应该指定 P-256prime256v1

    他们给出的使用命令行 openssl 进行签名的示例没有捕捉到这一点,因为 OpenSSL 是一种通用工具,不仅用于 JOSE/JWS,而且用于某些(其他)事物,secp256k1 是正确的。更令我惊讶的是,jjwt——至少我拥有的稍旧的版本——也没有捕捉到这一点。然而,结果不符合标准,不会被其他系统可靠地接受。

    【讨论】:

      猜你喜欢
      • 2020-02-03
      • 1970-01-01
      • 1970-01-01
      • 2019-08-27
      • 1970-01-01
      • 2019-10-18
      • 2018-11-14
      • 1970-01-01
      相关资源
      最近更新 更多