Рубрики
Без рубрики

Хранилища ключей Java — кровавые подробности

Глубоко погрузитесь в то, как хранилища ключей Java шифруют ключи на диске. Помечено java, криптографией, хранилищем ключей.

Первоначально размещенный на моем блог

Хранилища ключей Java используются для хранения ключевых материалов и связанных сертификатов в зашифрованном виде и с защитой целостности. Как и все Java, этот механизм является подключаемым, и поэтому существует множество различных опций. Существует множество статей, в которых описываются различные типы и способы их инициализации, загрузки ключей и сертификатов и т. Д. Однако отсутствует подробная техническая информация о том, как именно эти хранилища ключей хранят и защищают ваш ключевой материал. В этом посте предпринята попытка собрать эти важные сведения в одном месте для наиболее распространенных хранилищ ключей.

Каждое хранилище ключей имеет общий пароль, используемый для защиты всего хранилища, и может дополнительно иметь пароли для каждой записи с секретным или закрытым ключом (если ваш сервер поддерживает это).

Хранилище ключей Java (JKS)

Оригинальный формат Sun JKS (хранилище ключей Java) представляет собой собственный файл двоичного формата, в котором могут храниться только асимметричные закрытые ключи и связанные с ними сертификаты X.509.

Отдельные записи с закрытым ключом защищены простым потоковым шифром с домашней обработкой – в основном пароль засаливается (160 бит) и хэшируется с помощью SHA—1 в тривиальной цепной конструкции до тех пор, пока он не сгенерирует достаточно выходных байтов для ввода в закрытый ключ. Затем он сохраняет простой тег аутентификации, состоящий из SHA-1 (пароль + байты закрытого ключа) — это байты незашифрованного закрытого ключа. Другими словами, это схема Шифрования и MAC с домоткаными конструкциями, основанными на простом SHA-1 с префиксным ключом. (Эта схема имеет OID 1.3.6.1.4.1.42.2.17.1.1).

Весь архив снова защищен целостностью с помощью хэш-конструкции с префиксным ключом, состоящей из хэша SHA1 байта UTF-16 байтов исходного пароля хранилища ключей, за которым следуют байты UTF-8 фразы “Могучая Афродита” (я не шучу), за которыми следуют байты записей хранилища закодированных ключей.

Если каждая часть этого описания не заставила вас кричать на экране в равной степени от ужаса и недоумения, то вы, вероятно, не до конца поняли, насколько это ужасно. Не используйте его даже для простого хранения сертификатов — его устойчивость к взлому, если что-то еще хуже, чем шифрование.

Хранилище ключей JCE (JCEKS)

Позже Sun обновила криптографические возможности JVM с помощью расширений криптографии Java (JCE). С этим они также представили новый проприетарный формат хранилища ключей: JCEKS.

JCEKS использует “PBEWithMD5AndTripleDES” для шифрования отдельных записей ключей с помощью 64-разрядной случайной соли и 200 000 итераций PBKDF1 для получения ключа. TripleDES используется с 3 ключами (“шифровать-расшифровывать-шифровать”) в режиме CBC. Отдельной защиты целостности отдельных ключей не существует, что нормально, если целостность архива в целом защищена, но это означает, что контроль доступа эффективно осуществляется на уровне всего хранилища ключей. Это не ужасно с криптографической точки зрения, но определенно может быть улучшено — ни MD5, ни ТройняШКИ больше не считаются безопасными, и это было давно время с тех пор, как кто-либо рекомендовал их для новых проектов. Однако это также не было бы тривиальной попыткой сломать его.

JCEKS использует тот же нелепый хэш с префиксным ключом “Могучая Афродита”, что и JKS, для защиты целостности всего архива. Вероятно, лучше всего предположить, что ни в одном из этих хранилищ ключей нет серьезной защиты целостности.

ПККС №12

Помимо этих собственных хранилищ ключей, Java также поддерживает “стандартные” хранилища ключей формата PKCS#12. Причина пугающих цитат вокруг “стандарта” заключается в том, что, хотя это действительно стандартный формат, он очень гибкий, и поэтому на практике существуют существенные различия между тем, какие форматы “пакета ключей” и алгоритмы шифрования поддерживаются различным программным обеспечением. Например, когда вы храните симметричные объекты секретного ключа в хранилище ключей PKCS#12 из Java, OpenSSL не может их прочитать, поскольку они используют тип пакета (“secretbag” – OID 1.2.840.113549.1.12.10.1.5), который он не понимает.

Java использует версию 3 стандартного формата PKCS#12. Он хранит секретные ключи в вышеупомянутом формате “секретный пакет” и асимметричные закрытые ключи в формате “PKCS#8 Закрытый пакет ключей” (OID 1.2.840.113549.1.12.10.1.2). Это просто диктует формат байтов на диске. В обоих случаях фактический ключевой материал шифруется с использованием той или иной формы шифрования на основе пароля (PBE). По умолчанию это “PBEWithSHA1AndDESede” — “DESede” – другое название для TripleDES в режиме шифрования-дешифрования-шифрования, так что это очень похоже на режим, используемый JCEK, за исключением использования немного лучшего (но все еще устаревшего) хэша в форме SHA-1. По умолчанию для этого используется 160-битная соль и 50 000 итераций.

Но , есть важное улучшение в реализации PKCS#12 — вы можете выбрать алгоритм шифрования! Передавая параметр Защита паролем (начиная с Java 8 и далее) при сохранении ключа, вы можете указать конкретный (основанный на пароле) шифр для использования. Я не проверял, какие именно шифры разрешены, но вы можете, по крайней мере, указать более сильный режим PBE, такой как “pbewithhmacsha512andaes_256”, который выведет 256-битный ключ AES с использованием соленого PBKDF2, а затем зашифрует сохраненный ключ с помощью AES/CBC/PKCS5, используя этот ключ. Вы также можете увеличить количество используемых итераций PBKDF2. Например:

import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.KeyStore.PasswordProtection;
import java.security.KeyStore.SecretKeyEntry;
import java.security.SecureRandom;

import javax.crypto.SecretKey;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class scratch {
    public static void main(String... args) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(null, null); // Initialize a blank keystore

        SecretKey key = new SecretKeySpec(new byte[32], "AES");

        char[] password = "changeit".toCharArray();
        byte[] salt = new byte[20];
        new SecureRandom().nextBytes(salt);
        keyStore.setEntry("test", new SecretKeyEntry(key),
            new PasswordProtection(password,
                "PBEWithHmacSHA512AndAES_128",
                new PBEParameterSpec(salt, 100_000)));

        keyStore.store(new FileOutputStream("/tmp/keystore.p12"), password);
    }
}

Обратите внимание, что, несмотря на включение “HMACSHA512” в вышеуказанный режим PBE, это относится только к получению ключа из пароля. Защита целостности на уровне отдельных записей отсутствует.

Также стоит отметить, что хранилище ключей и индивидуальные пароли ключей должны быть одинаковыми . Я не думаю, что это фундаментальное ограничение PKCS#12 в Java, но, безусловно, стандартные инструменты Java, такие как утилита командной строки “keytool”, не смогут обрабатывать хранилища ключей PKCS#12 с разными паролями, используемыми для хранилища, по сравнению с отдельными ключами. Если вам не нужно использовать эти инструменты, то вы можете использовать разные пароли для каждого ключа.

В отличие от предыдущих записей, формат хранилища ключей PKCS#12 фактически шифрует сертификаты. Он делает это с помощью жестко запрограммированного алгоритма “pbewithsha1andrc2_40”. При этом используется 50 000 раундов соленого PBKDF1 для получения 40-битного ключа для шифрования RC2. RC2 – это старый потоковый шифр, который я бы, конечно, не рекомендовал. 40-битный ключ далеко слишком мал, чтобы обеспечить какую-либо серьезную безопасность. Это заставляет меня задуматься, зачем беспокоиться о применении 50 000 раундов PBKDF1 для защиты пароля при создании ключа, который сам по себе уязвим для грубой силы. Вероятно, на самом деле перебор производного ключа происходит быстрее, чем исходный пароль. Я могу только предположить, что это поддерживает совместимость с каким-то решением, принятым в далеком прошлом, о котором все участники сейчас глубоко сожалеют.

Целостность общего хранилища ключей PKCS#12 защищена с помощью “HMACPBESHA1”. Это HMAC-SHA1, использующий ключ, полученный из пароля хранилища, с использованием 100 000 итераций соленого PBKDF2-HMAC-SHA1. Все это жестко запрограммировано, поэтому не может быть изменено. Это правильный выбор, хотя было бы неплохо иметь возможность использовать здесь что-то другое, кроме SHA-1, поскольку, похоже, PKCS#12 позволяет использовать другие компьютеры MAC. Для использования HMAC SHA1 пока все еще в порядке, но было бы лучше удалить его. Также было бы неплохо иметь возможность настраивать количество итераций.

В целом, хранилище ключей PKCS#12 значительно лучше, чем любой из фирменных вариантов, разработанных Sun. Если вы укажете свои собственные экземпляры защиты паролем с помощью AES и SHA2 и будете использовать большое количество итераций и хорошие случайные соли, то на самом деле это довольно солидный дизайн даже по современным стандартам. Единственная действительно уродливая часть – это 40-битное шифрование доверенных сертификатов RC2, но если вас не волнует конфиденциальность сертификатов, мы можем не обращать внимания на эту деталь и просто считать их слегка запутанными. По крайней мере, использование HMAC-SHA1, наконец, является достойной защитой целостности.

ПККС №11

О PKCS № 11 особо нечего сказать. Это стандартный интерфейс, предназначенный для использования с аппаратными токенами безопасности различных видов: в частности, аппаратными модулями безопасности (HSM). Они варьируются от USB-накопителей за 50 евро до подключенных к сети гигантов, которые стоят десятки или сотни тысяч долларов. Оборудование обычно является запатентованным и закрытым, поэтому трудно точно сказать, как будут храниться ваши ключи. Однако, как правило, существуют значительные средства защиты от доступа к ключам либо от удаленных злоумышленников, либо даже от тех, у кого есть физический доступ к оборудованию и много свободного времени. Это не является гарантией безопасности, так как существует множество способов случайной утечки ключей из оборудования, как продемонстрировала недавняя уязвимость ROCA в оборудовании Infineon . Тем не менее, хорошо протестированный HSM, вероятно, является довольно безопасным вариантом для ключей с высокой стоимостью.

Я не буду вдаваться в подробности о том, как настроить хранилище ключей PKCS#11, так как оно действительно варьируется от поставщика к поставщику. Что касается PKCS#12, то, хотя интерфейс стандартизирован, в этом стандарте есть огромное пространство для изменений. В большинстве случаев вы позволяете HSM генерировать ключи на защищенном оборудовании и никогда не экспортируете материал закрытого ключа (за исключением, возможно, резервного копирования).

Резюме

Используйте хранилище ключей HSM или PKCS#12 и укажите аргументы защиты паролем вручную при хранении ключей. Избегайте фирменных хранилищ ключей.

В качестве альтернативы, передайте управление ключами кому-то другому и используйте систему управления ключами (KMS), такую как Хранилище Hashicorp.

Оригинал: “https://dev.to/neilmadden/java-keystoresthe-gory-details-dnj”