2016-08-09 2 views
0

Привет Я использую OTP на основе Counter с HOTPAlgorithm, который приведен ниже.OTP на основе Authenticator на основе OTP

Когда я пытаюсь сгенерировать код с помощью приложения аутентификации Google, введя тот же секретный ключ, что и на моем сервере, его производят другой код, чем мой фактический код на стороне сервера, который генерируется ниже алгоритмом. Пожалуйста, помогите.

import java.io.UnsupportedEncodingException; 
import java.net.URLEncoder; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 

import javax.crypto.Mac; 
import javax.crypto.spec.SecretKeySpec; 

import org.apache.http.client.utils.URIBuilder; 

public class HOTPAlgorithm { 
    private HOTPAlgorithm() { 
    } 

    // These are used to calculate the check-sum digits. 
    // 0 1 2 3 4 5 6 7 8 9 
    private static final int[] doubleDigits = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 }; 

    /** 
    * Calculates the checksum using the credit card algorithm. This algorithm 
    * has the advantage that it detects any single mistyped digit and any 
    * single transposition of adjacent digits. 
    * 
    * @param num 
    *   the number to calculate the checksum for 
    * @param digits 
    *   number of significant places in the number 
    * 
    * @return the checksum of num 
    */ 
    public static int calcChecksum(long num, int digits) { 
     boolean doubleDigit = true; 
     int total = 0; 
     while (0 < digits--) { 
      int digit = (int) (num % 10); 
      num /= 10; 
      if (doubleDigit) { 
       digit = doubleDigits[digit]; 
      } 
      total += digit; 
      doubleDigit = !doubleDigit; 
     } 
     int result = total % 10; 
     if (result > 0) { 
      result = 10 - result; 
     } 
     return result; 
    } 

    /** 
    * This method uses the JCE to provide the HMAC-SHA-1 algorithm. HMAC 
    * computes a Hashed Message Authentication Code and in this case SHA1 is 
    * the hash algorithm used. 
    * 
    * @param keyBytes 
    *   the bytes to use for the HMAC-SHA-1 key 
    * @param text 
    *   the message or text to be authenticated. 
    * 
    * @throws NoSuchAlgorithmException 
    *    if no provider makes either HmacSHA1 or HMAC-SHA-1 digest 
    *    algorithms available. 
    * @throws InvalidKeyException 
    *    The secret provided was not a valid HMAC-SHA-1 key. 
    * 
    */ 
    public static byte[] hmac_sha1(byte[] keyBytes, byte[] text) throws NoSuchAlgorithmException, InvalidKeyException { 
     // try { 
     Mac hmacSha1; 
     try { 
      hmacSha1 = Mac.getInstance("HmacSHA1"); 
     } catch (NoSuchAlgorithmException nsae) { 
      hmacSha1 = Mac.getInstance("HMAC-SHA-1"); 
     } 
     SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW"); 
     hmacSha1.init(macKey); 
     return hmacSha1.doFinal(text); 
     // } catch (GeneralSecurityException gse) { 
     // throw new UndeclaredThrowableException(gse); 
     // } 
    } 

    private static final int[] DIGITS_POWER // 0 1 2 3 4 5 6 7 8 
    = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 }; 

    /** 
    * This method generates an OTP value for the given set of parameters. 
    * 
    * @param secret 
    *   the shared secret 
    * @param movingFactor 
    *   the counter, time, or other value that changes on a per use 
    *   basis. 
    * @param codeDigits 
    *   the number of digits in the OTP, not including the checksum, 
    *   if any. 
    * @param addChecksum 
    *   a flag that indicates if a checksum digit should be appended 
    *   to the OTP. 
    * @param truncationOffset 
    *   the offset into the MAC result to begin truncation. If this 
    *   value is out of the range of 0 ... 15, then dynamic truncation 
    *   will be used. Dynamic truncation is when the last 4 bits of 
    *   the last byte of the MAC are used to determine the start 
    *   offset. 
    * @throws NoSuchAlgorithmException 
    *    if no provider makes either HmacSHA1 or HMAC-SHA-1 digest 
    *    algorithms available. 
    * @throws InvalidKeyException 
    *    The secret provided was not a valid HMAC-SHA-1 key. 
    * 
    * @return A numeric String in base 10 that includes 
    */ 
    static public String generateOTP(byte[] secret, long movingFactor, int codeDigits, boolean addChecksum, 
      int truncationOffset) throws NoSuchAlgorithmException, InvalidKeyException { 
     // put movingFactor value into text byte array 
     /* 
     * Base32 base32 = new Base32(); secret=base32.decode(secret); 
     */ 
     String result = null; 
     int digits = addChecksum ? (codeDigits + 1) : codeDigits; 
     byte[] text = new byte[8]; 
     for (int i = text.length - 1; i >= 0; i--) { 
      text[i] = (byte) (movingFactor & 0xff); 
      movingFactor >>= 8; 
     } 

     // compute hmac hash 

     byte[] hash = hmac_sha1(secret, text); 
     //System.out.println("hash" + new String(hash)); 
     // put selected bytes into result int 
     int offset = hash[hash.length - 1] & 0xf; 
     /*if ((0 <= truncationOffset) && (truncationOffset < (hash.length - 4))) { 
      offset = truncationOffset; 
     }*/ 

     //offset = hash[hash.length - 1] & 0xF; 
     //System.out.println("offset"+offset); 

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

     int otp = (int) (binary % Math.pow(10, codeDigits)); 
     if (addChecksum) { 
      otp = (otp * 10) + calcChecksum(otp, codeDigits); 
     } 
     result = Integer.toString(otp); 
     while (result.length() < digits) { 
      result = "0" + result; 
     } 
     return result; 
    } 

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

     /* 
     * for (int i = data.length - 1; i >= 0; i--) { data[i] = (byte) (value 
     * & 0xff); value >>= 8; } 
     */ 

     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, "HmacSHA1"); 

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

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

      // Processing the instant of time and getting the encrypted data. 
      byte[] hash = mac.doFinal(data); 
      System.out.println("hash1" + new String(hash)); 
      // Building the validation code performing dynamic truncation 
      // (RFC4226, 5.3. Generating an HOTP value) 
      int offset = hash[hash.length - 1] & 0xF; 
      // offset=0; 
      // We are using a long because Java hasn't got an unsigned integer 
      // type 
      // and we need 32 unsigned bits). 
      int binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16) 
        | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff); 

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

      // Clean bits higher than the 32nd (inclusive) and calculate the 
      // module with the maximum validation code value. 
      truncatedHash &= 0x7FFFFFFF; 
      truncatedHash %= (int) Math.pow(10, 6); 
      int otp = (int) (binary % Math.pow(10, 6)); 

      // Returning the validation code to the caller. 
      return (int) truncatedHash; 
     } catch (Exception ex) { 
      // Logging the exception. 

      return 0; 
      // We're not disclosing internal error details to our clients. 

     } 

    } 
    private static final String TOTP_URI_FORMAT = 
       "https://chart.eCWapis.com/chart?chs=200x200&chld=M%%7C0&cht=qr&chl=%s"; 

    public static String internalURLEncode(String s) { 
     try { 
      return URLEncoder.encode(s, "UTF-8"); 
     } catch (UnsupportedEncodingException e) { 
      throw new RuntimeException("UTF-8 encoding is not supported by URLEncoder.", e); 
     } 
    } 

    public static String getOtpAuthURL(String issuer, String accountName, String credentials) { 

     return String.format(TOTP_URI_FORMAT, internalURLEncode(getOtpAuthTotpURL(issuer, accountName, credentials))); 
    } 

    public static String getOtpAuthTotpURL(String issuer, String accountName, String credentials) { 

     URIBuilder uri = new URIBuilder().setScheme("otpauth").setHost("totp") 
       .setPath("/" + formatLabel(issuer, accountName)).setParameter("secret", credentials); 

     if (issuer != null) { 
      if (issuer.contains(":")) { 
       throw new IllegalArgumentException("Issuer cannot contain the \':\' character."); 
      } 

      uri.setParameter("issuer", issuer); 
     } 

     /* 
     * The following parameters aren't needed since they are all defaults. 
     * We can exclude them to make the URI shorter. 
     */ 
     // uri.setParameter("algorithm", "SHA1"); 
     // uri.setParameter("digits", "6"); 
     // uri.setParameter("period", "30"); 

     return uri.toString(); 

    } 

    private static String formatLabel(String issuer, String accountName) { 
     if (accountName == null || accountName.trim().length() == 0) { 
      throw new IllegalArgumentException("Account name must not be empty."); 
     } 

     StringBuilder sb = new StringBuilder(); 
     if (issuer != null) { 
      if (issuer.contains(":")) { 
       throw new IllegalArgumentException("Issuer cannot contain the \':\' character."); 
      } 

      sb.append(issuer); 
      sb.append(":"); 
     } 

     sb.append(accountName); 

     return sb.toString(); 
    } 

} 
+0

Почему бы не использовать существующую библиотеку/класс для HOTP/RFC4226? https://github.com/Jakobo/hotp-php – cornelinux

+0

Мне нужно интегрировать его в java.anyway thanks, я получил разрешение, изменив значение коэффициента перемещения (значение счетчика) на 1 вместо 0. –

ответ

0

Я получил это решить путем изменения начального значения счетчика, которое должно быть первоначально 1, а также во время проверки вам нужно передать секретный ключ формат base32 декодирования. Алгоритм останется таким же, как указано выше

String secret = "ABCDEABCDE"; 
Base32 base32 = new Base32(); 
byte barray[]=base32 .decode(secret); 
HOTPAlgorithm.generateOTP(barray, 1l, 6, false, 0);//1l is initial moving factor which is 
Смежные вопросы