Реализуется ли HKDF в архитектуре криптографии Java?
В приложении, которое я пишу, мне нужно сделать HKDF, чтобы получить два разных ключа из одного пароля. В поисках примеров того, как это сделать в Java, я нашел эти два:
- https://github.com/WhisperSystems/libsignal-protocol-java/blob/master/java/src/main/java/org/whispersystems/libsignal/kdf/HKDF.java
- https://www.javatips.net/api/keywhiz-master/hkdf/src/main/java/keywhiz/hkdf/Hkdf.java
В обоих случаях HKDF реализован поверх HMAC, предоставленного JCA. Я еще не читал эти реализации подробно, но мне было интересно, разве это не реализовано нигде в JCA или само по себе? Должен ли я реализовать свой собственный HKDF?
Больше всего меня беспокоит ошибка в применении аргумента информации. Это выглядит нетривиально и критично.
1 ответ
Реализации HKDF на Java
Нет, функция выведения ключа (HKDF) на основе хешированного сообщения аутентификации (HMAC), как и большинство KDF, не имеет стандартной реализации в JCA.
Есть некоторые реализации, встроенные в другие проекты (как вы уже сказали):
Также есть, конечно, Bouncy Castle, который использует свои собственные реализации Hmac/Mac и API. BC, однако, является большой зависимостью и может быть непрактичным, например, для встроенного или мобильного варианта использования. Для этого я реализовал отдельную легковесную библиотеку Java (проходящую через все тестовые векторы RFC 5869), которая работает с любым javax.crypto.Mac
пример:
Если вы предпочитаете, вы можете, конечно, реализовать его самостоятельно, это довольно прямолинейная спецификация при использовании встроенной реализации JCA Hmac.
Информационный параметр в HKDF
Из RFC 5869:
Хотя значение 'info' является необязательным в определении HKDF, оно
часто имеет большое значение в приложениях. Его основная цель заключается в
привязать полученный ключевой материал к конкретному приложению и контексту
Информация. (...) В частности, это может предотвратить создание одного и того же ключевого материала для разных контекстов.
Так, например, если вы хотите получить секретный ключ и IV из одного и того же исходного материала, вы должны использовать параметр info ( используя эту библиотеку):
//example input
String userInput = "this is a user input with bad entropy";
HKDF hkdf = HKDF.fromHmacSha256();
//extract the "raw" data to create output with concentrated entropy
byte[] pseudoRandomKey = hkdf.extract(staticSalt32Byte, userInput.getBytes(StandardCharsets.UTF_8));
//create expanded bytes for e.g. AES secret key and IV
byte[] expandedAesKey = hkdf.expand(pseudoRandomKey, "aes-key".getBytes(StandardCharsets.UTF_8), 16);
byte[] expandedIv = hkdf.expand(pseudoRandomKey, "aes-iv".getBytes(StandardCharsets.UTF_8), 16);
//Example boilerplate encrypting a simple string with created key/iv
SecretKey key = new SecretKeySpec(expandedAesKey, "AES"); //AES-128 key
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(expandedIv));
byte[] encrypted = cipher.doFinal("my secret message".getBytes(StandardCharsets.UTF_8));