我有一个 OAuth2 java 客户端(用于服务器到服务器应用程序),它试图创建一个 JWT,然后使用私钥(来自 Google API 控制台)进行签名 - 按照这些页面https://developers.google.com/accounts/文档/OAuth2ServiceAccount。但是,谷歌 OAuth2 不断返回“无效授权”。
这是客户端代码:
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.MessageFormat;
import org.apache.commons.codec.binary.Base64;
public class WebToken
{
private String iss;
private String prn;
private String scope;
private String aud;
private long exp;
private long iat;
private String keystoreLoc;
private String keyAlias;
private String password;
public WebToken(String iss, String prn, String scope, String aud, long exp, long iat, String p12file, String keyAlias, String password)
{
super();
this.iss = iss;
this.prn = prn;
this.scope = scope;
this.aud = aud;
this.exp = exp;
this.iat = iat;
this.keystoreLoc = p12file;
this.keyAlias = keyAlias;
this.password = password;
}
public static String encodeBase64(byte[] rawData)
{
byte[] data = Base64.encodeBase64(rawData);
return new String(data);
}
public String getToken()
throws Exception
{
String header = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}";
//String header = "{\"alg\":\"RS256\"}";
String claimTemplate = "'{'\"iss\": \"{0}\", \"prn\": \"{1}\", \"scope\": \"{2}\", \"aud\": \"{3}\", \"exp\": {4}, \"iat\": {5}'}'";
StringBuffer token = new StringBuffer();
//Encode the JWT Header and add it to our string to sign
token.append(encodeBase64(header.getBytes("UTF-8")));
//Separate with a period
token.append(".");
//Create the JWT Claims Object
String[] claimArray = new String[6];
claimArray[0] = this.iss;
claimArray[1] = this.prn;
claimArray[2] = this.scope;
claimArray[3] = this.aud;
claimArray[4] = "" + this.exp;
claimArray[5] = "" + this.iat;
MessageFormat claims = new MessageFormat(claimTemplate);
String payload = claims.format(claimArray);
print(payload);
//Add the encoded claims object
token.append(encodeBase64(payload.getBytes("UTF-8")));
//Load the private key
PrivateKey privateKey = getPrivateKey(keystoreLoc, password);
byte[] sig = signData(token.toString().getBytes("UTF-8"), privateKey);
String signedPayload = encodeBase64(sig);
//Separate with a period
token.append(".");
//Add the encoded signature
token.append(signedPayload);
return token.toString();
}
private PrivateKey getPrivateKey(String keyFile, String password)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException
{
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(new FileInputStream(keyFile), password.toCharArray());
PrivateKey privateKey = (PrivateKey) keystore.getKey(keyAlias, password.toCharArray());
return privateKey;
}
public byte[] signData(byte[] data, PrivateKey privateKey)
throws InvalidKeyException, SignatureException, NoSuchAlgorithmException
{
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
public static void print(String msg)
{
System.out.println(msg);
}
public static void main(String[] args)
throws Exception
{
String iss = "????????@developer.gserviceaccount.com";
String prn = "xxx@gmail.com";
String scope = "https://www.googleapis.com/auth/urlshortener";
String aud = "https://accounts.google.com/o/oauth2/token";
long iat = System.currentTimeMillis()/1000 - 60;
long exp = iat + 3600;
String keystoreLoc = "D:\\OAuth2\\google_sURL\\pkey1.p12";
String keyAlias = "privatekey";
String pwd = "notasecret";
WebToken jwt = new WebToken(iss, prn, scope, aud, exp, iat, keystoreLoc, keyAlias, pwd);
String token = jwt.getToken();
print(token);
// urn:ietf:params:oauth:grant-type:jwt-bearer
print("curl -vSs -X POST -d \"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + token +
"\" -k https://accounts.google.com/o/oauth2/token");
}
}
然后使用 curl 获取访问令牌: curl -k -vSs -X POST -d "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" https://accounts.google.com/o/ oauth2/令牌
< HTTP/1.1 400 Bad Request
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: Fri, 01 Jan 1990 00:00:00 GMT
< Date: Sun, 06 Jan 2013 01:02:21 GMT
< Content-Type: application/json
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Server: GSE
< Transfer-Encoding: chunked
<
{
"error" : "invalid_grant"
}
知道出了什么问题吗?
==============================
最后我自己发现了问题:
1)在 JWT 中,不要设置“prn”(但您可能需要根据要求)
2)使用“@developer.gserviceaccount.com”作为“iss”
这是更新的代码:
import java.io.FileInputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.text.MessageFormat;
import org.apache.commons.codec.binary.Base64;
public class WebToken
{
private String iss;
private String scope;
private String aud;
private long exp;
private long iat;
private String keystoreLoc;
private String keyAlias;
private String password;
public WebToken(String iss, String scope, String aud, long exp, long iat, String p12file, String keyAlias, String password)
{
super();
this.iss = iss;
this.scope = scope;
this.aud = aud;
this.exp = exp;
this.iat = iat;
this.keystoreLoc = p12file;
this.keyAlias = keyAlias;
this.password = password;
}
/**
* Performs base64-encoding of input bytes.
*
* @param rawData * Array of bytes to be encoded.
* @return * The base64 encoded string representation of rawData.
*/
public static String encodeBase64(byte[] rawData)
{
String data = Base64.encodeBase64URLSafeString(rawData);
return data;
}
public String getToken()
throws Exception
{
String header = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}";
String claimTemplate = "'{'\"iss\": \"{0}\", \"scope\": \"{1}\", \"aud\": \"{2}\", \"exp\": {3}, \"iat\": {4}'}'";
StringBuffer token = new StringBuffer();
//Encode the JWT Header and add it to our string to sign
token.append(encodeBase64(header.getBytes("UTF-8")));
//Separate with a period
token.append(".");
//Create the JWT Claims Object
String[] claimArray = new String[6];
claimArray[0] = this.iss;
claimArray[1] = this.scope;
claimArray[2] = this.aud;
claimArray[3] = "" + this.exp;
claimArray[4] = "" + this.iat;
MessageFormat claims = new MessageFormat(claimTemplate);
String payload = claims.format(claimArray);
print(payload);
//Add the encoded claims object
token.append(encodeBase64(payload.getBytes("UTF-8")));
//Load the private key
PrivateKey privateKey = getPrivateKey(keystoreLoc, password);
byte[] sig = signData(token.toString().getBytes("UTF-8"), privateKey);
String signedPayload = encodeBase64(sig);
//Separate with a period
token.append(".");
//Add the encoded signature
token.append(signedPayload);
return token.toString();
}
private PrivateKey getPrivateKey(String keyFile, String password)
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException
{
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(new FileInputStream(keyFile), password.toCharArray());
PrivateKey privateKey = (PrivateKey) keystore.getKey(keyAlias, password.toCharArray());
return privateKey;
}
public byte[] signData(byte[] data, PrivateKey privateKey)
throws InvalidKeyException, SignatureException, NoSuchAlgorithmException
{
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
public static void print(String msg)
{
System.out.println(msg);
}
public static void main(String[] args)
throws Exception
{
String iss = "<client id email>@developer.gserviceaccount.com";
String scope = "https://www.googleapis.com/auth/urlshortener";
String aud = "https://accounts.google.com/o/oauth2/token";
long iat = (System.currentTimeMillis()/1000)-60;
long exp = iat + 3600;
String keystoreLoc = "<privatekey>.p12";
String keyAlias = "privatekey";
String pwd = "notasecret";
WebToken jwt = new WebToken(iss, scope, aud, exp, iat, keystoreLoc, keyAlias, pwd);
String token = jwt.getToken();
print(token);
print("\\curl\\curl -vSs -X POST -H \"Content-Type: application/x-www-form-urlencoded\"" +
" -d \"grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + token +
"\" -k https://accounts.google.com/o/oauth2/token");
}
}