Java の構成ファイルでパスワードを暗号化する

Rashmi Patidar 2023年10月12日
Java の構成ファイルでパスワードを暗号化する

暗号化は、暗号化キーと呼ばれるパラメータと組み合わせた暗号化アルゴリズムを使用して、プレーンテキスト情報を読み取り不可能な形式に変換するプロセスです。判読できない形式は、暗号文形式としてよく知られています。復号化キーを持っている人だけがデータを復号化し、元の平文を復元できます。

構成ファイル内のパスワードの暗号化の問題は、次の 2つのサブタスクに分けることができます。

  1. ファイルにあるプレーンテキストのパスワードを暗号化します。
  2. ファイルから読み込まれた暗号化されたパスワードを復号化します。

まず、src/conf/パスに config.properties ファイルという名前の構成ファイルを作成しましょう。

password = TestPassword123

次に、構成ファイルを読み取るために、Properties クラスをインスタンス化します。コンストラクターを使用して、FileInputStream クラスのインスタンスを作成できます。構成ファイルのパスを入力として受け取ります。これで、プロパティクラスのインスタンスを使用してプロパティをロードします。クラスにプロパティファイルをロードするには、load メソッドを使用します。これにより、InputStreamReader インスタンスがパラメータとして取得されます。この入力ストリームに不正な形式の Unicode エスケープシーケンスが含まれている場合は IllegalArgumentException がスローされ、入力ストリームからの読み取り中にエラーが発生した場合は IOException がスローされます。

プロパティが正常に読み込まれたら、getProperty() メソッドを使用して、プロパティリストで指定されたキーを持つプロパティを検索します。プロパティが見つからない場合、メソッドは null を返します。このような状況を処理するために外部チェックを行い、ファイルからパスワードが null であることが判明した場合は、IllegalArgumentException をスローします。

salt は、パスワード文字列に追加する任意のランダムな文字列で作成されます。

createSecretKeySecretKeySpec キーを返すユーザー定義のメソッドであり、キーの使用はパスワードを暗号化および復号化することです。encrypt メソッドと decrypt メソッドが使用されます-Encryption クラスで指定された定義済みの static メソッド。

以下は、同じことを示すサンプルコードです。

package fileDataEncryption;

import static fileDataEncryption.Encryption.*;

import java.io.FileInputStream;
import java.util.Properties;
import javax.crypto.spec.SecretKeySpec;

public class ConfigFileEncryption {
  public static void main(String[] args) throws Exception {
    Properties properties = new Properties();
    FileInputStream inputStream = new FileInputStream("src/conf/config.properties");
    properties.load(inputStream);
    String password = properties.getProperty("password");

    if (password == null) {
      throw new IllegalArgumentException("No such parameter present in config file");
    }

    byte[] salt = new String("12345678").getBytes();
    int iterationCount = 40000;
    int keyLength = 128;
    SecretKeySpec key = createSecretKey(password.toCharArray(), salt, iterationCount, keyLength);

    String originalPassword = password;
    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);
  }
}

Encryption クラスのユーザー定義メソッドの詳細な説明は次のとおりです。

  1. createSecretKey は、passwordsaltiterationCountkeyLength などのパラメーターを受け取る関数です。password は、構成ファイル内の実際のパスワードです。暗号化では、salt は、データ、パスワード、またはパスフレーズをハッシュする追加入力として使用するランダムデータです。salts の使用は、ストレージ内のパスワードを保護することです。アルゴリズムが実行する必要のある反復回数として、iterationCount 変数を使用します。変数の値を小さくすると、起動時間が短縮されるため、テスト中に役立ちますが、ブルートフォース攻撃者にとっても簡単になります。keyLength 変数は、最終的に導出する必要のあるキーの長さです。使用したメソッドからスローされた例外をスローします。
  2. getInstance メソッドは、最も優先されるプロバイダーから始めて、登録されたセキュリティプロバイダーのリストをトラバースします。要求された秘密鍵アルゴリズムの標準名を受け取り、新しい SecretKeyFactory オブジェクトを返します。指定されたアルゴリズムが null の場合は NullPointerException をスローし、指定されたアルゴリズムの SecretKeyFactorySpi 実装をサポートするプロバイダーがない場合は NoSuchAlgorithmException をスローします。
  3. PBEKeySpec は、可変キーサイズの PBE 暗号の PBEKey を生成するために、パスワード、ソルト、反復回数、および派生キーの長さを受け取るクラスコンストラクターです。saltnull の場合は NullPointerException をスローし、salt が空の場合は IllegalArgumentException をスローします。
  4. generateSecret は、提供されたキー仕様またはキーマテリアルから SecretKey オブジェクトを生成します。秘密鍵の指定が必要です。指定された仕様がこの秘密鍵ファクトリが分類された鍵値を生成するのに不適切な場合、InvalidKeySpecException をスローします。

Encryption クラスの encrypt メソッドの詳細。

  1. encrypt メソッドは、暗号化するデータとキーの 2つのパラメーターを取ります。このメソッドは、その中の子メソッドからスローされた例外をスローします。
  2. getInstance メソッドは、最も優先されるプロバイダーから始めて、登録されたセキュリティプロバイダーのリストをトラバースします。これは、変換の名前、つまり AES / CBC / PKCS5Padding を取ります。変更が null、空、無効な形式の場合は NoSuchAlgorithmException をスローし、変更に使用できないパディングスキームが含まれている場合は NoSuchPaddingException をスローします。
  3. init メソッドは、操作モードの値に応じて、暗号化、復号化、キーの折り返し、またはキーのアンラップの 4つの操作のいずれかのために暗号を初期化します。この場合、ENCRYPT_MODE。このメソッドは、操作モードが無効な場合は UnsupportedOperationException をスローし、指定されたキーが不適切な場合は InvalidKeyException をスローします。
  4. getParameters は、この暗号で使用されるパラメーターを返します。
  5. getParameterSpec は、パラメータオブジェクトの仕様を返します。paramSpec パラメータは、パラメータが返す必要のある仕様クラスを識別します。たとえば、パラメータが DSAParameterSpec クラスのインスタンスで返される必要があることを示すのは、DSAParameterSpec.class である可能性があります。
  6. doFinal メソッドは、単一部分の作業でデータを暗号化または復号化するか、複数部分の操作を終了します。データは、暗号の初期化方法に応じて、暗号化または復号化されます。
  7. base64Encode は、Base64 エンコードスキームを使用して、指定されたバイト配列を文字列にエンコードするプライベートメソッドです。decrypt メソッドで使用される関数は、上記のメソッドと同様です。唯一の違いは、操作モードとして関数 DECRYPT_MODE で指定されたモードに基づいて動作が異なることです。
package fileDataEncryption;

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 Encryption {
  public 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");
  }

  public static String encrypt(String dataToEncrypt, 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(dataToEncrypt.getBytes("UTF-8"));
    byte[] iv = ivParameterSpec.getIV();
    return base64Encode(iv) + ":" + base64Encode(cryptoText);
  }

  private static String base64Encode(byte[] bytes) {
    return Base64.getEncoder().encodeToString(bytes);
  }

  public 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);
  }
}

以下は、構成ファイル内のパスワードを暗号化および復号化するために記述されたコードの出力です。

Original password: TestPassword123
Encrypted password: Hy7fbIwpyKgp0oileu+oLg==:WNRknMJz/8u8GmWlCZFPFA==
Decrypted password: TestPassword123
著者: Rashmi Patidar
Rashmi Patidar avatar Rashmi Patidar avatar

Rashmi is a professional Software Developer with hands on over varied tech stack. She has been working on Java, Springboot, Microservices, Typescript, MySQL, Graphql and more. She loves to spread knowledge via her writings. She is keen taking up new things and adopt in her career.

LinkedIn

関連記事 - Java Encryption