Google OAuth2 JWT token verification exception

je fais face à L'exception de vérification de JWT JWT dernière heure (pour que personne ne puisse accéder à mon application):

de java.sécurité.SignatureException: longueur de la Signature non correcte: 256 mais 128 prévu. J'utilise google-http-client 1.20.0 et Java 1.7.0 . La même configuration a fonctionné jusqu'à présent - des idées?

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]
21
demandé sur feeling abused and harassed 2015-06-11 15:10:33

6 réponses

même problème ici, j'ai ajouté le code source de GoogleIdTokenVerifier à mon projet et changé la méthode:

 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;
  }

juste gérer l'exception, le deuxième certificat fonctionne très bien.

modifier: vous pouvez sous-classe comme Erik-z suggéré si vous voulez le rendre plus propre:

Edit 2: je ne peux pas le faire fonctionner à l'aide du code ci-dessous, je vais passer pour le vilain hack ci-dessus.

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;
    }
}
7
répondu qtxo 2017-05-23 12:07:20

ne pensez pas que ce soit la solution finale, mais un travail temporaire-autour qui fonctionne certainement est de changer le public du vérificateur au 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();
5
répondu Ittai 2015-06-11 13:53:17

la cause fondamentale est du côté de Google, les certs dans le JSON est dans le mauvais ordre:

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

vous pouvez ajuster leur ordre, comme ceci:

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

après, vous pouvez spécifier votre URL personnalisé (ou en utilisant mine :) du JSON avec le GooglePublicKeysManager.setPublicCertsEncodedUrl(...) méthode:

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

...et il fonctionne.

j'espère, le Google corriger la question bientôt... :)

2
répondu Gábor AUTH 2015-06-11 15:47:35

ceci est copié de ma réponse ici , mais plus pertinent pour ceux qui n'utilisent pas Google Cloud Endpoint (correspondant à cette question). Le problème est causé par ceci:

  • RSA a des signatures de longueur variable, en fonction de la taille de la clé.
  • Google a mis à jour les paires de clés qu'il utilise pour la signature, et maintenant l'une des paires de clés génère une signature de longueur différente de l'autre
  • java.security.Signature.verify(byte[] signature) lance une exception si une signature de la mauvaise longueur est passée (au lieu de retourner false qui est normalement fait quand une signature ne correspond pas à la clé)

la solution la plus simple est d'envelopper l'appel vérifier ( try...catch ), et de retourner false si vous obtenez une exception

en regardant le code exemple sur http://android-developers.blogspot.com/2013/01/verifying-back-end-calls-from-android.html , il vous ressemble peut modifier cette ligne:

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;
       }
   }
};

Je n'ai malheureusement pas de configuration précise pour tester ceci, faites-moi savoir si cela fonctionne pour vous.

0
répondu beetstra 2017-05-23 12:07:19

il me semble que les bibliothèques se comportent mal. Comme alternative à la vérification des tokens hors ligne, vous pouvez utiliser les endpoints OAuth2 de Google pour vérifier les tokens. Un exemple de base de L'Explorateur API peut être vu ici .

vous pouvez vérifier un token avec une commande curl curl https://www.googleapis.com/oauth2/v2/tokeninfo?id_token=[id_token] , par exemple:

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
répondu class 2015-06-11 17:01:36

si vous ne voulez pas (ou ne pouvez pas) changer la source de la bibliothèque google, vous pouvez simplement étendre le GoogleIdTokenVerifier. (vous devez dupliquer une autre méthode qui accède à certaines variables privées - heureusement toutes sont accessibles via get-members). Cela me convient:

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;
    }

};
0
répondu Gerolf Scherr 2015-06-11 17:05:43