2

I am generating token using JJWT library like this -

    final String issuer = "my-app-auth-server@my-app-797ab.iam.gserviceaccount.com";
    final String sub = "my-app-auth-server@my-app-797ab.iam.gserviceaccount.com";
    final String aud = "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit";
    final String secret = "my-secret-key"   //only demo key , not real secret key that i am using



    final long iat = System.currentTimeMillis() / 1000L; // issued at claim
    final long exp = iat + 60L; // expires claim. In this case the token expires in 60 seconds

    final String jwtString = Jwts.builder()
                .claim("alg","HS256")
                .claim("iss", issuer)
                .claim("aud",aud)
                .claim("iat", iat)
                .claim("exp", exp)
                .claim("uid",number)
                .setSubject(sub)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();

The secret key("my-secret-key") i am using is generated by Firebase , as stated here

But i am getting this error when i am signing in Firebase with the token generated as above -

com.google.firebase.auth.FirebaseAuthInvalidCredentialsException: The custom token format is incorrect. Please check the documentation.
                                                                         at com.google.android.gms.internal.zzafd.zzes(Unknown Source)
                                                                         at com.google.android.gms.internal.zzafa$zzg.zza(Unknown Source)
                                                                         at com.google.android.gms.internal.zzafl.zzet(Unknown Source)
                                                                         at com.google.android.gms.internal.zzafl$zza.onFailure(Unknown Source)
                                                                         at com.google.android.gms.internal.zzafg$zza.onTransact(Unknown Source)
                                                                         at android.os.Binder.execTransact(Binder.java:367)
                                                                         at dalvik.system.NativeStart.run(Native Method)

This is how it looks decoded -

enter image description here

Please help , Thanks in advance.

Devansh Kumar
  • 1,552
  • 1
  • 15
  • 27

3 Answers3

1

my-secret-key is not a valid Base64 string. Please read the JavaDoc for the signWith(SignatureAlgorithm, String) method:

/**
 * Signs the constructed JWT using the specified algorithm with the
 * specified key, producing a JWS.
 *
 * <p>This is a convenience method: the string argument is first
 * BASE64-decoded to a byte array and this resulting byte array is 
 * used to invoke {@link #signWith(SignatureAlgorithm, byte[])}.</p>
 *
 * @param alg the JWS algorithm to use to digitally sign the JWT, 
 *            thereby producing a JWS.
 * @param base64EncodedSecretKey the BASE64-encoded algorithm-specific 
 *        signing key to use to digitally sign the JWT.
 * @return the builder for method chaining.
 */
JwtBuilder signWith(SignatureAlgorithm alg, String base64EncodedSecretKey);

Cryptographic signatures are always computed with byte array keys - never strings. You can get the UTF-8 bytes of a String, for example "my-secret-key".getBytes("UTF-8");, but that only masks what could be a very problematic cryptographic weakness.

Digital signature keys (again, byte arrays), should ideally never be based on simple strings like 'my secret' or 'my password'. Or, at the very least, if a simple password should be used as a signing key, it is almost always better to send it through a key-derivation algorithm (like PBKDF2) and then use that resulting output as the signature key. This ensures sufficient cryptographic entropy (randomness) that short, human-readable strings don't (and which are therefore risky).

Signing keys should ideally always be:

  1. generated by a secure-random number generator or at the least created via a cryptographically secure key-derivation function and
  2. and this is important - of sufficient length for the hashing algorithm to be used.

Number 2 is why JJWT provides the MacProvider.generateKey method - to ensure you always have keys of sufficient strength for the algorithm chosen. You can then easily base64 the result:

SecretKey key = MacProvider.generateKey(SignatureAlgorithm.HS256);
String base64Encoded = TextCodec.BASE64.encode(key.getEncoded());

This is why JJWT expects Base64 by default - because if you do these best practices, you'll always end up with a byte array key (e.g. key.getEncoded()). And if you have a byte array key, the most common way to turn that into a string (e.g. for configuration) is to Base64-encode that byte array.

Finally, note that TextCodec.BASE64.decode(myKey) does NOT produce the same byte array (key) as myKey.getBytes('UTF-8'). The latter is usually incorrect in cryptographic contexts.

That means that my-secret-token-to-change-in-production.getBytes("UTF-8") might represent a weakened signing key, and as a result, shouldn't be used. I recommend dumping that current key and generating a new one with strong cryptographic guarantees as shown above (e.g. using JJWT) and ensuring your Node library base64-decodes your string correctly.

So, once you have a secure random generated byte array and Base64 that, then check the "secret base64 encoded" checkbox in the above tool, and it should work for you.

Les Hazlewood
  • 18,480
  • 13
  • 68
  • 76
  • Sorry for the confusion sir , actually i am not using "my-secret-key" as key for the encryption , i didn't wrote the actual key , that i am using , here because of security issues. The key i am using is actually created by Firebase , as stated [here](https://firebase.google.com/docs/server/setup#add_firebase_to_your_app) , I should have mentioned this , my bad. – Devansh Kumar Oct 08 '16 at 08:53
1

Since you are minting tokens in Java, you can make use of the official Firebase Java SDK which has token minting built-in. Follow the instructions and code samples here to start creating custom tokens.

I think your main problem is that you're creating a token with HS256 encryption but Firebase requires RS256, as noted here. But if you use the official library, it will handle all of this for you.

jwngr
  • 4,284
  • 1
  • 24
  • 27
  • Thank you for response , the official library given for creating tokens is Firebase server-side SDK , meaning that it is meant to be used on servers , but what i am doing is that creating tokens right inside android app ,meaning locally , and then sign in with it . (i know i am not supposed to create tokens locally but right now it suits my requirements). And on the topic of encryption algo , i think you are right about HS256 , although i have not tested it yet . – Devansh Kumar Oct 11 '16 at 17:53
0

I had a similar problem with a PHP JWT generator library and the solution was playing with the iat payload parameter. My server was some seconds in the future compared to google's one (the error message never told me that...).

I just defined the iat payload parameter 5 minutes in the past and boom, it worked.

Hope this will help.

Masacc
  • 106
  • 8