Enterprise Java

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;
}

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!

Reference: Google Authenticator: Using It With Your Own Java Authentication Server from our JCG partner Enrico Crisostomo at the The Grey Blog.

 Related Articles :

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

7 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Ben Reeves
Ben Reeves
12 years ago

Thank you for posting this. Just a quick note to any developers that might be confused by this. You have to divide the current time in milliseconds by 30000 before calling check_code e.g.

if (check_code(shared_secret, user_code, new Date().getTime()  / 30000)) { //OK!}https://blockchain.info/wallet/google-authenticator

mohammed idrissi
mohammed idrissi
12 years ago

:
: ok

manish
manish
11 years ago

hI
Thanks for the valuable code .
But when i used these code . validity always showing false .
Please help me in checking validity ..
Thanks…

Nitin Surana
10 years ago

hey!

The example was good. But how does it verify a user ? Anyone with the app installed, can generate the passwd ? How does it relate to a particular user ?

To be very specific here I’ve explained in detail :

http://stackoverflow.com/questions/22699329/how-to-use-googleauthenticator-for-tfa-two-factor-authentication-in-a-custom-n

Regards
Nitin

Michael Lowden
Michael Lowden
5 years ago
Reply to  Nitin Surana

This is a separate path in your authentication code. You must already have user/pass logic enabled. This merely extends that as a 2nd factor. Here’s a quick example of what I’m thinking to do. ex (pseudo-code of what I’d do): public MyUser login(String username, String password, String authcode) { // check username/password combo // you should already have all the logic for this section on your site long userID = MyAuth.getUserID(username, password); // returns the userID if successful // spawn User object // MyUser class will not self-populate beyond User.ID if 2nd factor is needed MyUser user = new MyUser(userID);… Read more »

Zero-gravity
10 years ago

First, Thank you for post.

But I find out just one problem.

String format = “https://www.google.com/chart?chs=200×200&chld=M%%7C0&cht=qr&chl=otpauth://totp/%s@%s%%3Fsecret%%3D%s”;

Above code isn’t correct.

You will need to be replaced by
String format = “http://chart.apis.google.com/chart?cht=qr&chs=300×300&chl=otpauth://totp/%s@%s%%3Fsecret%%3D%s&chld=H|0”;

Because, your url of format can’t get QR code image by link.

Thomas
Thomas
3 years ago
Reply to  Zero-gravity

Hi, i think it is an very bad idea to use external server for QR generation of an secret key.
Event worse if you supply the secret in the query string that may be logged.

Back to top button