2015-06-11 14 views
21

sto affrontando OAuth2 JWT eccezione di verifica di token ultima ora (in modo che nessuno possa accedere al mio applicazione):Google OAuth2 JWT eccezione di verifica di token

java.security.SignatureException: Signature Durata non è corretto: ha ottenuto 256 ma mi aspettavo 128. Sto usando google-http-client 1.20.0 e Java 1.7.0. La stessa configurazione ha funzionato finora - qualche idea?

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] 
+0

Lo stesso problema anche con Java 1.8.0_45. – brunnsbe

+0

L'ho trovato sul motore di Google App quando utilizzai token di accesso dall'account gestore account di Google. (Ho appena iniziato a prenderli un'ora fa) –

+1

@ user3686724 Qual è il pubblico che hai impostato per GoogleIdTokenVerifier? Utilizzi ID client o ID token? (Abbiamo riscontrato lo stesso problema negli ultimi 60 minuti) – orrsella

risposta

7

Stesso problema qui, ho aggiunto il codice sorgente di GoogleIdTokenVerifier al mio progetto e cambiato il metodo:

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

solo gestire l'eccezione, il secondo certificato funziona benissimo.

Edit: è possibile creare una sottoclasse come Erik-z suggerito se si vuole rendere più pulito:

Edit 2: Non riesco a farlo funzionare usando il codice seguente, mi atterrò al brutto hack sopra.

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

Non pensate che sia la soluzione finale, ma un work-around temporanea che funziona sicuramente è quello di cambiare il pubblico del verificatore alla tokenId.

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

a

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

Grazie, Ittai! Anche questa soluzione funziona con il mio codice. Buon stopgap per un serio problema di produzione per la mia azienda. – kennbrodhagen

+0

felice di sentire :-) Penso, ma non sono sicuro, che in questo modo si spegne solo la verifica. È possibile che Google abbia rafforzato i controlli dei certificati e questo è ciò che ha causato l'improvvisa impennata. – Ittai

2

La causa principale si trova sul lato di Google, i CERT in JSON è in cattive ordine:

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

È possibile regolare l'ordine di loro , come questo:

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

Dopo, è possibile specificare l'URL personalizzato (o utilizzando il mio :) della JSON con il metodo 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); 

... e funziona.

spero, Google risolvere il problema presto ... :)

+0

Oh, è riparato. :) –

0

Questo è copiato da mia risposta here, ma più rilevanti per coloro che non utilizzano Google Cloud Endpoint (corrispondenti a questa domanda). Il problema è causato da questo:

  • RSA ha segnature di lunghezza variabile, a seconda della dimensione della chiave.
  • Google ha aggiornato le coppie di chiavi che utilizza per la firma, e ora una delle coppie di chiavi genera una firma di lunghezza diversa dall'altra
  • java.security.Signature.verify(byte[] signature) genera un'eccezione se viene passata una firma della lunghezza sbagliata (invece di ritornare falso che viene normalmente fatto quando una firma non corrisponde alla chiave)

la soluzione più semplice è quello di avvolgere la chiamata verifica (try...catch), e restituire false se si ottiene un'eccezione

Guardando il codice di esempio su http://android-developers.blogspot.com/2013/01/verifying-back-end-calls-from-android.html , sembra che tu possa ch ange questa linea:

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

a

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

Io purtroppo non ho una messa a punto esatto per testare questo, fatemi sapere se questo funziona per voi.

0

Mi sembra che le librerie si stiano comportando male. In alternativa alla verifica dei token offline, puoi utilizzare gli endpoint OAuth2 di Google per verificare i token. Un esempio di base dell'API explorer can be seen here.

È possibile controllare un token con un comando ricciolo curl https://www.googleapis.com/oauth2/v2/tokeninfo?id_token=[id_token], ad esempio:

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

Se non si vuole (o non può) cambiare la sorgente della libreria di Google, si può solo estendere GoogleIdTokenVerifier. (devi duplicare un altro metodo che accede ad alcune variabili private - fortunatamente tutti sono accessibili tramite i membri get). Questo funziona per me:

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

};