Intereting Posts

Конечные точки Google Cloud: verifyToken: длина подписи неверна

Сегодня утром следующее следующее исключение началось в каждом запросе API моей конечной точки Google из моего приложения для Android:

Com.google.api.server.spi.auth.GoogleIdTokenUtils verifyToken: verifyToken: длина подписи неверна: получил 256, но ожидал 128

Вызов по-прежнему отлично работает с моими веб-клиентами javascript. Я ничего не изменил для кода на стороне сервера или кода клиента.

Что-то изменилось с услугой в последнее время, что могло бы произойти это?

UPDATE: Первое появление этого, похоже, было в 11:17:07 UTC

ОБНОВЛЕНИЕ: Вещи, которые не работают, включают создание нового идентификатора клиента для Android и его обновление до App Engine SDK 1.9.22

Solutions Collecting From Web of "Конечные точки Google Cloud: verifyToken: длина подписи неверна"

Причины

  • RSA имеет подписи переменной длины, в зависимости от размера ключа.
  • Google обновил пары ключей, которые он использует для подписания, и теперь одна из пар ключей генерирует другую длину подписи от другой
  • java.security.Signature.verify(byte[] signature) генерирует исключение, если передается сигнатура неправильной длины (вместо того, чтобы возвращать false, что обычно делается, когда подпись не соответствует ключу)

Для меня решение заключалось в том, чтобы завернуть вызов проверки ( try...catch ) и вместо этого вернуть false. Вы также можете выполнить раннюю проверку открытого ключа самостоятельно, проверяя, соответствует ли длина подписи длине модуля открытого ключа.

Если вы используете библиотеку для проверки подписи, убедитесь, что используете последнюю версию.

Посмотрев пример кода на http://android-developers.blogspot.nl/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; } } }; 

У меня, к сожалению, нет точной установки, чтобы проверить это.

Для тех, кто использует Google Cloud Endpoint, как и в случае с вопросом, я думаю, что было очень мало, что вы могли бы сделать, кроме как подождать, пока Google не исправит это. К счастью, это исправлено. (Технически вы могли бы утверждать, что изменение ключей, как это делается сейчас, является обходным путем, и библиотека Google требует исправления. Но она работает, так что это хороший старт)

Такая же проблема здесь, насколько я могу рассказать публичный URL-адрес сертификата (теперь? Я предполагаю, что это было не так раньше или изменился порядок) возвращает два ключа:

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

Проверяя их, первый имеет 1024-битный ключ, а второй – ключ 2048 бит. Я считаю, что мои входящие токены от клиентов android были подписаны вторым сертификатом с ключом 2048 бит, поэтому «Длина подписи не верна: получил 256, но ожидал 128».

Если посмотреть на источник верификатора Google (GoogleTokenVerifier.java), он, похоже, повторит несколько ключей:

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

Предполагая, что ключи правильно проанализированы (этот код выглядит разумным, но на самом деле не проверял результаты).

Как указывала Бестера, этот код ожидает, что false будет возвращен, если он не сможет быть проверен, но вместо этого он выбрасывает исключение. В идеале он должен продолжать выполнять повторение после сбоя и использовать второй открытый ключ для проверки, который должен работать.

Чтобы исправить это, есть два варианта:

  1. Прокрутите клиентскую библиотеку google api и исправьте
  2. Дублируйте проверку (GoogleIdTokenVerifier.verify (GoogleIdToken)) в (ваш) код вызова

Я не знаю, насколько реалистичен 2., используется какая-то суперфункция, и много внутреннего состояния является частным, придется дублировать все это. Занятое расследование …

UPDATE : Хорошо, похоже, исправлено в моих тестах с использованием производственных данных, хотя пока они еще не развернули его для производства. Вот 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)) } } 

Чтобы быть ясным, если это не очевидно, используя этот метод вместо Google:

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

Я попытаюсь написать эквивалент Java позже, если кому-то это понадобится.