I am more than sure that each of you have at least one account with enabled Two-Factor Authentication (2FA). But if you are still unfamiliar with 2FA I’ll give you some general explanation.
It’s a second step in login sequence that asks you to enter 6-digits code sent to you by email, text message or Google Authenticator app and this code expires in 30 or 60 seconds. This second step of authentication makes your account more secure, because even if someone knows your login and password, they still need your physical mobile device to see 2FA code sent to you. Such approach is highly used in almost every single application, especially if this application has deal with money, cryptocurrencies, banks, transactions etc.
So in this article, as you may noticed, we will talk about using Google Authenticator app with your Java-based application. Let’s get started!
Maven Project
First of all, let’s create a maven project and add the following dependencies:
<dependency> <groupId>de.taimos</groupId> <artifactId>totp</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.10</version> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.2.1</version> </dependency>
Here is why we need those dependencies in our project:
- totp – The Time-based One-Time Password algorithm (TOTP) is an extension of the HMAC-based One-time Password algorithm (HOTP) generating a one-time password by instead taking uniqueness from the current time.
- commons-codec– for converting inputs to hex and base32.
- zxing – library for generating QR codes.
Also you need to install Google Authenticator app to your smartphone. It’s totally free and is available on App Store and Play Market. In order to add new entry to Google Authenticator app you need to enter manually a secret key or scan a QR code. I will show you how to use both of them in this tutorial. Let’s start with manual one first.
Google Authenticator requires 20 bytes secret key encoded as base32 string. We need to generate this key using the following code:
public static String generateSecretKey() { SecureRandom random = new SecureRandom(); byte[] bytes = new byte[20]; random.nextBytes(bytes); Base32 base32 = new Base32(); return base32.encodeToString(bytes); }
After running this method you should see the generated string with 32 characters. Please write down this string somewhere since this is our secret key and we will use it later.
QDWSM3OYBPGTEVSPB5FKVDM3CSNCWHVK
Now open your Google Authenticator app. Press ‘plus’ button to add a new entry and select ‘Manual entry’. In the ‘Account’ field enter your email address or any name and in ‘Key’ field paste our secret key. Press save button. You should see your entry in the list with 6-digits code that is changing every 30 seconds.
Now let’s write a method that converts base32 encoded secret keys to hex and uses the TOTP to turn them into 6-digits codes based on the current time.
public static String getTOTPCode(String secretKey) { Base32 base32 = new Base32(); byte[] bytes = base32.decode(secretKey); String hexKey = Hex.encodeHexString(bytes); return TOTP.getOTP(hexKey); }
Now let’s run the following code to generate time-based 6-digits code in sync with Google Authenticator.
String secretKey = "QDWSM3OYBPGTEVSPB5FKVDM3CSNCWHVK"; String lastCode = null; while (true) { String code = getTOTPCode(secretKey); if (!code.equals(lastCode)) { System.out.println(code); } lastCode = code; try { Thread.sleep(1000); } catch (InterruptedException e) {}; }
If you did everything correctly then you should see the 6-digits code being displayed in console every 30 seconds and this code should be synchronized with code displayed in Google Authenticator. If they are not synchronized then make sure that time on your PC and mobile device is in synch.
QR code
We have finished with manual entry example, so let’s jump to QR code which is more user friendly and is used in most cases.
First of all we need to receive bar code data from google. It requires some special format, so here is our method to generate this data.
public static String getGoogleAuthenticatorBarCode(String secretKey, String account, String issuer) { try { return "otpauth://totp/" + URLEncoder.encode(issuer + ":" + account, "UTF-8").replace("+", "%20") + "?secret=" + URLEncoder.encode(secretKey, "UTF-8").replace("+", "%20") + "&issuer=" + URLEncoder.encode(issuer, "UTF-8").replace("+", "%20"); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } }
Here is some notes about the generated string above:
- Account is the user id in system. Usually it’s user’s email or username. It’s used to label entries within Google Authenticator.
- Issuer is a company or organization name and is also used for labelling purposes.
- All dynamic values must be URL encoded.
- Google Authenticator doesn’t seem to deal with spaces encoded as plus signs. Encoding spaces as %20 seems to work.
Now run the above code with some test account name, issuer name and secret key generated previously:
String secretKey = "QDWSM3OYBPGTEVSPB5FKVDM3CSNCWHVK"; String email = "test@gmail.com"; String companyName = "My Awesome Company"; String barCodeUrl = Utils.getGoogleAuthenticatorBarCode(secretKey, email, companyName); System.out.println(barCodeUrl); You should receive a string in the following format: otpauth://totp/Awesome%20Company%3Atest%40gmail.com?secret=7t4gabg72liipmq7n43lt3cw66fel4iz&issuer=Awesome%20Company Now the last step is left – to generate a QR code. Let’s use ZXing library to do it. public static void createQRCode(String barCodeData, String filePath, int height, int width) throws WriterException, IOException { BitMatrix matrix = new MultiFormatWriter().encode(barCodeData, BarcodeFormat.QR_CODE, width, height); try (FileOutputStream out = new FileOutputStream(filePath)) { MatrixToImageWriter.writeToStream(matrix, "png", out); } }
Calling this method with the string returned by the method in the previous step as the 1st argument will write a PNG image to the specified path with the specified height and width.
Go ahead and try to use your generated image with the “Scan barcode” option you saw previously in Google Authenticator. After scanning this QR code you should see a new entry in Google Authenticator entry list.
We can simulate user’ login into the system. Let’s imagine that user entered his credentials and now he need to enter 2FA code from Google Authenticator. Here is the easiest code snippet:
Scanner scanner = new Scanner(System.in); String code = scanner.nextLine(); if (code.equals(getTOTPCode(secretKey))) { System.out.println("Logged in successfully"); } else { System.out.println("Invalid 2FA Code"); }
Of course this is just a pseudo code, but it simulates the real 2FA flow and shows you if you did everything correctly.
Conclusion
Now you know the basics of two-factor authentication and you can try to integrate it into your web-based application with a small efforts to add more security to your application.
Hope you like this article. Do not forget to press ‘Clap’ button and share this article with your friends. The full example can be found on my GitHub account.
Have a great day! And don’t forget to enable 2FA in all your accounts:)