谷歌OAuth2 JWT令牌验证异常

21

我最近遇到了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版本。 - brunnsbe
1
@user3686724,您为GoogleIdTokenVerifier设置了什么受众?您使用客户端ID还是令牌ID?(我们在过去60分钟内一直遇到相同的问题) - orrsella
1
我也遇到了同样的问题,看起来像是Google API的问题。你知道在哪里投诉并找到相关信息吗? - peveuve
1
感谢大家提供的报告和解决方法。我们已确认这是一个Google问题,并由我们的后端团队优先处理。 - John Hjelmstad
显然,现在问题已解决,因为谷歌更新了所有密钥以使用相同的密钥长度。 - beetstra
显示剩余3条评论
6个回答

7

我也遇到了同样的问题,我将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;
    }
}

5

不要认为这是最终解决方案,但一个有效的临时解决方法是将验证器的受众更改为tokenId。

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

to

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

1
谢谢,Ittai!这个解决方法对我的代码也有效。对于我们公司的一个严重生产问题来说,这是一个不错的权宜之计。 - kennbrodhagen
很高兴听到这个 :-) 我认为,但不确定,这只是关闭验证。谷歌可能已经加强了其证书检查,这可能导致了突然的激增。 - Ittai

2
根本原因在于 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);

"...它起作用了。希望谷歌能尽快解决这个问题...:)"

0

这是从我的答案here复制的,但更适用于不使用Google Cloud Endpoint(与此问题匹配)的人。 问题是由此引起的:

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

最简单的解决方案是包装verify调用(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;
       }
   }
};

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


0

在我看来,这些库可能表现不佳。除了离线令牌验证之外,您还可以使用Google的OAuth2端点来验证令牌。您可以从API资源管理器中查看基本示例 点击此处

您可以使用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

0

如果你不想(或不能)改变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;
    }

};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接