title: Google OAuth2 returns “invalid_grant” - signing JWT issue
body: <p>I've a OAuth2 java client (for Server to Server Applications) that is trying to create a JWT and then sign with a private key (from Google API console) - follow these pages <a href="https://developers.google.com/accounts/docs/OAuth2ServiceAccount" rel="nofollow">https://developers.google.com/accounts/docs/OAuth2ServiceAccount</a>. However, google OAuth2 keeps returning "invalid grant".</p>
  
  <p>Here is the client code:</p>
  
  <pre><code>    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&amp;assertion=" + token + 
                "\" -k https://accounts.google.com/o/oauth2/token");
        }
  }
  </code></pre>
  
  <p>Then use curl to get access token: curl -k -vSs -X POST -d "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&amp;assertion=" <a href="https://accounts.google.com/o/oauth2/token" rel="nofollow">https://accounts.google.com/o/oauth2/token</a></p>
  
  <pre><code>    &lt; HTTP/1.1 400 Bad Request
      &lt; Cache-Control: no-cache, no-store, max-age=0, must-revalidate
      &lt; Pragma: no-cache
      &lt; Expires: Fri, 01 Jan 1990 00:00:00 GMT
      &lt; Date: Sun, 06 Jan 2013 01:02:21 GMT
      &lt; Content-Type: application/json
      &lt; X-Content-Type-Options: nosniff
      &lt; X-Frame-Options: SAMEORIGIN
      &lt; X-XSS-Protection: 1; mode=block
      &lt; Server: GSE
      &lt; Transfer-Encoding: chunked
      &lt;
      {
        "error" : "invalid_grant"
      }
  </code></pre>
  
  <p>Any idea on what's wrong?</p>
  
  <p>============================</p>
  
  <p>Finally I found the problems myself:</p>
  
  <p>1) in the JWT, don't set "prn" (but you may need that depending on the requirement)</p>
  
  <p>2) use "@developer.gserviceaccount.com" for "iss"</p>
  
  <p>Here is the updated code:</p>
  
  <pre><code>    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 = "&lt;client id email&gt;@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 = "&lt;privatekey&gt;.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&amp;assertion=" + token +    
                "\" -k https://accounts.google.com/o/oauth2/token");
        }
      }
  </code></pre>