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

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

1. 概要

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

2. 最初にテストを書く

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

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

まず、we’ll encrypt the content using a newly generated secret key(この例では、対称暗号化アルゴリズムとしてAES、Advanced Encryption Standardを使用しています)。

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

our FileEncrypterDecrypter class will write the output to a file called baz.encと仮定します。 その後、we decrypt this file using the same secret keyを実行し、復号化されたコンテンツが元のコンテンツと等しいことを確認します。

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

その後、use the instantiated cipher and the provided secret key to perform the encryption:

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ではleverage the convenient CipherOutputStream class for writing the encrypted content into another OutputStreamが可能です。

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

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

4. 復号化

解読するには、同様にIVを最初に読む必要があります。 その後、暗号を初期化し、コンテンツを復号化できます。

ここでも、特別なJavaクラスCipherInputStream, which transparently takes care of the actual decryptionを利用できます。

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. 結論

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

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

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

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