Подтвердить что ты не робот

Исключение проверки токена Google OAuth2 JWT

Я столкнулся с проверкой проверки подлинности токена OAuth2 JWT за последний час (поэтому никто не может получить доступ к моему приложению):

java.security.SignatureException: длина подписи неверна: получил 256, но ожидал 128. Я использую google-http-client 1.20.0 и Java 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]
4b9b3361

Ответ 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: я не могу заставить его работать, используя приведенный ниже код, я буду придерживаться уродливого взлома выше.

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();

Ответ 3

Коренная причина находится на стороне Google, сертификаты в JSON находятся в плохом порядке:

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

Вы можете настроить их порядок, например:

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

После этого вы можете указать свой собственный URL (или используя мой:) JSON с методом GooglePublicKeysManager.setPublicCertsEncodedUrl(...):

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

Это скопировано из моего ответа здесь, но более актуально для тех, кто не использует конечную точку Google Cloud (соответствующий этому вопросу). Проблема вызвана этим:

  • 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-браузера .

Вы можете проверить токен с помощью команды 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;
    }

};