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

标签 java oauth jwt signature

我想要get a service account access token对于 Google API,使用 HTTP/REST 方法,但 OAuth 向我发送签名错误。我正在使用 Java。

我收到以下 JSON 字符串:

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

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

我的导入:

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;

这是 header 计算:

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-----\nturlututu123\n-----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/tokenPOST 模式下,具有以下 header :

"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() 编码器。现在我们可以更改所有脚本。

这是 header 计算:

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-----\nturlututu123\n-----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);

感谢this post这在调试过程中对我有帮助。

关于java - 如何计算服务帐户的 OAuth 签名?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55511916/

相关文章:

ios - WooCommerce iOS OAuth 无效签名

asp.net - JwtSecurityToken 理解与异常

javascript - 如何通过 PHP 使用 Google 电子钱包?

java - 如何通过控制层次结构(来自 C#)自动操作 Java GUI 程序?

java - 应用程序名称在设备上看起来很奇怪

java - Netbeans 7.2 中的代码导航

angular - isTokenExpired 方法返回 true 和 false

java - 如何调试 HTTP 错误 503 - Jetty

python - Tweepy 多重身份验证处理程序

spring - 为什么我的 Spring oauth 服务器总是返回相同的访问 token ?