【问题标题】:InvalidKeySpecException data isn't an object ID (tag = -96)InvalidKeySpecException 数据不是对象 ID(标记 = -96)
【发布时间】:2019-01-28 07:20:20
【问题描述】:

我在 Azure 门户免费订阅上获得了一个访问令牌,标题是:

{
  "typ": "JWT",
  "alg": "RS256",
  "x5t": "7_Zuf1tvkwLxYaHS3q6lUjUYIGw",
  "kid": "7_Zuf1tvkwLxYaHS3q6lUjUYIGw"
}

所以我从here 得到了 x5c,然后把

-----开始证书----- MIIDBTCCAe......cNpO9oReBUsX -----结束证书-----

    ze7xq1zGljQihJgcNpO9oReBUsX

https://jwt.io/,签名已验证。

但是,当我尝试使用 jjwt 和 jose4j 使用 JDK1.8 验证签名时,按照this refrence 中的步骤,我在线上遇到了以下异常

PublicKey publicKey = keyFactory.generatePublic(keySpec);

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: ObjectIdentifier() -- data isn't an object ID (tag = -96)
at java.base/sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:204)
at java.base/java.security.KeyFactory.generatePublic(KeyFactory.java:352)
at com.ipscape.api.v1_0.external.other.JwtExample.decodeJwt(JwtExample.java:41)
at com.ipscape.api.v1_0.external.other.JwtExample.main(JwtExample.java:72)
Caused by: java.security.InvalidKeyException: IOException: ObjectIdentifier() -- data isn't an object ID (tag = -96)
at java.base/sun.security.x509.X509Key.decode(X509Key.java:396)
at java.base/sun.security.x509.X509Key.decode(X509Key.java:401)
at java.base/sun.security.rsa.RSAPublicKeyImpl.<init>(RSAPublicKeyImpl.java:86)
at java.base/sun.security.rsa.RSAKeyFactory.generatePublic(RSAKeyFactory.java:297)
at java.base/sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:200)

使用 jose4j 的代码:

        String publicKeyPEM =
        "MIIDBTCCAe2gAwIBAgIQE7nbxEiAlqhFdKnsKV+nuTANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE4MDYyMTAwMDAwMFoXDTIwMDYyMTAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL1cxvfI3wPu1HFGZL6wnSHfcqX6DlvezLF1wbqUs6/DS5GhxtvHk6+9nKyNuCjznkEKql6bTIgnUbvTPr0UUKa3hezq/D9nkGu4qX8LVONVWkb53mjnN45lnVfOLh6VQ7J8/9Ut/ybbnhcn2a12vWiR0c6899TgtRp+i0bkT9dYl+3/wfP8+bBqmolwno7yojv/DxMz86mzhS7lW6mS9zzYtdsy6IHcF2P9XIac2TaP0efUpLrQY81wuTE3gUh2s6j7tqNH7aKK2PNAuxXtyGtZ9r+bg0gMrDXXVKJylQO9m4Z5J0vuz8xgCElLg3uJPOI8q/j0YrLe5rHy67ACg0MCAwEAAaMhMB8wHQYDVR0OBBYEFDnSNW3pMmrshl3iBAS4OSLCu/7GMA0GCSqGSIb3DQEBCwUAA4IBAQAFs3C5sfXSfoi7ea62flYEukqyVMhrDrpxRlvIuXqL11g8KEXlk8pS8gEnRtU6NBeHhMrhYSuiqj7/2jUT1BR3zJ2bChEyEpIgOFaiTUxq6tXdpWi/M7ibf8O/1sUtjgYktwJlSL6FEVAMFH82TxCoTWp2g5i2lmZQ7KxiKhG+Vl9nw1bPX57hkWWhR7Hpes0MbpGNZI2IEpZSjNG1IWPPOBcaOh4ed2WBQcLcaTuAaELlaxanQaC0B3029To80MnzpZuadaul3+jN7JQg0MpHdJJ8GMHAWe/IjXc0evJNhVUcKON41hzTu0R+Sze7xq1zGljQihJgcNpO9oReBUsX";

    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyPEM.getBytes()));
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PublicKey publicKey = keyFactory.generatePublic(keySpec);

    JwtConsumer jwtConsumer = new JwtConsumerBuilder()
        .setRequireExpirationTime()
        .setVerificationKey(publicKey)
        .build();

【问题讨论】:

标签: java azure-active-directory token


【解决方案1】:

这行得通:

        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
        .setRequireExpirationTime()
        .setExpectedAudience("https://example.com/")
        .setVerificationKey(publicKey)
        .build();

    JwtClaims jwtDecoded = null;
    jwtDecoded = jwtConsumer.processToClaims(token);

    Map<String, Object> jwtClaims = jwtDecoded.getClaimsMap();
    String iss = (String) jwtClaims.get("iss");

【讨论】:

    【解决方案2】:

    x5c is a (X.509) certificate, not a public key. 实际上它是一个证书数组,必要时形成一个链,虽然这个例子是一个单一的自签名证书。证书的 PEM 标题和尾行应该全部大写“BEGIN CERTIFICATE”和“END CERTIFICATE”,并且 base64 应该添加换行符,这与 JWT 格式不同,后者是 base64 但不是 PEM。

    要阅读证书,请使用java.security.cert.CertificateFactory。 (这也可以处理一个链,作为它调用 PkiPath 的证书序列,或一个标准的(通常称为 p7b 或 p7c)的(普通)PKCS7 消息。)要么给它正确 PEM 格式,或正确的二进制/DER 格式;后者更容易,因为它只是对 JWT x5c 格式进行 base64 解码(正如您现在所做的那样,在将其放入错误的密钥规范之前,但 java.util.Base64.Decoder 不需要以字节为单位的 base64,它可能需要String)。如果您只需要公钥,则可以从证书中获取它。执行以下操作:

    String certb64 = "...";
    byte[] certder = Base64.getDecoder().decode(certb64);
    InputStream certstream = new ByteArrayInputStream (certder);
    Certificate cert = CertificateFactory.getInstance("X.509").generateCertificate(certstream);
    PublicKey key = cert.getPublicKey();
    

    【讨论】:

    • 感谢@dave_thompson_085,我在获取PublicKey后添加了以下代码,`Claims body = null; JwtParser jwtParser = Jwts.parser().setSigningKey(key.getEncoded()); Jws 声明Jws=jwtParser.parseClaimsJws(token); body =claimsJws.getBody();` 然后得到Exception in thread "main" java.lang.IllegalArgumentException: Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance. at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541)
    • 即io/jsonwebtoken/jjwt/0.9.0/jjwt-0.9.0-sources.jar!/io/jsonwebtoken/impl/DefaultJwtParser.java Assert.isTrue(algorithm.isHmac(), "只能为 HMAC 签名指定密钥字节。请指定 PublicKey 或 PrivateKey 实例。");但算法 =“RS256”,而不是 HMAC。有什么事吗?
    • @Henryips 我想说的是,请在您的 Q 中对您的 Q 进行更改,因为 Stack 政策是 cmets 可以随时删除,尤其是代码不可读 - 但您已经找到了答案可能没有人需要阅读你的 Q。
    猜你喜欢
    • 1970-01-01
    • 2021-12-13
    • 1970-01-01
    • 2017-05-21
    • 2023-02-13
    • 2013-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-11
    相关资源
    最近更新 更多