답변:
이를 수행하는 간단한 방법은 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를 사용하는 경우에도 동일한 문제가 남아 있습니다. 기본적으로 어딘가에 하나의 마스터 암호가 있어야하며 보호하기가 어렵습니다.
예, 확실히 자신의 알고리즘을 작성하지 마십시오. Java에는 많은 암호화 API가 있습니다.
설치하려는 OS에 키 저장소가있는 경우이를 사용하여 구성 또는 기타 파일에서 중요한 데이터를 암호화하고 암호 해독해야하는 암호화 키를 저장할 수 있습니다.
최선의 접근 방식은 구성 파일 (비밀번호 포함)이 특정 사용자 계정에서만 액세스 할 수 있도록하는 것 입니다. 예를 들어, appuser
신뢰할 수있는 사람 만 암호를 가진 응용 프로그램 특정 사용자 가있을 수 있습니다.su
.
그렇게하면 성가신 암호화 오버 헤드가 없으며 여전히 안전한 암호가 있습니다.
편집 : 신뢰할 수있는 환경 외부에서 응용 프로그램 구성을 내 보내지 않는다고 가정합니다 (질문이 주어지면 확실하지 않습니다)
마스터 비밀번호의 문제를 해결하는 것이 가장 좋습니다. 가장 좋은 방법은 비밀번호를 아무 곳에 나 저장하지 않는 것입니다. 애플리케이션은 비밀번호 자체를 암호화하여 비밀번호 만 해독 할 수 있습니다. 따라서 .config 파일을 사용하는 경우 다음 mySettings.config를 수행합니다 .
encryptTheseKeys = secretKey, 다른 비밀
secretKey = 비보호 암호 ThatIput 여기
anotherSecret = anotherPass
someKey = unprotectedSettingIdontCare 정보
그래서 encryptTheseKeys에 언급 된 키를 읽고 위에서 Brodwalls 예제를 적용하고 일종의 마커 ( crypt 라고 말 하십시오 )로 파일에 다시 써서 응용 프로그램이하지 말라고 알리십시오. 다시 출력은 다음과 같습니다.
encryptTheseKeys = secretKey, 다른 비밀
secretKey = 암호 : ii4jfj304fjhfj934fouh938
anotherSecret = 암호 : jd48jofh48h
someKey = unprotectedSettingIdontCare 정보
원본을 안전한 곳에 보관하십시오 ...
가장 큰 요점과 방 안의 코끼리는 응용 프로그램이 암호를 파악할 수 있으면 상자에 액세스 할 수있는 해커도 암호를 얻을 수 있다는 것입니다!
이 문제를 해결하는 유일한 방법은 응용 프로그램이 표준 입력을 사용하여 콘솔에서 "마스터 암호"를 요청한 다음이를 사용하여 파일에 저장된 암호를 해독하는 것입니다. 물론, 부팅 할 때 OS와 함께 응용 프로그램을 자동으로 시작하는 것은 불가능합니다.
그러나 이러한 수준의 성가심에도 불구하고 해커가 루트 액세스 권한을 얻거나 응용 프로그램을 실행하는 사용자로 액세스 할 수 있다면 메모리를 덤프하고 거기에서 암호를 찾을 수 있습니다.
보장해야 할 것은 회사 전체가 프로덕션 서버에 액세스하여 암호에 액세스하지 못하게하고이 상자를 해독 할 수 없도록하는 것입니다!
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 이 좋은 해결책이 될 수 있습니다.
암호 해독을 너무 두려워하지 않고 Bean을 사용하여 암호 키를 저장하는 것이 실제로 간단 할 수 있습니다. 그러나 더 많은 보안이 필요한 경우 비밀로 환경 변수를 설정하고 시작 후 제거 할 수 있습니다. 이를 통해 응용 프로그램 / 서버가 다운되고 응용 프로그램이 자동으로 다시 시작되지 않는 것에 대해 걱정해야합니다.
당신이 사용하는 경우 자바 (8) 내부 Base64로 인코더 및 디코더의 사용은 대체 방지 할 수 있습니다
return new BASE64Encoder().encode(bytes);
와
return Base64.getEncoder().encodeToString(bytes);
과
return new BASE64Decoder().decodeBuffer(property);
와
return Base64.getDecoder().decode(property);
암호 해독 방법이 동일한 위치에 저장되므로이 솔루션은 데이터를 보호하지 않습니다. 깨지기가 더 어려워집니다. 주로 인쇄하지 않고 실수로 모든 사람에게 보여줍니다.