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


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_Sheet 및 paragonie.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.