구성 파일에서 비밀번호를 암호화 하시겠습니까? [닫은]


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 = ...)를 시작할 때 시스템 특성으로 제공 할 수 있습니다." (GNU / Linux) / UNIX에서 "ps fax"를 사용하여 비밀번호를 추출 할 수 있습니다.
Ztyx

7
@Ben 결과 값을 텍스트 파일 또는 문자열 기반 데이터베이스 열 등으로 저장할 수 있도록 Base64로 인코딩하는 것이 일반적입니다.
RB.

4
@ V.7 아니. MD5는 암호 해싱에 대해 절대 안전하지 않으며 해당 목적으로 설계되지 않았습니다. 그것을 위해 그것을 사용하지 마십시오. 요즘에는 Argon2가 가장 좋습니다. 참조 owasp.org/index.php/Password_Storage_Cheat_Sheetparagonie.com/blog/2016/02/how-safely-store-password-in-2016
킴볼 로빈슨

3
이 방법이 훨씬 좋습니다. 물론 안전한 무작위 소금과 40K의 (보존 적, 저가) 반복 횟수는 더 좋을 것입니다. 그러나 적어도 당신은 의견에 이러한 것들을 표시했으며 PBKDF2와 AES / CBC는 확실히 개선되었습니다. 답변을 업데이트하여 어떻게 처리했는지가 훌륭하다고 생각합니다. 경고를 제거하겠습니다. 사람들이 업데이트 된 코드를 발견하는 것에 놀라지 않도록 의견을 표명했습니다 (이전 코드를 편집하기 위해 편집 내용을 볼 수 있음). 이전 의견도 정리하는 것이 좋습니다.
Maarten Bodewes

20

예, 확실히 자신의 알고리즘을 작성하지 마십시오. Java에는 많은 암호화 API가 있습니다.

설치하려는 OS에 키 저장소가있는 경우이를 사용하여 구성 또는 기타 파일에서 중요한 데이터를 암호화하고 암호 해독해야하는 암호화 키를 저장할 수 있습니다.


4
KeyStore를 사용하여 +1! Jar 파일에 키를 저장하면 난독 화에 지나지 않습니다.
ninesided

2
필요한 모든 암호를 일반 텍스트로 저장하지 않으면 키 저장소가 과도하게 사용됩니다.
Thorbjørn Ravn Andersen 님이

20

최소한의 노력으로 기본 암호화 기능을 제공하는 라이브러리 인 jasypt를 확인하십시오 .


16

최선의 접근 방식은 구성 파일 (비밀번호 포함)이 특정 사용자 계정에서만 액세스 할 수 있도록하는 것 입니다. 예를 들어, appuser신뢰할 수있는 사람 만 암호를 가진 응용 프로그램 특정 사용자 가있을 수 있습니다.su .

그렇게하면 성가신 암호화 오버 헤드가 없으며 여전히 안전한 암호가 있습니다.

편집 : 신뢰할 수있는 환경 외부에서 응용 프로그램 구성을 내 보내지 않는다고 가정합니다 (질문이 주어지면 확실하지 않습니다)


4

마스터 비밀번호의 문제를 해결하는 것이 가장 좋습니다. 가장 좋은 방법은 비밀번호를 아무 곳에 나 저장하지 않는 것입니다. 애플리케이션은 비밀번호 자체를 암호화하여 비밀번호 만 해독 할 수 있습니다. 따라서 .config 파일을 사용하는 경우 다음 mySettings.config를 수행합니다 .

encryptTheseKeys = secretKey, 다른 비밀

secretKey = 비보호 암호 ThatIput 여기

anotherSecret = anotherPass

someKey = unprotectedSettingIdontCare 정보

그래서 encryptTheseKeys에 언급 된 키를 읽고 위에서 Brodwalls 예제를 적용하고 일종의 마커 ( crypt 라고 말 하십시오 )로 파일에 다시 써서 응용 프로그램이하지 말라고 알리십시오. 다시 출력은 다음과 같습니다.

encryptTheseKeys = secretKey, 다른 비밀

secretKey = 암호 : ii4jfj304fjhfj934fouh938

anotherSecret = 암호 : jd48jofh48h

someKey = unprotectedSettingIdontCare 정보

원본을 안전한 곳에 보관하십시오 ...


2
예, 이것은 3 년 전입니다. 마스터 키를 피하기 위해 내부 CA에서 발급 한 RSA 키를 사용했습니다. 개인 키에 대한 액세스는 기계 하드웨어의 지문으로 암호화되어 보호됩니다.
Petey B

나는 꽤 견고하게 들린다. 좋은.
user1007231

@ user1007231-보관할 곳- "원본을 안전한 곳에 보관하십시오 ..."?
nanosoft

@PeteyB-이해 못하셨습니까? 나를 밝힐 수있는 몇 가지 링크를 알려 주시겠습니까? 감사합니다
nanosoft

@nanosoft- "Aegis Secure Key USB"를 구하여 텍스트 문서 나 지갑에 종이에 저장하십시오
user1007231 2016 년

4

가장 큰 요점과 방 안의 코끼리는 응용 프로그램이 암호를 파악할 수 있으면 상자에 액세스 할 수있는 해커도 암호를 얻을 수 있다는 것입니다!

이 문제를 해결하는 유일한 방법은 응용 프로그램이 표준 입력을 사용하여 콘솔에서 "마스터 암호"를 요청한 다음이를 사용하여 파일에 저장된 암호를 해독하는 것입니다. 물론, 부팅 할 때 OS와 함께 응용 프로그램을 자동으로 시작하는 것은 불가능합니다.

그러나 이러한 수준의 성가심에도 불구하고 해커가 루트 액세스 권한을 얻거나 응용 프로그램을 실행하는 사용자로 액세스 할 수 있다면 메모리를 덤프하고 거기에서 암호를 찾을 수 있습니다.

보장해야 할 것은 회사 전체가 프로덕션 서버에 액세스하여 암호에 액세스하지 못하게하고이 상자를 해독 할 수 없도록하는 것입니다!


실제 솔루션은 카드 또는 HSM과 같은 다른 곳에 개인 키를 저장하는 것입니다. en.wikipedia.org/wiki/Hardware_security_module
atom88



0

구성 파일이 필요한 보안 또는 응용 프로그램의 안정성에 따라 http://activemq.apache.org/encrypted-passwords.html 이 좋은 해결책이 될 수 있습니다.

암호 해독을 너무 두려워하지 않고 Bean을 사용하여 암호 키를 저장하는 것이 실제로 간단 할 수 있습니다. 그러나 더 많은 보안이 필요한 경우 비밀로 환경 변수를 설정하고 시작 후 제거 할 수 있습니다. 이를 통해 응용 프로그램 / 서버가 다운되고 응용 프로그램이 자동으로 다시 시작되지 않는 것에 대해 걱정해야합니다.


HSM을 사용하는 것이 이상적인 방법입니다. en.wikipedia.org/wiki/Hardware_security_module
atom88

-8

당신이 사용하는 경우 자바 (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 2016 년

1
Base64는 암호화가 아니며 제공 할 수있는 최악의 사례입니다. 많은 사람들이 base64가 암호화 알고리즘이라고 믿기 때문에 혼동하지 않는 것이 좋습니다 ...
robob

소스 파일 상단의 decode () 메소드가 실제 암호화를 수행합니다. 그러나이 base64 인코딩과 디코딩은이 함수를 사용할 수있는 (바이트 배열 바이트 [])를 전달하기 위해 문자열에서 바이트를 변환하는 데 필요합니다.
atom88
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.