2016-08-09 1 views
0

Salut J'utilise Counter OTP avec HOTPAlgorithm qui est comme ci-dessous. Lorsque j'essaie de générer du code en utilisant google authenticator App en entrant la même clé secrète que celle de mon serveur, cela produit un code différent de celui de mon serveur qui est généré par l'algorithme ci-dessous.Veuillez nous aider.Google Authenticator Counter OTP

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

pourquoi ne pas utiliser une bibliothèque/classe existante pour HOTP/RFC4226? https://github.com/Jakobo/hotp-php – cornelinux

+0

J'ai besoin de l'intégrer dans java.anyway merci, j'ai obtenu la résolution en changeant la valeur du facteur de mouvement (valeur du compteur) à 1 au lieu de 0. –

Répondre

0

J'ai eu cette résolu en changeant la valeur initiale du compteur qui devrait être au départ 1 et aussi au moment de la validation, vous devez passer clé secrète avec le format base32 de décodage. L'algorithme restera le même que ci-dessus

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