1. 概要

簡単に言うと、暗号化とは、許可されたユーザーだけがメッセージを理解またはアクセスできるようにメッセージをエンコードするプロセスです。

plaintext と呼ばれるメッセージは、暗号化アルゴリズム( cipher )を使用して暗号化され、許可されたユーザーのみが復号化によって読み取ることができるciphertextを生成します。

この記事では、Javaで暗号化および復号化機能を提供するコアCipherクラスについて詳しく説明します。

2. 暗号クラス

Java Cryptography Extension(JCE)は、Java Cryptography Architecture(JCA)部分であり、データの暗号化と復号化、およびプライベートデータのハッシュのための暗号化暗号をアプリケーションに提供します。

Cipher クラス( javax.crypto パッケージにあります)は、JCEフレームワークのコアを形成し、暗号化と復号化の機能を提供します。

2.1. 暗号のインスタンス化

Cipher オブジェクトをインスタンス化するために、静的getInstanceメソッドを呼び出し、要求された変換の名前を渡します。 オプションで、プロバイダーの名前を指定できます。

Cipherのインスタンス化を示すサンプルクラスを書いてみましょう。

public class Encryptor {

    public byte[] encryptMessage(byte[] message, byte[] keyBytes) 
      throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        //...
    }
}

変換AES/ ECB / PKCS5Padding は、getInstanceメソッドにCipherオブジェクトをAES暗号としてECBモードでインスタンス化するように指示します操作およびPKCS5パディングスキームの。

変換でアルゴリズムのみを指定することにより、Cipherオブジェクトをインスタンス化することもできます。

Cipher cipher = Cipher.getInstance("AES");

この場合、Javaはモードとパディングスキームにプロバイダー固有のデフォルト値を使用します。

変換がnull、空、または無効な形式の場合、またはプロバイダーがサポートしていない場合、getInstanceNoSuchAlgorithmExceptionをスローすることに注意してください。

変換にサポートされていないパディングスキームが含まれている場合、NoSuchPaddingExceptionがスローされます。

2.2. スレッドセーフ

Cipher クラスは、内部同期の形式がないステートフルクラスです。 実際のところ、 init() update()のようなメソッドは、特定のCipherインスタンスの内部状態を変更します。

したがって、Cipherクラスはスレッドセーフではありません。 だから私たちはそれを作成する必要があります暗号暗号化/復号化の必要性ごとのインスタンス。

2.3. キー

Key interfaceは、暗号化操作のキーを表します。 キーは、エンコードされたキー、キーのエンコード形式、およびその暗号化アルゴリズムを保持する不透明なコンテナです。

キーは通常、キージェネレータ、証明書、またはキーファクトリを使用したキー仕様から取得されます。

提供されたキーバイトから対称キーを作成しましょう。

SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");

2.4. 暗号の初期化

init()メソッドを呼び出して、暗号オブジェクトKeyまたはCertificateopmodeで初期化します。暗号。

オプションで、ランダム性のソースを渡すことができます。 デフォルトでは、最も優先度の高いインストール済みプロバイダーのSecureRandom実装が使用されます。 それ以外の場合は、システムが提供するソースを使用します。

オプションでアルゴリズム固有のパラメーターのセットを指定できます。たとえば、IvParameterSpec初期化ベクトルを指定するために渡すことができます。

使用可能な暗号操作モードは次のとおりです。

  • ENCRYPT_MODE cipherオブジェクトを暗号化モードに初期化します
  • DECRYPT_MODE cipherオブジェクトを復号化モードに初期化します
  • WRAP_MODE 暗号オブジェクトをキーラッピングモードに初期化します
  • UNWRAP_MODE 暗号オブジェクトをキーアンラッピングモードに初期化します

Cipherオブジェクトを初期化してみましょう。

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// ...

これで、キーの長さ/エンコーディングが無効な場合など、指定されたキーが暗号の初期化に不適切な場合、initメソッドはInvalidKeyExceptionをスローします。

また、暗号がキーから決定できない特定のアルゴリズムパラメータを必要とする場合、またはキーのキーサイズが最大許容キーサイズ(構成されたJCE管轄ポリシーファイルから決定)を超える場合にもスローされます。

Certificateを使用した例を見てみましょう。

public byte[] encryptMessage(byte[] message, Certificate certificate) 
  throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
 
    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.ENCRYPT_MODE, certificate);
    // ...
}

Cipher オブジェクトは、 getPublicKey メソッドを呼び出すことにより、証明書からデータ暗号化用の公開鍵を取得します。

2.5. 暗号化と復号化

Cipher オブジェクトを初期化した後、 doFinal()メソッドを呼び出して、暗号化または復号化操作を実行します。 このメソッドは、暗号化または復号化されたメッセージを含むバイト配列を返します。

doFinal()メソッドは、 Cipher オブジェクトを、 init()メソッドの呼び出しによって以前に初期化されたときの状態にリセットし、[X169X ] Cipher オブジェクトは、追加のメッセージを暗号化または復号化するために使用できます。

encodeMessageメソッドでdoFinalを呼び出しましょう。

public byte[] encryptMessage(byte[] message, byte[] keyBytes)
  throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, 
    BadPaddingException, IllegalBlockSizeException {
 
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    return cipher.doFinal(message);
}

復号化操作を実行するには、opmodeDECRYPT_MODEに変更します。

public byte[] decryptMessage(byte[] encryptedMessage, byte[] keyBytes) 
  throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, 
    BadPaddingException, IllegalBlockSizeException {
 
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");
    cipher.init(Cipher.DECRYPT_MODE, secretKey);
    return cipher.doFinal(encryptedMessage);
}

2.6. プロバイダー

プロバイダーベースのアーキテクチャを使用するように設計されたJCEを使用すると、BouncyCastleなどの認定された暗号化ライブラリをセキュリティプロバイダーとしてプラグインし、新しいアルゴリズムをシームレスに追加できます

それでは、セキュリティプロバイダーとしてBouncyCastleを追加しましょう。 静的または動的にセキュリティプロバイダーを追加できます。

BouncyCastleを静的に追加するには、java.securityファイルを変更しますにあります / jre / lib / security フォルダ。

リストの最後に次の行を追加します。

...
security.provider.4=com.sun.net.ssl.internal.ssl.Provider
security.provider.5=com.sun.crypto.provider.SunJCE
security.provider.6=sun.security.jgss.SunProvider
security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider

プロバイダープロパティを追加する場合、プロパティキーは security.provider.N の形式になります。ここで、番号Nはリストの最後の番号より1つ多くなります。

セキュリティファイルを変更せずに、BouncyCastleセキュリティプロバイダーを動的に追加することもできます

Security.addProvider(new BouncyCastleProvider());

これで、暗号の初期化中にプロバイダーを指定できます。

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "BC");

BC は、プロバイダーとしてBouncyCastleを指定します。 Security.getProviders()メソッドを使用して、登録済みプロバイダーのリストを取得できます。

3. 暗号化と復号化のテスト

メッセージの暗号化と復号化を説明するためのテスト例を書いてみましょう。

このテストでは、128ビットキーでAES暗号化アルゴリズムを使用し、復号化された結果が元のメッセージテキストと等しいことを表明します。

@Test
public void whenIsEncryptedAndDecrypted_thenDecryptedEqualsOriginal() 
  throws Exception {
 
    String encryptionKeyString =  "thisisa128bitkey";
    String originalMessage = "This is a secret message";
    byte[] encryptionKeyBytes = encryptionKeyString.getBytes();

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    SecretKey secretKey = new SecretKeySpec(encryptionKeyBytes, "AES");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);

    byte[] encryptedMessageBytes = cipher.doFinal(message.getBytes());

    cipher.init(Cipher.DECRYPT_MODE, secretKey);

    byte[] decryptedMessageBytes = cipher.doFinal(encryptedMessageBytes);
    assertThat(originalMessage).isEqualTo(new String(decryptedMessageBytes));
}

4. 結論

この記事では、 Cipher クラスについて説明し、使用例を示しました。 Cipher クラスとJCEフレームワークの詳細については、クラスのドキュメント Java暗号化アーキテクチャ(JCA)リファレンスガイドを参照してください。

これらすべての例とコードスニペットの実装は、GitHubにあります。 これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。