4

我有一个 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");
      }
    }
4

0 回答 0