Статьи

Google Authenticator: использование его с вашим собственным сервером аутентификации Java

Приложение Google Authenticator для мобильных устройств — очень удобное приложение, в котором реализован алгоритм TOTP (указанный в RFC 6238 ). Используя Google Authenticator, вы можете создавать временные пароли, которые можно использовать для авторизации пользователей на сервере аутентификации, который разделяет секретный ключ запрашивающих пользователей.

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) {
  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 .

Статьи по Теме :