У меня есть распределенное приложение, которое запрашивает команды с центрального сервера, и содержимое этих команд «подписывается» закрытым ключом с открытым ключом, развернутым вместе с приложением на каждом удаленном сайте. Каждая команда подписана с помощью закрытого ключа, и эта подпись проверяется перед выполнением команды.
Вот как я генерирую свои пары открытого и закрытого ключей в Java. (я понимаю, мы должны делать больше 1024)
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA", "SUN");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(1024, random);
KeyPair pair = keyGen.generateKeyPair();
Новое требование заключается в том, что в дополнение к отправке простых относительно безопасных команд мы теперь хотим отправлять команды, которые инструктируют наше программное обеспечение загружать и запускать установщик для обновления. Это может открыть большую дыру, если все сделано неправильно. Команда «выполнить обновление установщика» будет подписана как всегда и будет также включать в себя md5 исполняемого файла, который будет загружен и запущен. Таким образом, подпись в команде должна быть правильной (и будет включать md5), а затем вычисленное значение md5 для загруженного файла должно быть правильным, прежде чем произойдет выполнение. Это должно заботиться о большинстве векторов атаки, о которых я могу думать. Есть ли еще какие-то вопросы, которые меня должны волновать?
Итак, теперь я обращаю свое внимание на защиту закрытого ключа на сервере, где эти команды происходят. Если этот закрытый ключ получен, игра окончена. Как мне защитить этот ключ на диске?
Моя мысль состоит в том, чтобы использовать симметричное шифрование с парольной фразой для защиты этого закрытого ключа.
Мое текущее решение выглядит следующим образом:
char[] passphrase; // Actual passphrase
PrivateKey privateKeyObj; // Actual Private Key
byte[] privateKeyBytes = privateKeyObj.getEncoded();
byte[] salt = new byte[8];
new SecureRandom().nextBytes(salt);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passphrase, salt, 1024, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal(privateKeyBytes);
FileOutputStream outputFileStream = new FileOutputStream(outputFile);
outputFileStream.write(salt);
outputFileStream.write(iv);
outputFileStream.write(ciphertext);
outputFileStream.close();
(исключения исключены для ясности)
Это в основном случайным образом генерирует соль, использует парольную фразу, чтобы придумать ключ, а затем сохраняет полученный соль, IV и зашифрованный текст в файле, готовом для дешифрования.
Однако я чувствую, что здесь чего-то не хватает. Как я должен также как-то включить некоторый тип MAC на ключ, чтобы я знал, что получаю правильное описание? Может быть, это так просто, как поместить 5 или 6 байтов известного текста перед закрытым ключом? Плохая парольная фраза прямо сейчас приводит к исключению плохого заполнения, но я читал, что это не всегда так, и некоторые плохие парольные фразы будут декодироваться и приводить к нежелательной. Я чувствую, что мне нужно остерегаться этого.
Может кто-нибудь сообщить мне, если я на правильном пути здесь, и предоставить некоторую обратную связь. Важно, чтобы я получил это как можно более безопасно ..