Generate totp using aerogear

  otp

Order

This article mainly describes how to use aerogear-otp to generate otp and related source code analysis.

Otp classification

The full name is one time password, which is usually used to support two-factor authentication. There are two main categories

  • HMAC-Based One-time Password (HOTP)

InRFC 4226In the specification

  • Time-based One-time Password (TOTP)

InRFC 6238In the specification

This article focuses on TOTP

  • Client

Its common mobile phone clients include Google Authenticator APP and Aliyun’s identity treasure. Because google’s software is blocked in China, it can use ariyun’sIdentity treasure

  • Server side

For the server, google officially has C code, and for java, many third parties have implemented it. Here, choose aerogear-otp-java provided by jboss, and its maven is as follows

        <dependency>
            <groupId>org.jboss.aerogear</groupId>
            <artifactId>aerogear-otp-java</artifactId>
            <version>1.0.0</version>
        </dependency>

step

The main steps are as follows:

Binding key

  • The server generates a secret for each account and saves it
  • The server provides the two-dimensional code scanning function of the key, which is convenient for the client to scan the bound account number.
  • Users install Google Authenticator APP or Aliyun’s identity treasure on their mobile phones, and scan the secret that the QR code binds to the account.

Use otp authentication

Once secret is bound, it can be verified using one time password.

Example

Generating a two-dimensional code of a client key

String secret = Base32.random();
Totp totp = new Totp(secret);
String uri = totp.uri(account);

Just use this uri as the information of the two-dimensional code.

    /**
     * Prover - To be used only on the client side
     * Retrieves the encoded URI to generated the QRCode required by Google Authenticator
     *
     * @param name Account name
     * @return Encoded URI
     */
    public String uri(String name) {
        try {
            return String.format("otpauth://totp/%s?secret=%s", URLEncoder.encode(name, "UTF-8"), secret);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException(e.getMessage(), e);
        }
    }

Its format otnauth://totp/%s? Secret=%s, Google Authenticator APP or Aliyun’s Identity Treasure all support this format of identification.

Check

 boolean isValid = totp.verify(code);

The source code is as follows

    /**
     * Verifier - To be used only on the server side
     * <p/>
     * Taken from Google Authenticator with small modifications from
     * {@see <a href="http://code.google.com/p/google-authenticator/source/browse/src/com/google/android/apps/authenticator/PasscodeGenerator.java?repo=android#212">PasscodeGenerator.java</a>}
     * <p/>
     * Verify a timeout code. The timeout code will be valid for a time
     * determined by the interval period and the number of adjacent intervals
     * checked.
     *
     * @param otp Timeout code
     * @return True if the timeout code is valid
     *         <p/>
     *         Author: sweis@google.com (Steve Weis)
     */
    public boolean verify(String otp) {

        long code = Long.parseLong(otp);
        long currentInterval = clock.getCurrentInterval();

        int pastResponse = Math.max(DELAY_WINDOW, 0);

        for (int i = pastResponse; i >= 0; --i) {
            int candidate = generate(this.secret, currentInterval - i);
            if (candidate == code) {
                return true;
            }
        }
        return false;
    }

Here is a DELAY_WINDOW parameter, which is introduced to prevent the time difference between the mobile phone client and the server. The default value is 1, which means that the code is allowed to reach the server for verification within 30 seconds after the expiration of the handset.

  • clock

aerogear-otp-java-1.0.0-sources.jar! /org/jboss/aerogear/security/otp/api/Clock.java

public class Clock {

    private final int interval;
    private Calendar calendar;

    public Clock() {
        interval = 30;
    }

    public Clock(int interval) {
        this.interval = interval;
    }

    public long getCurrentInterval() {
        calendar = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC"));
        long currentTimeSeconds = calendar.getTimeInMillis() / 1000;
        return currentTimeSeconds / interval;
    }
}

The default value of this interval is 30, of course you can also change it to 1 minute, that is 60.
In addition, milliseconds are converted to seconds, and then intervals are removed. since/,the integer part is directly taken, so the value of DELAY_WINDOW =N above is actually equivalent to allowing the code of the past n intervals to be verified successfully.

Code generation source code

aerogear-otp-java-1.0.0-sources.jar! /org/jboss/aerogear/security/otp/Totp.java

    private int generate(String secret, long interval) {
        return hash(secret, interval);
    }

    private int hash(String secret, long interval) {
        byte[] hash = new byte[0];
        try {
            //Base32 encoding is just a requirement for google authenticator. We can remove it on the next releases.
            hash = new Hmac(Hash.SHA1, Base32.decode(secret), interval).digest();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (Base32.DecodingException e) {
            e.printStackTrace();
        }
        return bytesToInt(hash);
    }

    private int bytesToInt(byte[] hash) {
        // put selected bytes into result int
        int offset = hash[hash.length - 1] & 0xf;

        int binary = ((hash[offset] & 0x7f) << 24) |
                ((hash[offset + 1] & 0xff) << 16) |
                ((hash[offset + 2] & 0xff) << 8) |
                (hash[offset + 3] & 0xff);

        return binary % Digits.SIX.getValue();
    }

    /**
     * Retrieves the current OTP
     *
     * @return OTP
     */
    public String now() {
        return leftPadding(hash(secret, clock.getCurrentInterval()));
    }

    private String leftPadding(int otp) {
        return String.format("%06d", otp);
    }

Summary

  • interval

The default value is 30, and interval can be modified by the constructor in Clock. However, since the identity of Google Authenticator APP or Aliyun is changed every 30 seconds, this parameter can be set by default.

  • DELAY_WINDOW

Aerogear-otp-java itself does not provide modification of DELAY_WINDOW, but you can inherit Totp and extend it yourself.

doc