回答:
これを行う簡単な方法は、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);
}
}
問題が1つ残っています。パスワードの暗号化に使用するパスワードをどこに保存すればよいですか。ソースファイルに保存して難読化することはできますが、再び見つけるのはそれほど難しくありません。または、Javaプロセスを開始するときに、システムプロパティとして指定することもできます(-DpropertyProtectionPassword=...
)。
パスワードで保護されているKeyStoreを使用する場合も、同じ問題が残ります。基本的には、どこかに1つのマスターパスワードが必要であり、保護するのはかなり困難です。
はい、絶対に独自のアルゴリズムを記述しないでください。Javaにはたくさんの暗号化APIがあります。
インストールするOSにキーストアがある場合、それを使用して、構成やその他のファイルの機密データを暗号化および復号化するために必要な暗号キーを保存できます。
最良の方法は、(パスワードを含む)構成ファイルに特定のユーザーアカウントのみがアクセスできるようにすることです。たとえば、appuser
信頼できる人だけがパスワードを持っている(そしてその人が持っている)アプリケーション固有のユーザーがいるとしますsu
。
そうすれば、煩わしい暗号化のオーバーヘッドがなくなり、安全なパスワードを手に入れることができます。
編集: 私はあなたが信頼された環境の外であなたのアプリケーション設定をエクスポートしていないと仮定しています(質問を考えると、それが意味をなさないかわかりません)
マスターパスワードの問題を解決するには-最善の方法はパスワードをどこにも保存しないことです。アプリケーションはそれ自体のパスワードを暗号化し、それだけがパスワードを復号化できるようにする必要があります。したがって、.configファイルを使用していた場合、mySettings.configを実行します。
encryptTheseKeys = secretKey、anotherSecret
secretKey = unprotectedPasswordThatIputHere
anotherSecret = anotherPass
someKey = unprotectedSettingIdontCareAbout
したがって、encryptTheseKeysに記載されているキーを読み取り、上からBrodwallsの例を適用して、何らかのマーカー(crypt:と言う)を付けてファイルに書き戻し、アプリケーションにそれを行わないように通知します。この場合も、出力は次のようになります。
encryptTheseKeys = secretKey、anotherSecret
secretKey = crypt:ii4jfj304fjhfj934fouh938
anotherSecret = crypt:jd48jofh48h
someKey = unprotectedSettingIdontCareAbout
オリジナルを安全な場所に保管してください...
重要な点、そして部屋の中の象など、アプリケーションがパスワードを入手できる場合、ボックスにアクセスできるハッカーもパスワードを入手できるということです。
これをいくらか回避する唯一の方法は、アプリケーションが標準入力を使用してコンソールで「マスターパスワード」を要求し、これを使用してファイルに保存されているパスワードを復号化することです。もちろん、これにより、起動時にOSと共にアプリケーションを無人で起動することは完全に不可能になります。
ただし、このレベルの煩わしさがあっても、ハッカーがrootアクセス権(またはアプリケーションを実行しているユーザーとしてアクセス権)を取得できた場合、メモリをダンプしてパスワードを見つけることができます。
確認すべきことは、会社全体が本番サーバー(およびパスワード)にアクセスできないようにし、このボックスをクラックできないようにすることです。
ESAPI暗号化メソッドを使用してみてください。設定は簡単で、キーを簡単に変更することもできます。
http://owasp-esapi-java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/Encryptor.html
君は
1)暗号化2)復号化3)署名4)署名解除5)ハッシュ6)時間ベースの署名など、たった1つのライブラリで。
Jettyでパスワード(またはハッシュ)を構成ファイルに格納するために利用できるものを確認し、OBFエンコーディングが役立つかどうかを検討してください。次に、ソースでそれがどのように行われるかを確認します。
http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html
構成ファイルがどれほど安全であるか、またはアプリケーションの信頼性に応じて、http://activemq.apache.org/encrypted-passwords.htmlが適切なソリューションになる場合があります。
解読されるパスワードをそれほど恐れていない場合、Beanを使用してパスワードキーを格納するように構成するのは非常に簡単です。ただし、さらにセキュリティが必要な場合は、シークレットを使用して環境変数を設定し、起動後にそれを削除できます。これにより、アプリケーション/サーバーがダウンすることを心配し、アプリケーションが自動的に再起動しないことを心配する必要があります。
Java 8を使用している場合、内部Base64エンコーダーおよびデコーダーの使用は、
return new BASE64Encoder().encode(bytes);
と
return Base64.getEncoder().encodeToString(bytes);
そして
return new BASE64Decoder().decodeBuffer(property);
と
return Base64.getDecoder().decode(property);
復号化のメソッドは同じ場所に保存されるため、このソリューションではデータが保護されないことに注意してください。壊れるのを難しくするだけです。主にそれを印刷して、誤ってみんなに見せることを避けます。