Зашифровать пароль в конфигурационных файлах? [закрытый]

У меня есть программа, которая считывает информацию о сервере из файла конфигурации и хотела бы зашифровать пароль в этой конфигурации, который может быть прочитан моей программой и расшифрован.

требования:

  • зашифровать пароль открытого текста, который будет храниться в файле
  • расшифровать зашифрованный пароль, считанный из файла из моей программы

любые рекомендации о том, как я буду это делать? Я думал написать свой собственный но я чувствую, что это было бы ужасно небезопасно.

10 ответов


простой способ сделать это-использовать шифрование на основе пароля в Java. Это позволяет шифровать и расшифровывать текст с помощью пароля.

это в основном означает инициализацию javax.crypto.Cipher алгоритм "AES/CBC/PKCS5Padding" и получение ключа от javax.crypto.SecretKeyFactory С .

вот пример кода (обновлен, чтобы заменить менее безопасный вариант на основе MD5):

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class ProtectedConfigFile {

    public static void main(String[] args) throws Exception {
        String password = System.getProperty("password");
        if (password == null) {
            throw new IllegalArgumentException("Run with -Dpassword=<password>");
        }

        // The salt (probably) can be stored along with the encrypted data
        byte[] salt = new String("12345678").getBytes();

        // Decreasing this speeds down startup time and can be useful during testing, but it also makes it easier for brute force attackers
        int iterationCount = 40000;
        // Other values give me java.security.InvalidKeyException: Illegal key size or default parameters
        int keyLength = 128;
        SecretKeySpec key = createSecretKey(password.toCharArray(),
                salt, iterationCount, keyLength);

        String originalPassword = "secret";
        System.out.println("Original password: " + originalPassword);
        String encryptedPassword = encrypt(originalPassword, key);
        System.out.println("Encrypted password: " + encryptedPassword);
        String decryptedPassword = decrypt(encryptedPassword, key);
        System.out.println("Decrypted password: " + decryptedPassword);
    }

    private static SecretKeySpec createSecretKey(char[] password, byte[] salt, int iterationCount, int keyLength) throws NoSuchAlgorithmException, InvalidKeySpecException {
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
        PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterationCount, keyLength);
        SecretKey keyTmp = keyFactory.generateSecret(keySpec);
        return new SecretKeySpec(keyTmp.getEncoded(), "AES");
    }

    private static String encrypt(String property, SecretKeySpec key) throws GeneralSecurityException, UnsupportedEncodingException {
        Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        pbeCipher.init(Cipher.ENCRYPT_MODE, key);
        AlgorithmParameters parameters = pbeCipher.getParameters();
        IvParameterSpec ivParameterSpec = parameters.getParameterSpec(IvParameterSpec.class);
        byte[] cryptoText = pbeCipher.doFinal(property.getBytes("UTF-8"));
        byte[] iv = ivParameterSpec.getIV();
        return base64Encode(iv) + ":" + base64Encode(cryptoText);
    }

    private static String base64Encode(byte[] bytes) {
        return Base64.getEncoder().encodeToString(bytes);
    }

    private static String decrypt(String string, SecretKeySpec key) throws GeneralSecurityException, IOException {
        String iv = string.split(":")[0];
        String property = string.split(":")[1];
        Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        pbeCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(base64Decode(iv)));
        return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
    }

    private static byte[] base64Decode(String property) throws IOException {
        return Base64.getDecoder().decode(property);
    }
}

остается одна проблема: где вы должны хранить пароль, который вы используете для зашифровать пароли? Вы можете сохранить его в исходном файле и запутать его, но найти его снова не так сложно. Кроме того, вы можете дать его как системное свойство при запуске процесса Java (-DpropertyProtectionPassword=...).

та же проблема остается, если вы используете хранилище ключей, которое также защищено паролем. В принципе, вам нужно будет иметь один мастер-пароль где-то, и его довольно сложно защитить.


Да, определенно не пишите свой собственный алгоритм. Java имеет много криптографических API.

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


проверить jasypt, которая является библиотекой, предлагающей основные возможности шифрования с минимальными усилиями.


Я думаю, что лучшим подходом является обеспечение того, чтобы ваш файл конфигурации (содержащий ваш пароль) был доступно только для конкретной учетной записи пользователя. Например, у вас может быть конкретный пользователь приложения appuser к которому только доверенные люди имеют пароль (и к которому они su to).

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

EDIT: Я предполагаю, что вы не экспортируйте конфигурацию приложения вне доверенной среды (что, я не уверен, имело бы смысл, учитывая вопрос)


ну, чтобы решить проблемы мастер-пароля-лучший подход не хранить пароль в любом месте, приложение должно шифровать пароли для себя-так что только он может расшифровать их. Так что, если бы я использовал a .конфигурационный файл я бы сделал следующее,mySettings.config:

encryptTheseKeys=секретный ключ, anotherSecret

secretKey=unprotectedPasswordThatIputHere

anotherSecret=anotherPass

someKey=unprotectedSettingIdontCareAbout

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

encryptTheseKeys=секретный ключ, anotherSecret

secretKey=крипта:ii4jfj304fjhfj934fouh938

anotherSecret=крипта: jd48jofh48h

someKey=unprotectedSettingIdontCareAbout

просто убедитесь, что оригиналы хранятся в вашем собственном безопасном месте...


самое главное, и слон в комнате, и все такое, это если ваше приложение может получить пароль, то хакер с доступом к коробке может получить его тоже!

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

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

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


попробуйте использовать методы шифрования ESAPIs. Его легко настроить, и вы также можете легко изменить свои ключи.

http://owasp-esapi-java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/Encryptor.html

вы

1)шифровать 2)расшифровать 3)Знак 4)удалить подпись 5)хеширование 6) подписи на основе времени и многое другое только с одной библиотекой.


посмотрите, что доступно в Jetty для хранения пароля (или хэшей) в файлах конфигурации, и подумайте, может ли кодировка OBF быть полезной для вас. Затем посмотрите в источнике, как это делается.

http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html


в зависимости от того, насколько безопасны вам нужны файлы конфигурации или насколько надежно ваше приложение, http://activemq.apache.org/encrypted-passwords.html может быть хорошим решением для вас.

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


если вы используете в Java 8 использование внутреннего кодера и декодера Base64 можно избежать, заменив

return new BASE64Encoder().encode(bytes);

С

return Base64.getEncoder().encodeToString(bytes);

и

return new BASE64Decoder().decodeBuffer(property);

С

return Base64.getDecoder().decode(property);

обратите внимание, что это решение не защищает ваши данные, поскольку методы дешифрования хранятся в одном месте. Это только затрудняет разрыв. Главным образом оно избегает напечатать его и показать всем по ошибке.