Создать ключ AES только на основе пароля

У меня есть приложение, которое может потребовать пароль для аутентификации.
Это приложение не обрабатывает конфиденциальные данные, поскольку этот "пароль" выбирается хостом и сообщается "клиентам" через другой канал (WhatsApp или любой другой). Поэтому, когда клиент хочет аутентифицироваться, хост генерирует случайную строку, которая отправляется клиенту.
Затем клиент шифрует эту произвольную строку с помощью пароля, который вводит пользователь.
Зашифрованная случайная строка отправляется обратно на хост.
Хост расшифровывает эту зашифрованную строку с помощью ключа, сгенерированного тем же паролем.
Если незашифрованная и исходная строка совпадают, пользователь входит в систему.
Это то, что я придумал до сих пор:

String base64;
char[] password = "password".toCharArray();
String randomString = new BigInteger(130, new SecureRandom()).toString(32);
try {
    //Encrypt Client Side
    SecretKey key = new SecretKeySpec(SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512").generateSecret(new PBEKeySpec(password)).getEncoded(), "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, key);
    base64 = Base64.getEncoder().encodeToString(cipher.doFinal(randomString.getBytes(StandardCharsets.UTF_8)));
} catch (GeneralSecurityException e) {
    throw new IllegalStateException(e);
}
try {
    //Decrypt Server Side
    SecretKey key = new SecretKeySpec(SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512").generateSecret(new PBEKeySpec(password)).getEncoded(), "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, key);
    //Check if both strings match
    System.out.println(Arrays.equals(cipher.doFinal(Base64.getDecoder().decode(base64)), randomString.getBytes(StandardCharsets.UTF_8)));
} catch (GeneralSecurityException e) {
    throw new IllegalStateException(e);
}

К сожалению, этот код вызывает исключение: java.security.spec.InvalidKeySpecException: Salt not found
Должен ли я использовать другой алгоритм, или я должен генерировать соль путем хеширования самого пароля, или совершенно другой подход? Я хотел бы избежать отправки сгенерированной соли вместе со случайной строкой

1 ответ

Решение

Вы должны дать PBEKeySpec что нужно для генерации достаточного количества битов для ключа AES. Вам нужно одну и ту же соль с обеих сторон, чтобы вы могли сделать:

 byte[] salt = new byte[8];
 System.arraycopy(randomString.getBytes("UTF-8"), 0, salt, 0, 8);

Теперь замените ваш PBEKeySpec с new PBEKeySpec(password, salt, 10, 128) и все должно работать.

Другие вопросы по тегам