Google Cloud Endpoints: verifyToken: Signature length not correct

前端 未结 2 1806
庸人自扰
庸人自扰 2021-01-31 18:26

This morning the following exception has started occuring on every API request to my Google Cloud Endpoint from my Android app:

com.google.api.server.spi.

2条回答
  •  温柔的废话
    2021-01-31 18:52

    Same problem here, as far as I can tell the public cert URL (now? I guess this wasn't the case before or the order changed) returns two keys:

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

    inspecting those, the first has a 1024 bit key and the second a 2048 bit key. I believe my incoming tokens from android clients were signed by the second cert with the 2048 bit key, hence the "Signature length not correct: got 256 but was expecting 128".

    Looking at the Google verifier source (GoogleTokenVerifier.java) it does appear to iterate multiple keys:

    // verify signature
    for (PublicKey publicKey : publicKeys.getPublicKeys()) {
      if (googleIdToken.verifySignature(publicKey)) {
        return true;
      }
    }
    

    assuming the keys got parsed correctly (that code looks reasonable but haven't actually inspected the results).

    As beestra pointed out the, this code expects false to be returned in case it could not be verified but instead it's throwing an exception. Ideally it should keep iterating after a failure and use the second public key to verify, which should work.

    To fix this there appears to be two options:

    1. Fork the google api client library and fix
    2. Duplicate the verification (GoogleIdTokenVerifier.verify(GoogleIdToken)) in (your) calling code

    I don't know how realistic 2. is, some super functionality is used and a lot of internal state is private, would have to duplicate all of it. Busy investigating...

    UPDATE: Ok, looks to be fixed in my tests using production data, though haven't deployed it to production just yet. Here's the Scala

      val jsonFactory = new JacksonFactory()
      val transport = new NetHttpTransport()
      val googleIdTokenVerifier = new GoogleIdTokenVerifier(transport, jsonFactory)
    
      class DuplicateVerifier(builder: GoogleIdTokenVerifier.Builder) extends IdTokenVerifier(builder)
      val topIdTokenVerifier = new DuplicateVerifier(new GoogleIdTokenVerifier.Builder(transport, jsonFactory))
      val publicKeysManager = new GooglePublicKeysManager(transport, jsonFactory)
      def duplicateGoogleVerify(token: GoogleIdToken): Boolean = {
        // check the payload
        if (!topIdTokenVerifier.verify(token)) {
          false
        } else {
          // verify signature
          import scala.collection.JavaConverters._
          publicKeysManager.getPublicKeys.asScala.map { k =>
            Try(token.verifySignature(k))
          }.foldLeft(false)((c, x) => c || x.getOrElse(false))
        }
      }
    

    Just to be clear if it isn't obvious, using this method instead of Google's:

    // if (googleIdTokenVerifier.verify(token)) {
    if (duplicateGoogleVerify(token)) {
    

    I'll try to write the Java equivalent later if anyone needs it.

提交回复
热议问题