【问题标题】:The signing key's algorithm 'SHA256withECDSA' does not equal a valid HmacSHA* algorithm name and cannot be used with HS512签名密钥的算法“SHA256withECDSA”不等于有效的 HmacSHA* 算法名称,不能与 HS512 一起使用
【发布时间】:2021-07-14 23:05:43
【问题描述】:

现在我正在使用此代码生成 JWT 令牌(我正在使用 jjwt lib),这是我的代码迷你示例:

package com.dolphin.soa.post;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author dolphin
 */
public class MiniExample {

    public static void main(String[] args) {
        Map<String,Object> jwtHeader = new HashMap<>();
        jwtHeader.put("alg","ES256");
        jwtHeader.put("kid","YDKL424AF9");
        jwtHeader.put("typ","JWT");
        Map<String,Object> appleJwtPayload = new HashMap<>();
        appleJwtPayload.put("iss","5fb8e836-27d7-4390-8f40-008acd64a29d");
        appleJwtPayload.put("iat",System.currentTimeMillis() / 1000L);
        appleJwtPayload.put("exp",System.currentTimeMillis() / 1000L + 60 * 15);
        appleJwtPayload.put("aud","appstoreconnect-v1");
        appleJwtPayload.put("nonce",UUID.randomUUID().toString());
        appleJwtPayload.put("bid","com.earth.dolphin");
        String appleKey = "-----BEGIN EC PRIVATE KEY-----\n" +
                "MHcCAQEEIDxLR4/hZpkIor0r2OlxhG2cVPOjD63jUPFaiRzGomvgoAoGCCqGSM49\n" +
                "AwEHoUQDQgAE4B1UkwnabK3TqzeIakdWD3EVdA+IQUlsQFGP/fkLjrbyDxtADpi0\n" +
                "JuGLvtKnw/vGEkyAnXethxpebzDCcfdWGQ==\n" +
                "-----END EC PRIVATE KEY-----";
        SecretKey secretKey = new SecretKeySpec(appleKey.getBytes(), SignatureAlgorithm.ES256.getJcaName());
        String accessToken = Jwts.builder()
                .setClaims(appleJwtPayload)
                .setHeader(jwtHeader)
                .signWith(secretKey)
                .compact();
        System.out.println("Access Token:" + accessToken);
    }
}

但是当我在服务器端运行此代码时,显示此错误:

 Connected to the target VM, address: '127.0.0.1:57457', transport: 'socket'
Exception in thread "main" io.jsonwebtoken.security.InvalidKeyException: The signing key's algorithm 'SHA256withECDSA' does not equal a valid HmacSHA* algorithm name and cannot be used with HS512.
    at io.jsonwebtoken.SignatureAlgorithm.assertValid(SignatureAlgorithm.java:373)
    at io.jsonwebtoken.SignatureAlgorithm.assertValidSigningKey(SignatureAlgorithm.java:315)
    at io.jsonwebtoken.impl.DefaultJwtBuilder.signWith(DefaultJwtBuilder.java:122)
    at io.jsonwebtoken.impl.DefaultJwtBuilder.signWith(DefaultJwtBuilder.java:115)
    at com.dolphin.soa.post.MiniExample.main(MiniExample.java:39)
Disconnected from the target VM, address: '127.0.0.1:57457', transport: 'socket'

Process finished with exit code 1

哪里出了问题,我应该怎么做才能让它工作?我试过转换苹果p8文件生成私钥?

 openssl ec -in AuthKey_Y24AF9.p8 -out au_private.p8

仍然没有解决这个问题。我在这个问题上苦苦挣扎了一段时间,并从互联网上搜索,但仍然没有任何线索。

【问题讨论】:

    标签: java jwt


    【解决方案1】:

    TLDR:私钥和秘钥不同

    在过去的半个世纪中,使用了两种截然不同的数字密码学:传统的或对称的,也称为传统的,以及公钥或非对称的。在 Java 加密以及 jjwt 中,SecretKey[Spec] 类及其子类仅用于对称算法,包括 HMAC,而PrivateKey[Spec] 类及其子类仅用于非对称算法,包括 ECDSA。请参阅https://github.com/jwtk/jjwt#signing-key——您必须为 HSnnn 使用合适大小的 SecretKey,但为 RSnnn PSnnn 或 ESnnn 使用正确算法(RSA 或 EC)合适大小的 PrivateKey。因为您给 jjwt 一个 SecretKeySpec,它会尝试使用它知道的唯一可用于密钥的签名算法,即 HMAC,但您指定的类型对于密钥无效,因此对于 HMAC。

    而且您的密钥不是 PKCS8。

    您的私钥采用 OpenSSL 特定的“传统”或“传统”格式,这是由 SECG in SEC1 首先定义的,后来由 RFC5915 正式化(如果这是一个词)。 Java 加密不直接支持(几种)OpenSSL 传统格式,仅支持发布为 RFC5208 并以新名称 RFC5958 更新的行业标准 PKCS8 格式。

    您说您的文件名具有扩展名.p8,这表明有人认为它是或应该是PKCS8,但实际上不是。您询问的命令openssl ec 不会转换为 PKCS8,实际上相反:如果您给它一个 PKCS8 格式的输入,它将产生传统格式的输出,这正是您不想要的。如果您能够在使用之前修复密钥,则有两种选择:

     openssl pkey -in trad_pem_file -out pkcs8_pem_file
     # only in 1.0.0 up, but that's been 11 years and pretty much everyone has it now
    

    这会产生 PKCS8 格式,但在 Java 不直接支持的 PEM 'armor' 中,因此要阅读它,您必须:删除 BEGINEND 行;将base64部分解码为二进制(Java中的byte[])忽略换行符,这可以通过在解码之前删除它们或使用忽略它们的j8+ java.util.Base64.getMimeDecoder()来完成;最后将结果通过一个合适的KeyFactory:

     PrivateKey key = KeyFactory.getInstance("EC").generatePrivateKey(new PKCS8EncodedKeySpec(decodedbase64));
     # note KeyFactory is for asymmetric algorithms (and private and public keys) only
     # while SecretKeyFactory is for symmetric algorithms and secret keys,
     # but KeyGenerator is for symmetric algorithms and secret keys
     # while KeyPairGenerator is for asymmetric algorithsm and private/public keys
     # I think this is to keep you on your toes
    

    OpenSSL 中的另一个(旧)转换是:

     openssl pkcs8 -topk8 -nocrypt -in trad_pem_file -out pkcs8_file [-outform pem|der]
    

    使用默认输出“pem”,这与上面的pkey 相同;使用“der”,它会为您执行 PEM 解码步骤并创建一个可以由 Java 直接读取和使用的二进制文件:

     byte[] p8der = Files.readAllBytes(Paths.of("pkcs8_der_file"));
     PrivateKey key = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(p8der));
    

    或者您可以通过各种方式将其嵌入到您的源代码中。

    如果您无法将密钥转换为 pkcs8 并且需要您的程序来处理传统格式,这是可能的,但更复杂 - 并且之前已经被问过并回答过很多次。等我有时间再挖掘一些。

    【讨论】:

      【解决方案2】:

      感谢 dave_thompson_085 患者,我正在使用以下代码解决问题:

      public static String generateToken(AppleJwtHeader jwtHeader, AppleJwtPayload jwtPayload ) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
              byte[] p8der = Files.readAllBytes(Path.of("/opt/apps/dolphin-post/AuthKey_YDKL424AF9.p8"));
              PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(new org.apache.commons.codec.binary.Base64().decode(p8der));
      
              PrivateKey appleKey = KeyFactory.getInstance("EC").generatePrivate(priPKCS8);
              ObjectMapper objectMapper = new ObjectMapper();
              Map claims = objectMapper.convertValue(jwtPayload, Map.class);
              Map header = objectMapper.convertValue(jwtHeader,Map.class);
              String accessToken = Jwts.builder()
                      .setClaims(claims)
                      .setHeader(header)
                      .signWith(appleKey)
                      .compact();
              return accessToken;
          }
      

      希望对其他人有所帮助。

      【讨论】:

      • 我正在尝试使用相同的代码来访问 App Store API。但是,我看到上述代码出现以下错误......有什么想法吗? java.security.InvalidKeyException: IOException : DerInputStream.getLength(): lengthTag=111, too big.
      • 当我删除页眉 (-----BEGIN PRIVATE KEY-----)、页脚 (-----END PRIVATE KEY-----) 和换行符后,我的问题就消失了。这些是我对从 Apple 下载的 .p8 所做的唯一修改。
      猜你喜欢
      • 2020-09-10
      • 2021-01-08
      • 2020-12-27
      • 1970-01-01
      • 2013-12-09
      • 1970-01-01
      • 1970-01-01
      • 2017-10-14
      • 1970-01-01
      相关资源
      最近更新 更多