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]
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;
}
}
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();
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... :)
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.
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
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;
}
};