Google Cloud Endpoints:verifyToken:签名长度不正确
Google Cloud Endpoints: verifyToken: Signature length not correct
今天早上,从我的 Android 应用向我的 Google 云端点发出的每个 API 请求开始出现以下异常:
com.google.api.server.spi.auth.GoogleIdTokenUtils verifyToken:
verifyToken: Signature length not correct: got 256 but was expecting
128
我的 javascript 网络客户端的调用仍然完美。我没有对服务器端代码或客户端代码进行任何更改。
服务最近有没有发生任何可能导致这种情况发生的变化?
更新:第一次出现这种情况似乎是在 11:17:07 UTC
更新:不起作用的事情包括为 android 生成新的客户端 ID 以及更新到 App Engine SDK 1.9.22
原因
- RSA 具有可变长度的签名,具体取决于密钥大小。
- Google 更新了它用于签名的密钥对,现在其中一个密钥对生成与另一个密钥对不同长度的签名
如果传递了错误长度的签名,java.security.Signature.verify(byte[] signature)
将抛出异常(而不是 returning false,通常在签名与密钥不匹配时会这样做)
对我来说,解决方案是包装验证调用 (try...catch
),而 return false。
您也可以自己对 public 密钥进行早期检查,检查签名的长度是否与 public 密钥模数的长度相匹配。
如果您使用库来检查签名,请确保您使用的是最新版本。
查看 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 提供的库需要修复。但它有效,所以这是一个好的开始)
这里有同样的问题,据我所知 public 证书 URL(现在?我想以前不是这种情况,或者顺序改变了)returns 两个键:
https://www.googleapis.com/oauth2/v1/certs
检查那些,第一个有一个 1024 位密钥,第二个有一个 2048 位密钥。我相信来自 android 客户端的传入令牌是由第二个证书使用 2048 位密钥签名的,因此是 "Signature length not correct: got 256 but was expecting 128"。
查看 Google 验证程序源 (GoogleTokenVerifier.java) 它似乎迭代了多个键:
// verify signature
for (PublicKey publicKey : publicKeys.getPublicKeys()) {
if (googleIdToken.verifySignature(publicKey)) {
return true;
}
}
假设键被正确解析(该代码看起来合理但实际上没有检查结果)。
正如 beestra 所指出的,此代码期望返回 false 以防无法验证,而是抛出异常。理想情况下,它应该在失败后继续迭代并使用第二个 public 密钥进行验证,这应该有效。
要解决此问题,似乎有两个选择:
- 分叉 google api 客户端库并修复
- 在(您的)调用代码中复制验证 (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 等价物。
今天早上,从我的 Android 应用向我的 Google 云端点发出的每个 API 请求开始出现以下异常:
com.google.api.server.spi.auth.GoogleIdTokenUtils verifyToken: verifyToken: Signature length not correct: got 256 but was expecting 128
我的 javascript 网络客户端的调用仍然完美。我没有对服务器端代码或客户端代码进行任何更改。
服务最近有没有发生任何可能导致这种情况发生的变化?
更新:第一次出现这种情况似乎是在 11:17:07 UTC
更新:不起作用的事情包括为 android 生成新的客户端 ID 以及更新到 App Engine SDK 1.9.22
原因
- RSA 具有可变长度的签名,具体取决于密钥大小。
- Google 更新了它用于签名的密钥对,现在其中一个密钥对生成与另一个密钥对不同长度的签名 如果传递了错误长度的签名,
java.security.Signature.verify(byte[] signature)
将抛出异常(而不是 returning false,通常在签名与密钥不匹配时会这样做)
对我来说,解决方案是包装验证调用 (try...catch
),而 return false。
您也可以自己对 public 密钥进行早期检查,检查签名的长度是否与 public 密钥模数的长度相匹配。
如果您使用库来检查签名,请确保您使用的是最新版本。
查看 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 提供的库需要修复。但它有效,所以这是一个好的开始)
这里有同样的问题,据我所知 public 证书 URL(现在?我想以前不是这种情况,或者顺序改变了)returns 两个键:
https://www.googleapis.com/oauth2/v1/certs
检查那些,第一个有一个 1024 位密钥,第二个有一个 2048 位密钥。我相信来自 android 客户端的传入令牌是由第二个证书使用 2048 位密钥签名的,因此是 "Signature length not correct: got 256 but was expecting 128"。
查看 Google 验证程序源 (GoogleTokenVerifier.java) 它似乎迭代了多个键:
// verify signature
for (PublicKey publicKey : publicKeys.getPublicKeys()) {
if (googleIdToken.verifySignature(publicKey)) {
return true;
}
}
假设键被正确解析(该代码看起来合理但实际上没有检查结果)。
正如 beestra 所指出的,此代码期望返回 false 以防无法验证,而是抛出异常。理想情况下,它应该在失败后继续迭代并使用第二个 public 密钥进行验证,这应该有效。
要解决此问题,似乎有两个选择:
- 分叉 google api 客户端库并修复
- 在(您的)调用代码中复制验证 (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 等价物。