Javaでのファイルの暗号化と復号化

1.概要

このチュートリアルでは、既存のJDK APIを使ってファイルを暗号化および復号化する方法について説明します。

2.最初にテストを書く

TDDスタイルのテストを書くことから始めましょう。ここではファイルを使って作業するので、統合テストが適切であると思われます。

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

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

また、コンストラクタ( AES/CBC/PKCS5Padding )で完全な変換文字列を定義しています。これは、使用されている暗号化、ブロック暗号モード、およびパディング( algorithm/mode/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では、暗号化されたコンテンツを別の OutputStream ** に書き込むために、便利な CipherOutputStream クラスを利用できます。

出力ファイルの先頭にIV(https://en.wikipedia.org/wiki/Initialization vector[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クラスを使用して基本的な暗号化と復号化を実行できることがわかりました。

いつものように、この記事の完全なコードはhttps://github.com/eugenp/tutorials/tree/master/core-java[GitHub repository]にあります。

さらに、JDK ここ に入手可能な暗号のリストもあります。

最後に、ここに記載したコード例は製品レベルのコードを意味するものではなく、使用する際にはシステムの詳細を十分に考慮する必要があります。