1. 概要

このチュートリアルでは、既存のJDKAPIを使用してファイルを暗号化および復号化する方法を見ていきます。

2. 最初にテストを書く

まず、テスト、TDDスタイルを作成します。 ここではファイルを処理するので、統合テストが適切であるように思われます。

既存のJDK機能を使用しているだけなので、外部の依存関係は必要ありません。

まず、新しく生成された秘密鍵を使用してコンテンツを暗号化します(この例では、対称暗号化アルゴリズムとしてAES、 Advanced Encryption Standard を使用しています)。

また、使用される暗号化、ブロック暗号モード、およびパディング(アルゴリズム/モード)を連結したコンストラクター( AES / CBC / PKCS5Padding )で完全な変換文字列を定義していることにも注意してください。 / padding )。 JDKの実装は、デフォルトでさまざまな変換をサポートしていますが、今日の標準では、すべての組み合わせが暗号化された安全性を備えているとは限らないことに注意してください。

FileEncrypterDecrypterクラスが出力をbaz.encというファイルに書き込むと想定します。 その後、同じ秘密鍵を使用してこのファイルを復号化し、復号化されたコンテンツが元のコンテンツと等しいことを確認します。

@Test
public void whenEncryptingIntoFile_andDecryptingFileAgain_thenOriginalStringIsReturned() {
    String originalContent = "foobar";
    SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey();

    FileEncrypterDecrypter fileEncrypterDecrypter
      = new FileEncrypterDecrypter(secretKey, "AES/CBC/PKCS5Padding");
    fileEncrypterDecrypter.encrypt(originalContent, "baz.enc");

    String decryptedContent = fileEncrypterDecrypter.decrypt("baz.enc");
    assertThat(decryptedContent, is(originalContent));

    new File("baz.enc").delete(); // cleanup
}

3. 暗号化

指定された変換String。を使用して、FileEncrypterDecrypterクラスのコンストラクターで暗号を初期化します。

これにより、間違った変換が指定された場合に早期に失敗することができます。

FileEncrypterDecrypter(SecretKey secretKey, String transformation) {
    this.secretKey = secretKey;
    this.cipher = Cipher.getInstance(transformation);
}

次に、インスタンス化された暗号と提供された秘密鍵を使用して暗号化を実行できます:

void encrypt(String content, String fileName) {
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    byte[] iv = cipher.getIV();

    try (FileOutputStream fileOut = new FileOutputStream(fileName);
      CipherOutputStream cipherOut = new CipherOutputStream(fileOut, cipher)) {
        fileOut.write(iv);
        cipherOut.write(content.getBytes());
    }
}

Javaを使用すると、便利なCipherOutputStreamクラスを利用して、暗号化されたコンテンツを別のOutputStreamに書き込むことができます。

IV( Initialization Vector )を出力ファイルの先頭に書き込んでいることに注意してください。 この例では、Cipherを初期化するときにIVが自動的に生成されます。

CBCモードを使用する場合、暗号化された出力をランダム化するために、IVの使用は必須です。 ただし、IVは秘密とは見なされないため、ファイルの先頭に書き込んでもかまいません。

4. 復号化

復号化についても、同様に最初にIVを読み取る必要があります。 その後、暗号を初期化し、コンテンツを復号化できます。

ここでも、実際の復号化を透過的に処理する特別なJavaクラスCipherInputStreamを利用できます。

String decrypt(String fileName) {
    String content;

    try (FileInputStream fileIn = new FileInputStream(fileName)) {
        byte[] fileIv = new byte[16];
        fileIn.read(fileIv);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(fileIv));

        try (
                CipherInputStream cipherIn = new CipherInputStream(fileIn, cipher);
                InputStreamReader inputReader = new InputStreamReader(cipherIn);
                BufferedReader reader = new BufferedReader(inputReader)
            ) {

            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            content = sb.toString();
        }

    }
    return content;
}

5. 結論

Cipher CipherOutputStream CipherInputStreamなどの標準のJDKクラスを使用して基本的な暗号化と復号化を実行できることを確認しました。

いつものように、この記事の完全なコードは、GitHubリポジトリで入手できます。

さらに、JDKここで利用可能な暗号のリストを見つけることができます。

最後に、ここでのコード例は製品グレードのコードを意図したものではなく、システムの詳細を使用する際には十分に考慮する必要があることに注意してください。