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 bufferbyte[] 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 Base32Base32 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 .
Статьи по Теме :