예제 #1
0
  /**
   * Calculates the verification code of the provided key at the specified instant of time using the
   * algorithm specified in RFC 6238.
   *
   * @param key the secret key in binary format.
   * @param tm the instant of time.
   * @return the validation code for the provided key at the specified instant of time.
   */
  public static int calculateCode(byte[] key, long tm) {
    // Allocating an array of bytes to represent the specified instant
    // of time.
    byte[] data = new byte[8];
    long value = tm;

    // Converting the instant of time from the long representation to a
    // big-endian array of bytes (RFC4226, 5.2. Description).
    for (int i = 8; i-- > 0; value >>>= 8) {
      data[i] = (byte) value;
    }

    // Building the secret key specification for the HmacSHA1 algorithm.
    SecretKeySpec signKey = new SecretKeySpec(key, HMAC_HASH_FUNCTION);

    try {
      // Getting an HmacSHA1 algorithm implementation from the JCE.
      Mac mac = Mac.getInstance(HMAC_HASH_FUNCTION);

      // Initializing the MAC algorithm.
      mac.init(signKey);

      // Processing the instant of time and getting the encrypted data. [!!!]
      byte[] hash = mac.doFinal(data);

      // Building the validation code performing dynamic truncation
      // (RFC4226, 5.3. Generating an HOTP value)
      int offset = hash[hash.length - 1] & 0xF;

      //            System.out.println(offset);
      // We are using a long because Java hasn't got an unsigned integer type
      // and we need 32 unsigned bits).
      long truncatedHash = 0;

      for (int i = 0; i < 4; ++i) {
        truncatedHash <<= 8;

        // Java bytes are signed but we need an unsigned integer:
        // cleaning off all but the LSB.
        truncatedHash |= (hash[offset + i] & 0xFF);
      }

      // Cleaning bits higher than the 32nd and calculating the module with the
      // maximum validation code value.
      truncatedHash &= 0x7FFFFFFF;
      truncatedHash %= SECRET_KEY_MODULE;

      // Returning the validation code to the caller.
      return (int) truncatedHash;
    } catch (NoSuchAlgorithmException | InvalidKeyException ex) {
      // Logging the exception.
      LOGGER.log(Level.SEVERE, ex.getMessage(), ex);

      // We're not disclosing internal error details to our clients.
      throw new GoogleAuthenticatorException("The operation cannot be " + "performed now.");
    }
  }