構成ファイルからサーバー情報を読み取るプログラムがあり、その構成でパスワードを暗号化して、プログラムで読み取って復号化できるようにします。
要件:
私はこれをどうやってやるのでしょうか?私は自分のアルゴリズムを書くことを考えていましたが、それはひどく安全ではないと感じます。
これを行う簡単な方法は、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=...
)を開始するときにシステムプロパティとして指定することもできます。
パスワードで保護されているキーストアを使用する場合も、同じ問題が残ります。基本的に、どこかにマスターパスワードが1つ必要であり、保護するのはかなり困難です。
はい、独自のアルゴリズムを作成しないでください。 Javaには多くの暗号化APIがあります。
インストールするOSにキーストアがある場合は、それを使用して、構成またはその他のファイルの機密データを暗号化および復号化する必要がある暗号キーを格納できます。
jasypt を確認してください。これは、最小限の労力で基本的な暗号化機能を提供するライブラリです。
最善の方法は、設定ファイル(パスワードを含む)が特定のユーザーアカウントのみにアクセスできるようにすることだと思います。たとえば、アプリケーション固有のユーザー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とともにアプリケーションを無人で起動することは完全に不可能になります。
ただし、このレベルの迷惑でも、ハッカーがルートアクセス(またはアプリケーションを実行しているユーザーとしてのアクセス)を取得した場合、メモリをダンプしてパスワードを見つけることができます。
確保することは、会社全体が本番サーバー(およびパスワード)にアクセスできないようにし、このボックスを解読できないようにすることです!
パスワード(またはハッシュ)を構成ファイルに保存するためにJettyで利用できるものを確認し、OBFエンコーディングが役立つかどうかを検討してください。次に、ソースでそれがどのように行われるかを確認します。
http://www.Eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html
ESAPI暗号化方式を使用してみてください。設定が簡単で、キーを簡単に変更することもできます。
http://owasp-esapi-Java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/Encryptor.html
君は
1)暗号化2)復号化3)署名4)署名解除5)ハッシュ6)時間ベースの署名など、たった1つのライブラリで。
構成ファイルの安全性やアプリケーションの信頼性に応じて、 http://activemq.Apache.org/encrypted-passwords.html が適切なソリューションになる場合があります。
パスワードが復号化されることを恐れていない場合、Beanを使用してパスワードキーを保存することは非常に簡単です。ただし、さらにセキュリティが必要な場合は、シークレットを使用して環境変数を設定し、起動後に削除できます。これにより、アプリケーション/サーバーがダウンするのではなく、アプリケーションが自動的に再起動しないことを心配する必要があります。