【问题标题】:Google OAuth2 JWT token verification exceptionGoogle OAuth2 JWT 令牌验证异常
【发布时间】:2015-08-27 03:01:24
【问题描述】:

我在上一小时面临 OAuth2 JWT 令牌验证异常(所以没有人可以访问我的应用程序):

java.security.SignatureException:签名长度不正确:得到 256 但预期为 128。我正在使用 google-http-client 1.20.0Java 1.7.0。到目前为止,相同的配置有效 - 有什么想法吗?

Stacktrace

java.security.SignatureException: Signature length not correct: got 256 but was expecting 128
    at sun.security.rsa.RSASignature.engineVerify(Unknown Source) ~[na:1.7.0_45]
    at java.security.Signature$Delegate.engineVerify(Unknown Source) ~[na:1.7.0_45]
    at java.security.Signature.verify(Unknown Source) ~[na:1.7.0_45]
    at com.google.api.client.util.SecurityUtils.verify(SecurityUtils.java:164) ~[google-http-client-1.20.0.jar:1.20.0]

【问题讨论】:

  • 使用 Java 1.8.0_45 也有同样的问题。
  • 当我使用来自谷歌客户经理账户的访问令牌时,我在谷歌应用引擎上得到了这个。 (一小时前才开始搞定他们)
  • @user3686724 您为 GoogleIdTokenVerifier 设置的受众是什么?您使用客户端 ID 还是令牌 ID? (过去 60 分钟我们一直遇到同样的问题)
  • 我也有同样的问题,看起来是 Google API 问题。您知道在哪里投诉并找到相关信息吗?
  • 感谢大家的报告和解决方法。这已被确认为 Google 问题,我们的后端团队将优先处理。

标签: java jwt java-security google-http-client


【解决方案1】:

这里同样的问题,我把GoogleIdTokenVerifier的源代码加到我的项目中,改了方法:

 public boolean verify(GoogleIdToken googleIdToken) throws GeneralSecurityException, IOException {
    // check the payload
    if (!super.verify(googleIdToken)) {
      return false;
    }
    // verify signature
    for (PublicKey publicKey : publicKeys.getPublicKeys()) {
      try {
        if (googleIdToken.verifySignature(publicKey)) {
            return true;
          }
    } catch (Exception e) {
        System.err.println("Verify Token:" + e);
    }
    }
    return false;
  }

只处理异常,第二个证书工作正常。

编辑:如果你想让它更干净,你可以按照Erik-z的建议进行子类化:

编辑 2:我无法使用下面的代码使其工作,我将坚持上面的丑陋 hack。

package com.my.project.package;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PublicKey;

import com.google.api.client.auth.openidconnect.IdTokenVerifier;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;

// Remember to remove this class later by making it deprecated
@Deprecated
public class GoogleIdTokenVerifier2 extends GoogleIdTokenVerifier {

    // Add constructors as needed
    public GoogleIdTokenVerifier2(HttpTransport transport, JsonFactory jsonFactory) {
        super(transport, jsonFactory);
    }

    @Override
    public boolean verify(GoogleIdToken googleIdToken) throws GeneralSecurityException, IOException {
        // check the payload
        if (!((IdTokenVerifier)this).verify(googleIdToken)) {
            return false;
        }
        // verify signature
        for (PublicKey publicKey : getPublicKeysManager().getPublicKeys()) {
            try {
                if (googleIdToken.verifySignature(publicKey)) {
                    return true;
                }
            } catch (Exception e) {
                System.err.println("Verify Token:" + e);
            }
        }
        return false;
    }
}

【讨论】:

    【解决方案2】:

    不要认为这是最终解决方案,但绝对可行的临时解决方法是将验证者的受众更改为 tokenId。

    GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory).setAudience(Arrays.asList(clientId)).build();
    

    GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
                        .setAudience(Arrays.asList(tokenResponse.getIdToken())).build();
    

    【讨论】:

    • 谢谢,Ittai!这种解决方法也适用于我的代码。为我的公司解决严重的生产问题提供了很好的权宜之计。
    • 很高兴听到 :-) 我认为,但不确定,这实际上只是关闭了验证。谷歌可能加强了它的证书检查,这就是导致突然激增的原因。
    【解决方案3】:

    根本原因在于 Google 方面,JSON 中的证书顺序错误:

    https://www.googleapis.com/oauth2/v1/certs

    你可以调整它们的顺序,像这样:

    http://test.gacivs.info/frontend/certs.json

    之后,您可以使用 GooglePublicKeysManager.setPublicCertsEncodedUrl(...) 方法指定 JSON 的自定义 URL(或使用我的 :):

    final GoogleIdToken idToken = GoogleIdToken.parse(JSON_FACTORY, token);
    final GooglePublicKeysManager manager = new GooglePublicKeysManager.Builder(HTTP_TRANSPORT, JSON_FACTORY).setPublicCertsEncodedUrl(CUSTOM_CERTS_URL).build();
    final GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(manager).setAudience(Arrays.asList(CLIENT_ID)).build();
    verifier.verify(idToken);
    

    ...它的工作原理。

    我希望 Google 能尽快解决这个问题... :)

    【讨论】:

      【解决方案4】:

      这是从我的回答 here 复制而来,但对于那些不使用 Google Cloud Endpoint 的人来说更相关(匹配这个问题)。问题是由这个引起的:

      • RSA 具有可变长度的签名,具体取决于密钥大小。
      • Google 更新了用于签名的密钥对,现在其中一个密钥对生成的签名长度与另一个不同
      • java.security.Signature.verify(byte[] signature) 如果传递了错误长度的签名,则抛出异常(而不是返回 false,这通常在签名与密钥不匹配时完成)

      最简单的解决方案是包装验证调用(try...catch),如果遇到异常则返回 false

      http://android-developers.blogspot.com/2013/01/verifying-back-end-calls-from-android.html上的示例代码,好像可以改这一行:

      GoogleIdToken token = GoogleIdToken.parse(mJFactory, tokenString);
      

      JsonWebSignature jws = JsonWebSignature.parser(mJFactory).setPayloadClass(Payload.class).parse(tokenString);
      GoogleIdToken token = new GoogleIdToken(jws.getHeader(), (Payload) jws.getPayload(), jws.getSignatureBytes(), jws.getSignedContentBytes()) {
         public boolean verify(GoogleIdTokenVerifier verifier)
        throws GeneralSecurityException, IOException {
             try {
                 return verifier.verify(this);
             } catch (java.security.SignatureException e) {
                 return false;
             }
         }
      };
      

      很遗憾,我没有确切的设置来测试这个,如果这对你有用,请告诉我。

      【讨论】:

        【解决方案5】:

        在我看来,图书馆可能表现不佳。作为离线令牌验证的替代方法,您可以使用 Google 的 OAuth2 端点来验证令牌。 API explorer can be seen here 的一个基本示例。

        您可以使用 curl 命令curl https://www.googleapis.com/oauth2/v2/tokeninfo?id_token=[id_token] 来检查令牌,例如:

        curl https://www.googleapis.com/oauth2/v2/tokeninfo?id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjRlYjczOTg0MzBkNTNjZjZjNGZkMGU5YmM4NzkzZWViZWNkMWY1NWUifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTA3Mzc3MTkxNjgxODAyNjY5ODY2IiwiYXpwIjoiMTE2MjY4ODY3NTIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdF9oYXNoIjoieGJSVGJOdFJYRnJzcUJHTkRtRTR6USIsImF1ZCI6IjExNjI2ODg2NzUyLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiY19oYXNoIjoiU3JFa25WczRUejhQSWJicExnNXF2QSIsImlhdCI6MTQzNDA0MTY5OSwiZXhwIjoxNDM0MDQ1Mjk5fQ.vqQXCTFfbDqpTYnfFrDD7m68oEuGqd8NWa4s9wstOrrcyuVKUsqFXM_2bH-un_4C8UBvqtQEyzU_-53DxgvhCHQ7S0W-wtQ9YMoJcy7iL1wDjcy1p7aFVoeGCoqxWv1vzlWTUDu_FnD9oIBSAawyDexvRwwGxN8O1D8nzyj__1DQ_ivxIMF-j1W89mc7adK4p5B8ioZA_PI-AGawX2Nm8t58yBMIYrTk0XExr9Pf4eHHRGbrQxcd0ERGHbRMYuG6k-MzdnVNHIScgZ3Cixx9v15PbQ5hXExNvleifG_Wk3Thnz0j6Uacr4fbi-mO93-h8c0r3BSvQ270_JqlpL5q5Q
        

        【讨论】:

          【解决方案6】:

          如果您不想(或不能)更改 google 库的来源,您可以扩展 GoogleIdTokenVerifier。 (您必须复制另一个访问一些私有变量的方法 - 幸运的是,它们都可以通过 get-members 访问)。这对我有用:

          GoogleIdTokenVerifier myVerifier = new GoogleIdTokenVerifier(httpTransport, jsonFactory) {
          
              public boolean superVerify(IdToken idToken) {
                        return (getIssuer()== null || idToken.verifyIssuer(getIssuer()))
                            && (getAudience() == null || idToken.verifyAudience(getAudience()))
                            && idToken.verifyTime(getClock().currentTimeMillis(), getAcceptableTimeSkewSeconds());
              }
          
          
              @Override
            public boolean verify(GoogleIdToken googleIdToken) throws GeneralSecurityException, IOException {
                // check the payload
                if (!superVerify(googleIdToken)) {
                    log.info("superVerify returned false");
                  return false;
                }
                // verify signature
                for (PublicKey publicKey : getPublicKeysManager().getPublicKeys()) {
                        try {
                                if (googleIdToken.verifySignature(publicKey)) {
                                        log.info("verifySignature: success!");
                                        return true;
                                }
                        } catch (Exception e) {
                                log.info("error verifying!", e);
                        }
                }
                return false;
              }
          
          };
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-04-23
            • 2015-06-27
            • 2019-06-23
            • 2021-11-02
            • 2021-02-10
            • 2017-07-27
            • 2019-10-20
            相关资源
            最近更新 更多