Pregunta

Estoy tratando de envolver mi cerebro alrededor de la generación de un caso no 6 dígitos / caracteres sensibles que expira la contraseña de una sola vez.

Mi fuente es http://tools.ietf.org/html/rfc4226#section -5

En primer lugar la definición de los parámetros

C       8-byte counter value, the moving factor.  This counter
       MUST be synchronized between the HOTP generator (client)
       and the HOTP validator (server).

K       shared secret between client and server; each HOTP
       generator has a different and unique secret K.

T       throttling parameter: the server will refuse connections
       from a user after T unsuccessful authentication attempts.

A continuación, tenemos el algoritmo para generar el HOTP

As the output of the HMAC-SHA-1 calculation is 160 bits, we must
   truncate this value to something that can be easily entered by a
   user.

                   HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))

A continuación, tenemos Truncar definido como

String = String[0]...String[19]
 Let OffsetBits be the low-order 4 bits of String[19]
 Offset = StToNum(OffsetBits) // 0 <= OffSet <= 15
 Let P = String[OffSet]...String[OffSet+3]
 Return the Last 31 bits of P

Y a continuación, un ejemplo se ofrece para un HOTP 6 dígitos

The following code example describes the extraction of a dynamic
binary code given that hmac_result is a byte array with the HMAC-
SHA-1 result:

    int offset   =  hmac_result[19] & 0xf ;
    int bin_code = (hmac_result[offset]  & 0x7f) << 24
       | (hmac_result[offset+1] & 0xff) << 16
       | (hmac_result[offset+2] & 0xff) <<  8
       | (hmac_result[offset+3] & 0xff) ;

Soy más bien a una pérdida en el intento de convertir esto en código C # útil para generar contraseñas de un. Ya tengo código para crear un HMAC vencimiento de la siguiente manera:

byte[] hashBytes = alg.ComputeHash(Encoding.UTF8.GetBytes(input));
byte[] result = new byte[8 + hashBytes.Length];

hashBytes.CopyTo(result, 8);
BitConverter.GetBytes(expireDate.Ticks).CopyTo(result, 0);

No estoy seguro de cómo ir de eso, a 6 dígitos como se propone en los algoritmos anteriores.

¿Fue útil?

Solución

Hay dos cuestiones aquí:

  1. Si va a generar alfa-numérico, usted no está conforme con el RFC - en este punto, se puede simplemente tomar cualquier N bytes y convertirlos en una cadena hexadecimal y get alfa-numérico. O, convertirlos a la base 36 si quieres az y 0-9. Sección 5.4 del RFC se está dando la calc HOTP estándar para un parámetro Digit conjunto (nótese que Digit es un parámetro junto con C, K y T). Si usted está eligiendo hacer caso omiso de esta sección, entonces no es necesario convertir el código -. Sólo tiene que utilizar lo que quiere

  2. Su matriz de bytes "número" tiene el tiempo de caducidad, simplemente rellena en los primeros 8 bytes después de hash. Si el truncamiento para alfanumérico de 6 dígitos no recoge estos junto con partes de la almohadilla, puede ser que también no puede calcular en absoluto. También es muy fácil de "falso" o bien la repetición - el secreto de hash una vez, a continuación, poner lo que desee garrapatas en frente de ella - no es realmente una contraseña de una sola vez. Tenga en cuenta que C parámetro en el RFC tiene la intención de cumplir con la ventana de expirar y debe añadirse a la entrada antes calcular el código hash.

Otros consejos

Para cualquier persona interesada, lo hice encontrar una manera de caducidad acumulación en mi contraseña de una sola vez. El método consiste en utilizar el tiempo creado hasta el último minuto (ignorando segundos, milisegundos, etc). Una vez que tenga ese valor, utilice las garrapatas de la fecha y hora como su contador, o variable C.

otpLifespan es mi HOTP esperanza de vida en cuestión de minutos.

DateTime current = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 
    DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, 0);

for (int x = 0; x <= otpLifespan; x++)
{
    var result = NumericHOTP.Validate(hotp, key, 
        current.AddMinutes(-1 * x).Ticks);

    //return valid state if validation succeeded

    //return invalid state if the passed in value is invalid 
    //  (length, non-numeric, checksum invalid)
}

//return expired state

Mi HOTP expira extiende desde mi HOTP numérico que tiene un método de validación estática que comprueba la longitud, asegura que es numérico, valida la suma de control si se utiliza, y, finalmente, compara el HOTP aprobada en con un uno generado.

La única desventaja de esto es que cada vez que validar un HOTP expira, el peor de los casos es comprobar n + 1 valores HOTP donde n es el tiempo de vida en cuestión de minutos.

El ejemplo de código Java en el documento que describe el RFC 4226 fue un movimiento muy sencillo en C #. La única pieza que realmente tenía que poner ningún esfuerzo en la reescritura fue el método de hash.

private static byte[] HashHMACSHA1(byte[] keyBytes, byte[] text)
{
    HMAC alg = new HMACSHA1(keyBytes);

    return alg.ComputeHash(text);
}

Espero que esto ayude a nadie intentar generar contraseñas de un.

Este fragmento debe hacer lo que usted está pidiendo:

  public class UniqueId
{
    public static string GetUniqueKey()
    {
        int maxSize = 6; // whatever length you want
        char[] chars = new char[62];
        string a;
        a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
           char[] chars = new char[a.Length];
        chars = a.ToCharArray();
        int size = maxSize;
        byte[] data = new byte[1];
        RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();
        crypto.GetNonZeroBytes(data);
        size = maxSize;
        data = new byte[size];
        crypto.GetNonZeroBytes(data);
        StringBuilder result = new StringBuilder(size);
        foreach (byte b in data)
        { result.Append(chars[b % (chars.Length - 1)]); }
        return result.ToString();
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top