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


130

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

Вимоги:

  • Зашифруйте пароль простого тексту, який потрібно зберегти у файлі
  • Розшифруйте зашифрований пароль, прочитаний у файлі моєї програми

Будь-які рекомендації щодо того, як я б ішов робити це? Я думав написати власний алгоритм, але відчуваю, що це було б дуже небезпечно.

Відповіді:


172

Простий спосіб зробити це - використовувати шифрування на основі пароля на Java. Це дозволяє зашифрувати та розшифрувати текст за допомогою пароля.

В основному це означає , що ініціалізація javax.crypto.Cipherз допомогою алгоритму "AES/CBC/PKCS5Padding"і отримати ключ від javax.crypto.SecretKeyFactoryз "PBKDF2WithHmacSHA512"алгоритмом.

Ось приклад коду (оновлений для заміни менш безпечного варіанту на основі 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=...).

Ця ж проблема залишається, якщо ви використовуєте KeyStore, який також захищений паролем. В основному, вам потрібно буде мати один головний пароль десь, і це досить важко захистити.


3
Дякую за приклад коду, це майже все, як я закінчив це робити. Що стосується пароля, який захищає паролі, я зіткнувся з тією самою проблемою, я поки що не зациклювався на цьому методі, але поки не знайшов прийнятного рішення, дякую за ваші пропозиції.
Petey B

7
"Крім того, ви можете надати його як системне властивість під час запуску процесу Java (-DpropertyProtectionPassword = ...)". Зауважте, що це дозволить витягнути пароль, використовуючи "ps fax" в (GNU / Linux) / UNIX.
Ztyx

7
@Ben Це звичайна практика кодування до Base64, щоб ви могли зберігати отримане значення у текстовому файлі або стовпці на базі рядків бази даних чи подібних.
РБ.

4
@ V.7 ніп. MD5 абсолютно не безпечний для хешування паролів і ніколи не був розроблений для цієї мети. Ніколи не використовуйте для цього. У наші дні найкращий Argon2. Див owasp.org/index.php/Password_Storage_Cheat_Sheet і paragonie.com/blog/2016/02/how-safely-store-password-in-2016
Kimball Robinson

3
Набагато краще таким чином. Звичайно, безпечна випадкова сіль та кількість ітерацій (консервативний, нижній кінець) 40K було б приємніше, але принаймні ви вказали ці речі в коментарях, а PBKDF2 та AES / CBC - це певні покращення. Я думаю, що чудово, як ви впоралися з цим, оновивши відповідь; Я зніму попередження. Проголосував ваш коментар, щоб люди не здивувались, коли вони знайдуть оновлений код (вони можуть переглянути зміни, щоб знайти старий код, який я думаю). Можливо, буде гарною ідеєю очистити і ваші старі коментарі.
Maarten Bodewes

20

Так, точно не пишіть власний алгоритм. У Java є багато API для криптографії.

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


4
+1 за використання KeyStore! Якщо ви зберігаєте ключ у файлі Jar, це не що інше, як опуднення.
дев'ятисторонній

2
Якщо все, що потрібно, - це не збереження пароля в чистому тексті, то магазини ключів зайві.
Thorbjørn Ravn Andersen

20

Ознайомтеся з Jasypt - бібліотекою, що пропонує основні можливості шифрування з мінімальними зусиллями.


16

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

Таким чином, криптовалюти над головою немає дратівливої ​​криптовалюти, і ви все одно маєте захищений пароль.

EDIT: Я припускаю, що ви не експортуєте конфігурацію програми за межами надійного середовища (що, я не впевнений, має сенс для цього)


4

Ну а щоб вирішити проблеми з основним паролем - найкращий підхід - не зберігати пароль ніде, програма повинна зашифрувати паролі для себе - щоб тільки він міг їх розшифрувати. Отже, якщо я використовував файл .config, я би зробив наступне, mySettings.config :

encryptTheseKeys = secretKey, anotherSecret

secretKey = незахищенийPasswordThatIputHere

anotherSecret = іншийPass

someKey = незахищенийSettingIdontCare About

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

encryptTheseKeys = secretKey, anotherSecret

secretKey = склеп: ii4jfj304fjhfj934fouh938

AnotherSecret = склеп: jd48jofh48h

someKey = незахищенийSettingIdontCare About

Просто переконайтеся, що зберігаєте оригінали у власному безпечному місці ...


2
Так, це з 3 років тому. Щоб уникнути головного ключа, я в кінцевому рахунку використовував RSA-ключі, видані з нашого внутрішнього офісу. Доступ до приватного ключа захищається за допомогою шифрування за допомогою відбитків пальців апаратних засобів машини.
Petey B

Я бачу, це звучить досить солідно. приємно.
користувач1007231

@ user1007231 - Де зберігати - "Просто переконайтеся, що зберігати оригінали у власному безпечному місці ..."?
nanosoft

@PeteyB - Не зрозумів? Чи можете ви вказати мені на деякі посилання, які можуть мене просвітити. Спасибі
nanosoft

@nanosoft - придбайте "Aegis Secure Key USB" і зберігайте там текстовий документ там або на папері у своєму гаманці
user1007231

4

Важливим моментом є і слон у кімнаті, і все, що якщо ваша програма може отримати пароль, то хакер, що має доступ до скриньки, може отримати і його!

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

Однак навіть при такому рівні роздратування, якщо хакеру вдасться отримати кореневий доступ (або навіть просто доступ, як користувач, що запускає вашу програму), він може скинути пам'ять і знайти там пароль.

Що потрібно забезпечити - це не дозволяти всій компанії мати доступ до виробничого сервера (і, тим самим, до паролів), і переконатися, що зламати цю скриньку неможливо!


Справжнє рішення - зберігати свій приватний ключ десь в іншому місці, як-от картка або HSM: en.wikipedia.org/wiki/Hardware_security_module
atom88

1

Спробуйте скористатися методами шифрування ESAPI. Його легко налаштувати, і ви також можете легко змінити свої клавіші.

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

ти

1) шифрувати 2) розшифрувати 3) знак 4) відписати 5) хешування 6) підписи на основі часу та багато іншого лише за допомогою однієї бібліотеки.



0

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

Якщо ви не надто боїтеся розшифровки пароля, і це може бути дуже просто налаштувати за допомогою ключа bean для зберігання ключа пароля. Однак, якщо вам потрібен додатковий захист, ви можете встановити змінну середовища з секретом і видалити її після запуску. З цим вам доведеться турбуватися про те, що програма / сервер вийде з ладу, а не програма автоматично не перезапуститься.


використання HSM - ідеальний спосіб: en.wikipedia.org/wiki/Hardware_security_module
atom88

-8

Якщо ви використовуєте java 8, використання внутрішнього кодера Base64 та декодера можна уникнути, замінивши

return new BASE64Encoder().encode(bytes);

з

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

і

return new BASE64Decoder().decodeBuffer(property);

з

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

Зауважте, що це рішення не захищає ваші дані, оскільки методи дешифрування зберігаються в одному місці. Це просто ускладнює розрив. Головним чином, він уникає друку та показує його помилково.


26
Base64 - це не шифрування.
jwilleke

1
Base64 - це не шифрування, і це найгірший приклад, який ви можете надати ... багато людей вважають, що base64 - це алгоритм шифрування, тому краще не плутати їх ...
робоб

зауважте, що метод decode () у верхній частині вихідного файлу робить фактичне шифрування. Однак це кодування та декодування base64 потрібно для перетворення з рядка байтів, щоб передати цій функції те, що вона може використовувати (байт масиву байтів [])
atom88
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.