Google Authenticator в основном используется для доступа к службам Google с использованием двухфакторной аутентификации. Однако вы можете воспользоваться Google Authenticator, чтобы сгенерировать основанный на времени пароль для аутентификации на вашем сервере. Реализация такого сервера довольно проста в Java, и вы можете получить некоторое вдохновение, получив исходный код модуля PAM Google Authenticator. В этом посте мы рассмотрим простую реализацию алгоритма TOTP в классе Java.
Генерация секретного ключа
- Для генерации секретного ключа мы будем использовать генератор случайных чисел для заполнения байтового массива требуемого размера. В этом случае мы хотим:
- Секретный ключ в кодировке Base32, состоящий из 16 символов: поскольку при кодировании Base32 x байтов генерируется 8x / 5 символов, для секретного ключа мы будем использовать 10 байтов.
Некоторые скретч-коды (используя жаргон Google).
1
2
3
4
5
6
7
8
|
// Allocating the buffer byte [] buffer = new byte [secretSize + numOfScratchCodes * scratchCodeSie]; // Filling the buffer with random numbers. // Notice: you want to reuse the same random generator // while generating larger random number sequences. new Random().nextBytes(buffer); |
Теперь мы хотим извлечь байты, соответствующие секретному ключу, и закодировать его, используя кодировку Base32. Я использую библиотеку Apache Common Codec, чтобы получить реализацию кодека:
1
2
3
4
5
|
// Getting the key and converting it to Base32 Base32 codec = new Base32(); byte [] secretKey = Arrays.copyOf(buffer, secretSize); byte [] bEncodedKey = codec.encode(secretKey); String encodedKey = new String(bEncodedKey); |
Загрузка ключа в Google Authenticator
Вы можете вручную загрузить ключ в Google Authenticator или создать штрих-код QR, чтобы приложение загружало его из него. Если вы хотите сгенерировать QR-код с помощью сервисов Google, вы можете создать соответствующий URL-адрес с помощью следующего кода:
1
2
3
4
5
6
7
|
public static String getQRBarcodeURL( String user, String host, String secret) { String format = "https://www.google.com/chart?chs=200x200&chld=M%%7C0&cht=qr&chl=otpauth://totp/%s@%s%%3Fsecret%%3D%s" ; return String.format(format, user, host, secret); } |
Проверка кода
Теперь, когда мы сгенерировали ключ и наши пользователи могут загрузить их в свое приложение Google Authenticator, нам необходим код, необходимый для проверки сгенерированных кодов подтверждения. Вот реализация Java алгоритма, указанного в RFC 6238:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
private static boolean check_code( String secret, long code, long t) throws NoSuchAlgorithmException, InvalidKeyException { Base32 codec = new Base32(); byte [] decodedKey = codec.decode(secret); // Window is used to check codes generated in the near past. // You can use this value to tune how far you're willing to go. int window = 3 ; for ( int i = -window; i <= window; ++i) { long hash = verify_code(decodedKey, t + i); if (hash == code) { return true ; } } // The validation code is invalid. return false ; } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
private static int verify_code( byte [] key, long t) throws NoSuchAlgorithmException, InvalidKeyException { byte [] data = new byte [ 8 ]; long value = t; for ( int i = 8 ; i-- > 0 ; value >>>= 8 ) { data[i] = ( byte ) value; } SecretKeySpec signKey = new SecretKeySpec(key, "HmacSHA1" ); Mac mac = Mac.getInstance( "HmacSHA1" ); mac.init(signKey); byte [] hash = mac.doFinal(data); int offset = hash[ 20 - 1 ] & 0xF ; // We're using a long because Java hasn't got unsigned int. long truncatedHash = 0 ; for ( int i = 0 ; i < 4 ; ++i) { truncatedHash <<= 8 ; // We are dealing with signed bytes: // we just keep the first byte. truncatedHash |= (hash[offset + i] & 0xFF ); } truncatedHash &= 0x7FFFFFFF ; truncatedHash %= 1000000 ; return ( int ) truncatedHash; } |
Вывод
Теперь вы можете использовать приложения Google Authenticator и использовать его для генерации паролей на основе времени для ваших пользователей, аутентифицированных на вашем собственном сервере аутентификации.
Как видите, необходимый код довольно прост, и все необходимые криптографические функции предоставляются самой средой выполнения. Единственная неприятность связана с подписанными типами в Java. Наслаждайтесь!
Справка: Google Authenticator: использование его с вашим собственным сервером аутентификации Java от нашего партнера JCG Энрико Кризостомо из The Grey Blog .
Статьи по Теме :