Pages

Wednesday, December 21, 2011

Google Authenticator: Using It With Your Own Java Authentication Server

The Google Authenticator application for mobile devices is a very handy application that implements the TOTP algorithm (specified in RFC 6238). Using Google Authenticator you can generate time passwords that can be used to authorize users in an authentication server that shares the secret key of the requesting users.

Google Authenticator is mainly used to access Google services using two-factor authentication. However, you can take advantage of Google Authenticator to generate time based password to be authenticated by a server of yours. The implementation of such a server is pretty simple in Java and you can get some inspiration getting the source code of the Google Authenticator PAM module. In this blog post, we will go through a simple implementation of the TOTP algorithm in a Java class.

Generating the Secret Key.

To generate the secret key we will use a random number generator to fill up a byte array of the required size. In this case, we want:
  • A 16 characters Base32 encoded secret key: since Base32 encoding of x bytes generate 8x/5 characters, we will use 10 bytes for the secret key.
  • Some scratch codes (using Google's jargon).

// Allocating the buffer
byte[] buffer =
  new byte[secretSize + numOfScratchCodes * scratchCodeSie];

// Filling the buffer with random numbers.
// Notice: you want to reuse the same random generator
// while generating larger random number sequences.
new Random().nextBytes(buffer);

Now we want to extract the bytes corresponding to the secret key and encode it using the Base32 encoding. I'm using the Apache Common Codec library to get a codec implementation:

// Getting the key and converting it to Base32
Base32 codec = new Base32();
byte[] secretKey = Arrays.copyOf(buffer, secretSize);
byte[] bEncodedKey = codec.encode(secretKey);
String encodedKey = new String(bEncodedKey);

Loading the Key Into Google Authenticator

You can manually load the key into Google Authenticator, or generate a QR barcode to have the application loading it from it. If you want to generate a QR barcode using Google services, you can generate the corresponding URL with a code such as this:

public static String getQRBarcodeURL(
  String user,
  String host,
  String secret) {
  String format = "https://www.google.com/chart?chs=200x200&chld=M%%7C0&cht=qr&chl=otpauth://totp/%s@%s%%3Fsecret%%3D%s";
  return String.format(format, user, host, secret);
}

Verifying a Code

Now that we've generated the key and our users can load them into their Google Authenticator application, we need the code required to verify the generated verification codes. Here's a Java implementation of the algorithm specified in the RFC 6238:


private static boolean check_code(
  String secret,
  long code,
  long t)
    throws NoSuchAlgorithmException,
      InvalidKeyException {
  Base32 codec = new Base32();
  byte[] decodedKey = codec.decode(secret);

  // Window is used to check codes generated in the near past.
  // You can use this value to tune how far you're willing to go. 
  int window = 3;
  for (int i = -window; i <= window; ++i) {
    long hash = verify_code(decodedKey, t + i);

    if (hash == code) {
      return true;
    }
  }

  // The validation code is invalid.
  return false;
}

private static int verify_code(
  byte[] key,
  long t)
  throws NoSuchAlgorithmException,
    InvalidKeyException {
  byte[] data = new byte[8];
  long value = t;
  for (int i = 8; i-- > 0; value >>>= 8) {
    data[i] = (byte) value;
  }

  SecretKeySpec signKey = new SecretKeySpec(key, "HmacSHA1");
  Mac mac = Mac.getInstance("HmacSHA1");
  mac.init(signKey);
  byte[] hash = mac.doFinal(data);

  int offset = hash[20 - 1] & 0xF;
  
  // We're using a long because Java hasn't got unsigned int.
  long truncatedHash = 0;
  for (int i = 0; i < 4; ++i) {
    truncatedHash <<= 8;
    // We are dealing with signed bytes:
    // we just keep the first byte.
    truncatedHash |= (hash[offset + i] & 0xFF);
  }

  truncatedHash &= 0x7FFFFFFF;
  truncatedHash %= 1000000;

  return (int) truncatedHash;
}

The t parameter of the check_code method and verify_code methods "is an integer and represents the number of time steps between the initial counter time t0 and the current Unix time." (RFC 6238, p. 3) The default size of a time step is 30 seconds, and it's the value that Google Authenticator uses too. Therefore, t can be calculated in Java as

t = new Date().getTime() / TimeUnit.SECONDS.toMillis(30);

Download the Library

A ready to use library can be downloaded from GitHub, where Mr. Warren Strange kindly started a repository with the code from this post and packaged it in a Maven project. The library contains a complete implementation of the server-side code, better documentation and some example code in the test cases.

Conclusion

You can now use the Google Authenticator applications and use it to generate time based passwords for your users, authenticated against your own authentication server.

As you can see, the required code is pretty simple and all of the required cryptographic functions are provided by the runtime itself. The only nuisance is dealing with signed types in Java.

Enjoy!

55 comments:

  1. Hi Enrico

    Thanks so much for this!

    Using your sample, I put together a small project on github that may useful to others:

    https://github.com/wstrange/GoogleAuth

    ReplyDelete
  2. Hi.

    Thank you very much for doing that, I'm really glad it helped you.

    Bye

    ReplyDelete
  3. This is very helpful! Thank you.

    How would I extract/see what the scratch codes are?

    ReplyDelete
  4. Thanks to both of you for this - it's proven very useful for me to get this going.

    By the way, creating a hyperlink using the generated Google Charts link may cause some HTTP 400 Malformed errors and the image may not load up. They will work pasting in the browser but not as an img src link (probably some redirects going on)

    To fix this, simply change www.google.com in the url generation code to chart.googleapis.com

    ReplyDelete
  5. Hi Enrico,

    Thanks for posting your code - I wondered what sort of license you publish this code under? Specifically, would it be okay to make use of it within a non-opensource project?

    Thanks again, and to Mr Strange for putting it together into a Maven project.

    ReplyDelete
    Replies
    1. Hi Nick,

      You're welcome.

      I made no reference about a license for the code published in this blog post (and in practically all the others) mainly because my goal was the code being illustrative.

      On my part, please feel free to use the code in your project and I'm really, really glad you asked. If you want, you can put a mention in the source file. Nobody will ever se it, but nonetheless it's a memento. :)

      Cheers,
      -- Enrico

      Delete
  6. Hi Grey / wstrange

    Many thanks for the great effort.
    I tried your code and it works fine.

    Can you please clarify this ?
    In my mobile 'Google Authenticator' apps I registered the the generated QR code.
    And when I tried to test the authTest() method by invoking it continuously it returned true even after that code expired and I see a new code on my mobile. I tried continuously until 5 different codes were generated. It works fine with the initial code.

    I am confused. I thought it should work only till that code is valid and displayed in my mobile. ?

    Thanks
    Jay

    ReplyDelete
  7. Hi Jay.

    As you can see in the code shown in the post, the verifyCode method is invoked in a loop whose purpose is verifying a code in a time window of size 6: 2 * window and window equals 3. That's why the code is valid even though the authenticator application is shown the next one.

    Why this behaviour? Because a code generated by a client wouldn't pass the server validation unless the clock of the server and the client are synchronised within a very small error (much smaller than one minute). The code above validates a code if it's one of the codes which are generated in a 6 minutes "tolerance window" centred in the current time: [-3, 3]. You can adjust the size of the tolerance window by setting the window parameter to another value.

    Hope this helps,
    -- Enrico

    ReplyDelete
  8. I followed this example but the verify-code I enter is never the correct one according to the output. :/

    ReplyDelete
    Replies
    1. Which window size are you testing with? Keep into account that the code changes over time and if the two clocks are skewed, then the code won't be the same when calculated at the same instant. That's why the code is usually checked over a time window.

      Delete
    2. the window int? It's set to 3 as it is in the example.

      Delete
    3. To keep thing simple and the jargon as well: in that loop you increase the coordinate of the "time period" on which you calculate the code. If we use a period of 30 seconds, you're checking 1 minute and a half in the past and 1 minute and a half in the future. Check your clock skew and see whether it's bigger than that. If it is, either you synchronise them or you raise the window size. The former is the better option between the two.

      Delete
    4. Yeah I changed it to 1000 but it still didn't work (plus I think it took like 10 sec or so to enter the code). I made 2 pastebins with my code, http://pastebin.com/s6xxCg3R & http://pastebin.com/aZdtVamd
      The first one verifies the code and the second one outputs a QR barcode link.

      Really appreciate your help, I've tried to get this working for a week now :/

      Delete
    5. You're welcome. Which value are you passing to the t parameter or the check_code method? I suspect you're not passing the correct time_period. Google uses a 30-seconds period, so that you must take the UNIX time in milliseconds, divide it by 30.000 (the size of a 30-second window in milliseconds) and use that value as t.

      Delete
    6. Right now it's on 100. What should I set it to if I want uhm let's say a 1 minute time frame?

      Delete
    7. 1 minute means window size == 2. I believe, as I said, your problem may be t. Try to set t to new Date().getTime() / 30000 when you invoke check_code.

      Delete
    8. wow that worked! Thank you so much! You literally saved my day :D

      Delete
    9. I'm really glad that it helped. By the way, this made me realise that the blog post assumes that people know what the aforementioned RFC says. I'll correct it and add further information so that nobody else gets stuck on this issue.

      Delete
  9. Really nice tutorial. Thanks alot.

    ReplyDelete
  10. How are the scratch codes work/are supposed to work? I can't find a lot of good information on how they work.
    I see that they are supposed to be used in some manner to gain access if the MFA is lost or broken. However, I can't really find how

    I see that they are generated in a some complicated manner. What I don't see is why that is done, and then what one does with them after they are generated. The credentials repository is passed the scratch codes, ostensibly to store them.

    Is the idea that when a MFA is lost or broken, the user is provided an alternate link, where they verify a scratch code? Once the scratch code is verified, do we reset the MFA so they can set up a new one?

    ReplyDelete
  11. Hi Skip,

    Scratch codes are emergency passwords you can use to log into your account, for example when you can't use the Google Authenticator application for some reason. If and how to use them is up to your backend. You could ignore them or allow each of them once (as Google does), etc. Google, in fact, suggests you print them and take them with you in case Google Authenticator is inaccessible (you lost the phone) even temporarily (the battery is dead).

    The credential repository is passed the scratch codes because they indeed are passwords, but as I said the policy is yours.

    Hope this helps,
    -- Enrico

    ReplyDelete
  12. What will happen in cases where the code is generated on a devices that are in different timezones e.g. one in USA and one in Asia. Will this implementation still work ??

    ReplyDelete
  13. Hi Peter,

    Yes, it works because it does not depend on any time zone. Since the time frame is calculated as the elapsed time from the the UNIX epoch, the time zone delta is zeroed by the difference. That is: if you describe the UNIX epoch in any time zone, the number of milliseconds elapsed form that moment up to now will be the same no matter which time zone you are using.

    Cheers,
    -- Enrico

    ReplyDelete
  14. HI Enrico,

    Thanks for sharing this. I am wondering if there is anyway we can turn our 6 digit one time pin to 8 digit?

    Regards
    Liang

    ReplyDelete
  15. plz send Two server Authentication source code...

    ReplyDelete
  16. Hi,

    I am in a bizzare situation where Google Authenticator is generating a number with leading zero and this implementation is not handling it as in Java numbers with leading zeros are not possible. Just wondering if there is any way of solving this issue.

    Thank you.

    ReplyDelete
  17. Hi Peter,

    Did you just try removing the leading 0?

    ReplyDelete
  18. Well from the UI the value is coming as String and i am saving it into integer/long. The reason why i am doing because of the below lines in checkCode method.

    for (int i = -((window - 1) / 2); i <= window / 2; ++i) {
    // Calculating the verification code for the current time interval.
    long hash = calculateCode(decodedKey, timeWindow + i);

    // Checking if the provided code is equal to the calculated one.
    if (hash == code) {
    // The verification code is valid.
    return true;
    }
    }

    ReplyDelete
  19. Hi Peter,

    The authorize method has the following signature:

    public boolean authorize(String secret, int verificationCode)

    where verificationCode is the integer to verify. The string you get from compliant TOTP implementations is a 0-padded integer in base 10. Hence, you have to remove the leading zeroes, convert the remaining String to int and invoke authorize.

    ReplyDelete
  20. I am sorry i didn't get it exactly.
    e.g. if ValidattionCode generated by google authenticator is 002314
    and secret is ABDCD342434DD then what do i have to do exactly in authorize method. The validation is already changed to 2314 with no lead ing zeros when this method is called. Please advise.

    ReplyDelete
  21. Hi Peter, you have to pass the secret key and an integer whose value is 2314.

    I don't understand what this means:

    The validation is already changed to 2314 with no lead ing zeros when this method is called.

    ReplyDelete
  22. Alright here is what i meant. The
    authorize(String secret, int verificationCode) takes verificationCode as int and when i call i convert string value (003456) coming from ui to int it will be changed to 3456 and therefore
    authorize() method will be called with validationCode 3456.

    Now authorize method internally calls return checkCode( secret, verificationCode,
    new Date().getTime(), this.config.getWindowSize());

    This is method is internally doing integer comparisons. I just want to know where to change what to make this leading 0 issue be resolved. Thank you.

    ReplyDelete
  23. Hi Peter,

    Yes, and that method is expected to work as is. I just re-tested it and I found no problem in it.

    ReplyDelete
  24. Yeah. It works. Your help is appreciated. Thank you.

    ReplyDelete
  25. Hi Enrico,
    My case is, Authenticator is working fine when both client phone and server time is same. But when there is a difference in time between client and server, code generated in mobile is not working, when i enter in server to verify.(like server in US and client in India). Mobile time and server running time will be different in this case.
    plz help me...Is this scenario possible in Google Authenticator?

    ReplyDelete
  26. Hi,

    Of course it's possible. The error here is using local time: both your server and your client must agree on a time representation and RFC-6238 mandates the use of UTC. Make sure the server is using the number of seconds elapsed from midnight of 1970/01/01 UTC (the UNIX epoch).

    Cheers,
    --
    Enrico

    ReplyDelete
  27. Thanks for ur explanation,
    How to check my server is using the number of seconds elapsed from midnight of 1970/01/01 UTC (the UNIX epoch)?
    plz elaborate with guidelines....Thanq

    ReplyDelete
  28. I'm sorry but your question is too general: it's a needle in a haystack. I have no idea what your "server" is, what applications you are running and which one of them is involved into TOTP authentication.

    ReplyDelete
  29. Sorry about that...above question is related to your last comment on march 29.
    I copied same code what u provided in the link and passed my secret code to my android mobile GAuthenticator app through qrcode.
    In some servers it is working fine. I can't able to trace correct problem in some servers that code(Gauth app generated) is not matching with the code generated in your code.

    ReplyDelete
  30. That's really weird. Can you dump the value of new Date().getTime() in both a server that is working correctly and one that is not and see if they are correct? That's the only moment in which time information is used.

    In your first comment you hinted at a "difference in time" that I interpreted as a timezone difference. Now I guess that's not the problem. Are server clocks kept in sync with NTP? This algorithm is time sensible: if the skew is too big, you won't be able to validate a TOTP password.

    ReplyDelete
  31. Solved my issue. Server clocks kept in sync with other than unix epoch.

    Thank you for your help...:)

    ReplyDelete
  32. Hi Enrico,
    I downloaded your code and ran Unit Tests mentioned in GoogleAuthTest.java. for both authorise(), authoriseUser() unit tests i got following message. "Check VALIDATION_CODE = false". Is there any thing am i missing related to time settings in my system. I am using Windows, eclipse , jre7.
    Thanks

    ReplyDelete
  33. Hi samba,

    Unfortunately since TOTP password are a function of time, the only way for unit tests to work would be generating a TOTP password and validate it straight away (which BTW it does in another test). Those failures can be ignored since they require some refactoring.

    ReplyDelete
  34. Will the emergency codes work even if the time settings in the client and server are wildly different?

    ReplyDelete
  35. Hi, the emergency codes are managed by the application: they are not validated using TOTP. Hence, I guess you would just look up the provided password against the list of scratch codes still active and if you find a match you will authenticate the user (and mark the code as used).

    ReplyDelete
  36. Hi, and if the application happens to be the login shell? How do we get the shell to respond to the emergency keys? It probably needs to be handlesd before it reaches the application. Perhaps the google authenticator PAM should take care of this. Consider what happens if the time daemon dies at the target host. There's got to be a configuration for this without a work around...

    ReplyDelete
  37. Hi, Just carried out an experiment after this comment. By setting the time off by a day on the server I got locked out of the system for all values provided by the authenticator on my phone. However, when I provided an emergency key it logged me in. I guess the default settings for google authenticator PAM work. -- A

    ReplyDelete
  38. Hi, Enrico

    I've been successfully using your GoogleAuth library for quite a while but now have a need to be able to generate validation codes (i.e., for sending via SMS). All the bits and pieces are there in the GoogleAuth project but are package private (it actually looks like the pieces are there but just wasn't finished). Since I know what changes need to be made, would you be open to me making them and you reviewing them for inclusion in the github project? Thanks!

    ReplyDelete
  39. Hi Stephen, please, open an issue in the project so that we concentrate all the communications there.

    ReplyDelete
  40. Hi All,
    I Have taken your code as Reference and Created the sample JAVA Web Application and using the same in my Project and the Code is working like charm in my system (India). But the same code is not working in the Singapore. I am trying a lot but not getting the reason . Can anybody please help on this.
    Reference from : https://github.com/wstrange/GoogleAuth
    1. Is there any dependency with the Country name .
    Thanks in Advance..

    ReplyDelete
  41. Works nicely. One gotcha: If the verification code from Google Authenticator is not matching the server-side generated code it might be that the Authenticator internal clock is out of sync with Google's servers. To correct this on Android, open the Authenticator app, open top-right menu > Settings > Time correction for codes > Sync now.

    ReplyDelete
  42. Thanks a lot! You made a new blog entry to answer my question; I really appreciate your time and effort.

    ReplyDelete
  43. Hi.

    Thank you very much for doing incredible. It is best tutorial I have searched for GA

    ReplyDelete