如何计算服务帐户的 OAuth 签名?



我想使用 HTTP/REST 方法获取 Google API 的服务帐户访问令牌,但 OAuth 向我发送了签名错误。我正在使用Java。

我收到了以下 JSON 字符串:

{
    "error": "invalid_grant",
    "error_description": "Invalid JWT Signature."
}

据我了解,在文档中,我需要编写一个带有标头、声明集和签名的 JWT(JSON Web 令牌(。我设法获得了正确的标头和声明集,但显然,我的签名计算不正确。

我的进口:

import java.security.KeyFactory;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.PrivateKey;

这是标头计算:

String header = "{"alg":"RS256","typ":"JWT"}";
byte[] bytes = header.getBytes();
header = Base64.getEncoder().encodeToString(bytes);

我的索赔集

long time_milis = System.currentTimeMillis()/1000L;
String claim_set = "{n"
     + "  "iss":"something@something.iam.gserviceaccount.com",n"
     + "  "scope":"https://www.googleapis.com/auth/devstorage.read_write",n"
     + "  "aud":"https://www.googleapis.com/oauth2/v4/token",n"
     + "  "exp":" + (time_milis + 300) + ",n"
     + "  "iat":" + time_milis + "n"
     + "}";
bytes = claim_set.getBytes();
claim_set = Base64.getEncoder().encodeToString(bytes);

为我的签名尝试做好准备!(出于明显的安全原因,我屏蔽了私钥(:

String signature = header + "." + claim_set;
String privateKeyString = "-----BEGIN PRIVATE KEY-----nturlututu123n-----END PRIVATE KEY-----n";
privateKeyString = private_key.replaceAll("-----END PRIVATE KEY-----", "").replaceAll("-----BEGIN PRIVATE KEY-----", "").replaceAll("n", "");
Signature privateSignature = Signature.getInstance("SHA256withRSA");
KeyFactory keyFactory=KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(
    new PKCS8EncodedKeySpec(
        Base64.getDecoder().decode(privateKeyString)
    )
);
privateSignature.initSign(privateKey);
privateSignature.update(signature.getBytes("UTF-8"));
bytes = privateSignature.sign();
signature = Base64.getEncoder().encodeToString(bytes);

我在 https://www.googleapis.com/oauth2/v4/token 模式下POST使用以下标头发送我的请求:

"Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded"

而在体内:

"grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=" + header + "." + claim_set + "." + signature

我真的被困住了,不知道如何前进。

终于,我找到了答案!这是一个编码问题。事实上,当我对字符串进行编码时,我调用了Base64.getEncoder()编码器而不是Base64.getUrlEncoder()编码器。现在我们可以更改所有脚本。

这是标头计算:

String header = "{"alg":"RS256","typ":"JWT"}";
byte[] bytes = header.getBytes();
header = Base64.getUrlEncoder().encodeToString(bytes);

我的索赔集

long time_milis = System.currentTimeMillis()/1000L;
String claim_set = "{n"
     + "  "iss":"something@something.iam.gserviceaccount.com",n"
     + "  "scope":"https://www.googleapis.com/auth/devstorage.read_write",n"
     + "  "aud":"https://www.googleapis.com/oauth2/v4/token",n"
     + "  "exp":" + (time_milis + 300) + ",n"
     + "  "iat":" + time_milis + "n"
     + "}";
bytes = claim_set.getBytes();
claim_set = Base64.getUrlEncoder().encodeToString(bytes);

还有签名(出于明显的安全原因,我屏蔽了私钥(:

String signature = header + "." + claim_set;
String privateKeyString = "-----BEGIN PRIVATE KEY-----nturlututu123n-----END PRIVATE KEY-----n";
privateKeyString = private_key.replaceAll("-----END PRIVATE KEY-----", "").replaceAll("-----BEGIN PRIVATE KEY-----", "").replaceAll("n", "");
Signature privateSignature = Signature.getInstance("SHA256withRSA");
KeyFactory keyFactory=KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(
    new PKCS8EncodedKeySpec(
        Base64.getDecoder().decode(privateKeyString)
    )
);
privateSignature.initSign(privateKey);
privateSignature.update(signature.getBytes("UTF-8"));
bytes = privateSignature.sign();
signature = Base64.getUrlEncoder().encodeToString(bytes);

感谢这篇文章,它在调试期间对我有帮助。

最新更新