AES-CBCで暗号・複合、AES-GCMで暗号・複合するJavaプログラム

# 1. AES-CBCとは
AES-CBC（Advanced Encryption Standard - Cipher Block Chaining）は、対称鍵暗号方式の AES（Advanced Encryption Standard）を CBC（Cipher Block Chaining）というモードで使用する暗号化アルゴリズムです。AES-CBC は、ブロック暗号の一つであり、特にデータの暗号化と復号に用いられます。

### 1.1. AES-CBCの特徴
#### 1.1.1. ブロック暗号モード

AES-CBC は、データを一定のブロック（通常は 128 ビット）ごとに分割して暗号化します。各ブロックは、前のブロックの暗号化結果と XOR（排他的論理和）されてから暗号化されるため、同じ平文ブロックが繰り返し現れても暗号文は異なります。

#### 1.1.2. 初期化ベクトル（IV）

AES-CBC では、最初のブロックを暗号化する前に、初期化ベクトル（IV）と呼ばれるランダムな値が使用されます。これにより、同じ平文を何度も暗号化しても、異なる暗号文が生成されます。
IV は各暗号化操作の際に異なる値を使用する必要がありますが、IV 自体は暗号化せずに送信されます。

#### 1.1.3. パディング

AES-CBC はブロックサイズの倍数でないデータを暗号化する際に、データの末尾にパディングを追加します。最も一般的に使用されるパディング方式は PKCS#5（または PKCS#7）です。

#### 1.1.4. 暗号化の順次処理

各ブロックは、前のブロックの暗号化結果に依存しているため、並列処理が難しく、AES-GCM などのカウンターモードに比べて処理速度が遅くなることがあります。

### 1.2. AES-CBCの利用例
AES-CBC は、以下のような状況で利用されています。

#### 1.2.1. データの保護

ファイルの暗号化やデータベースの暗号化に広く利用されており、古くから使用されている標準的な暗号化方式の一つです。

#### 1.2.2. TLS/SSL プロトコル

以前は、TLS/SSL プロトコルの一部として、Web サイトの HTTPS 通信を保護するために使用されていましたが、暗号ブロックの欠陥（例: パディングオラクル攻撃）が発見され、現在ではより安全な暗号化方式が推奨されています。

### 1.3. AES-CBCの問題点と代替策
#### 1.3.1. パディングオラクル攻撃

AES-CBC はパディングオラクル攻撃に対して脆弱であることが知られています。この攻撃により、暗号文から平文を復元する可能性があります。

#### 1.3.2. 認証がない

AES-CBC にはデータの整合性を保証する仕組みがないため、暗号文が改ざんされる可能性があります。このため、データの改ざんを防ぐために HMAC などの追加の認証手段が必要です。

#### 1.3.3. 代替策

現在では、AES-GCM や AES-CCM などの認証付き暗号化モードが推奨されることが多いです。これらのモードは、暗号化と認証を同時に行い、データの整合性を保証します。

# 2. AES-GCMとは

AES-GCM（Advanced Encryption Standard - Galois/Counter Mode）は、対称鍵暗号方式の一つである AES（Advanced Encryption Standard）を、GCM（Galois/Counter Mode）というモードで使用する暗号化アルゴリズムです。AES-GCM は、暗号化と同時に認証を提供するため、セキュリティの高い暗号化方式として広く使用されています。

### 2.1. AES-GCMの特徴
#### 2.1.1. 暗号化と認証の統合

AES-GCM は、データの暗号化と同時に、そのデータが改ざんされていないことを確認するための認証タグを生成します。これにより、暗号化データの整合性が保証されます。

#### 2.1.2. 速度と効率

GCM モードは、並列処理に適しているため、高速な暗号化が可能です。これにより、AES-GCM はパフォーマンスが要求される環境でも効果的に利用できます。

#### 2.1.3. パディング不要

GCM はカウンターモード（CTR）に基づいているため、データの長さがブロックサイズにぴったり合わない場合でも、パディングが不要です。

#### 2.1.4. 初期化ベクトル（IV）

AES-GCM では、12バイト（96ビット）の初期化ベクトル（IV）が推奨されています。96ビットの IV はパフォーマンスとセキュリティのバランスが良く、一般的に使用されています。

#### 2.1.5. 認証タグ

AES-GCM は、暗号化したデータに対する認証タグを生成します。このタグを使って、復号時にデータが改ざんされていないかを確認します。認証タグは通常、128ビットの長さを持ちます。

### 2.2. AES-GCMの利用例
AES-GCM は、以下のような状況で利用されます。

### 2.2.1. セキュアな通信

TLS（Transport Layer Security）プロトコルの一部として、インターネット上のデータ通信を保護するために使用されます。

### 2.2.2. データの保護

データベースの暗号化やファイルの保護など、暗号化とデータの整合性を同時に確保する必要があるシナリオで利用されます。

# 3. JavaでのAES-CBCの暗号・複合のサンプル

AES-CBCで暗号・複合するJavaのサンプルは以下のとおりです。

```java:AesCbc.java
package crypto;

import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

public class AesCbc {

	/** AES */
	public static final String AES = "AES";
	/** AES/CBC/PKCS5Padding */
	public static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5Padding";
	/** IVの長さ */
	public static final int GCM_IV_LENGTH = 16;
	/** AESのキーの長さ ( 128 or 192 or 256 ) */
	public static final int AES_KEY_SIZE = 256;

	// 平文
	private static String plainText = "こんにちは。この文を「AES-CBC」の暗号化アルゴリズムによって暗号化します。";

	public static void main(String[] args) throws GeneralSecurityException {

		// 共通鍵の生成
		KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
		keyGenerator.init(AES_KEY_SIZE);
		SecretKey secretKey = keyGenerator.generateKey();

		// 初期化ベクトル（IV）の生成
		byte[] iv = new byte[GCM_IV_LENGTH];
		SecureRandom secureRandom = new SecureRandom();
		secureRandom.nextBytes(iv);

		// IvParameterSpecインスタンス生成
		IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

		// 平文出力
		System.out.println("Original Text : " + plainText);
		byte[] plainTextData = plainText.getBytes(StandardCharsets.UTF_8);

		// 暗号化
		byte[] encryptedData = encrypt(plainTextData, secretKey, ivParameterSpec);

		// 暗号化データ出力
		System.out.println("Encrypted Text : " + Base64.getEncoder().encodeToString(encryptedData));

		// 複合化
		byte[] decryptedData = decrypt(encryptedData, secretKey, ivParameterSpec);

		// 複合化データ出力
		System.out.println("Decrypted Text : " + new String(decryptedData, StandardCharsets.UTF_8));
	}

	/**
	 * 暗号化
	 * 
	 * @param plainData 平文データ
	 * @param key       暗号化キー
	 * @param params    暗号パラメータ
	 * @return 暗号化データ
	 * @throws GeneralSecurityException
	 */
	public static byte[] encrypt(byte[] plainData, SecretKey secretKey, AlgorithmParameterSpec params)
			throws GeneralSecurityException {

		// Cipherインスタンス生成
		Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);

		// Cipherに暗号モードを設定
		cipher.init(Cipher.ENCRYPT_MODE, secretKey, params);

		// 暗号化
		byte[] cipherData = cipher.doFinal(plainData);

		return cipherData;
	}

	/**
	 * 複合化
	 * 
	 * @param cipherData 暗号化データ
	 * @param key        暗号キー
	 * @param params     暗号パラメータ
	 * @return 複合化データ
	 * @throws GeneralSecurityException
	 */
	public static byte[] decrypt(byte[] cipherData, SecretKey key, AlgorithmParameterSpec params)
			throws GeneralSecurityException {
		// Cipherインスタンス生成
		Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);

		// Cipherに複合モードを設定
		cipher.init(Cipher.DECRYPT_MODE, key, params);

		// 複合化
		byte[] decryptedData = cipher.doFinal(cipherData);

		return decryptedData;
	}

}
```

実行結果は以下となります。

```:実行結果
Original Text : こんにちは。この文を「AES-CBC」の暗号化アルゴリズムによって暗号化します。
Encrypted Text : qnfLEM/GQsZZPJepe4YE6oDJvwNYRxo/f4XAVZmpgk7a9Zb0SQJHuXSYaGhdslTfIqCBsv+Q807x/QGKytVNNb7isfPJgrVoIrS3qTDUS5UnqnMar5NLhK6XCUTrYbFA/9DWDtqfaksyB2CYw0PtCw==
Decrypted Text : こんにちは。この文を「AES-CBC」の暗号化アルゴリズムによって暗号化します。
```

このプログラムは、JavaでAES（Advanced Encryption Standard）を使用して、CBC（Cipher Block Chaining）モードでデータを暗号化および復号化する方法を示しています。以下は、プログラムの各部分の説明です。

### 3.1. クラスの定義と定数の宣言

```java:java
public class AesCbc {
    public static final String AES = "AES";
    public static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5Padding";
    public static final int GCM_IV_LENGTH = 16;
    public static final int AES_KEY_SIZE = 256;
}
```
+ **AES**: AESアルゴリズムを使用するための定数
+ **AES_CBC_PKCS5PADDING**: AESをCBCモードで使用し、PKCS5Paddingを適用するための定数
+ **GCM_IV_LENGTH**: 初期化ベクトル（IV）の長さ（16バイト）を指定
+ **AES_KEY_SIZE**: AESキーの長さを256ビットに指定

### 3.2. 平文の定義

```java:java
private static String plainText = "こんにちは。この文を「AES-CBC」の暗号化アルゴリズムによって暗号化します。";
```

暗号化する平文が定義されています。

### 3.3. mainメソッド

```java:java
public static void main(String[] args) throws GeneralSecurityException {
```
`main`メソッドでは、以下の手順で暗号化と復号化を行います。

###### 3.3.1. 共通鍵の生成

```java:java
KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
keyGenerator.init(AES_KEY_SIZE);
SecretKey secretKey = keyGenerator.generateKey();
```

AESの共通鍵（秘密鍵）を生成します。256ビットのキーを生成するために`KeyGenerator`を使用しています。

###### 3.3.2. 初期化ベクトル（IV）の生成

```java:java
byte[] iv = new byte[GCM_IV_LENGTH];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
```

暗号化の際に必要となる初期化ベクトル（IV）を生成します。`SecureRandom`を使用してランダムなIVを生成し、`IvParameterSpec`に渡します。

###### 3.3.3. 平文の表示とバイト配列への変換

```java:java
System.out.println("Original Text : " + plainText);
byte[] plainTextData = plainText.getBytes(StandardCharsets.UTF_8);
```

平文を表示し、それをUTF-8でバイト配列に変換します。

###### 3.3.4. 暗号化

```java:java
byte[] encryptedData = encrypt(plainTextData, secretKey, ivParameterSpec);
System.out.println("Encrypted Text : " + Base64.getEncoder().encodeToString(encryptedData));
```

`encrypt`メソッドを使用して、平文を暗号化し、その結果をBase64エンコードして表示します。

###### 3.3.5. 複合化

```java:java
byte[] decryptedData = decrypt(encryptedData, secretKey, ivParameterSpec);
System.out.println("Decrypted Text : " + new String(decryptedData, StandardCharsets.UTF_8));
```

`decrypt`メソッドを使用して、暗号文を復号化し、元のテキストに戻して表示します。

### 3.4. encryptメソッド

```java:java
public static byte[] encrypt(byte[] plainData, SecretKey secretKey, AlgorithmParameterSpec params)
        throws GeneralSecurityException {
    Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, params);
    byte[] cipherData = cipher.doFinal(plainData);
    return cipherData;
}
```

`Cipher`インスタンスを生成し、暗号化モードに初期化します。
暗号化処理を行い、暗号文をバイト配列として返します。

### 3.5. decryptメソッド

```java:java
public static byte[] decrypt(byte[] cipherData, SecretKey key, AlgorithmParameterSpec params)
        throws GeneralSecurityException {
    Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);
    cipher.init(Cipher.DECRYPT_MODE, key, params);
    byte[] decryptedData = cipher.doFinal(cipherData);
    return decryptedData;
}
```

`Cipher`インスタンスを生成し、復号化モードに初期化します。
複合化処理を行い、復号されたデータをバイト配列として返します。

### 3.6. まとめ（AES-CBCのサンプル）

このプログラムは、指定された平文をAES-CBCモードで暗号化し、その暗号文を復号化するデモンストレーションです。AESキーとIVを生成し、`Cipher`クラスを使用して暗号化および復号化を行っています。最終的に、元のテキストと一致することが確認できます。

# 4. JavaでのAES-GCMの暗号・複合のサンプル

AES-GCMで暗号・複合するJavaのサンプルは以下のとおりです。

```java:AesGcm.java
package crypto;

import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;

public class AesGcm {

	/** AES */
	public static final String AES = "AES";
	/** AES/GCM/NoPadding */
	public static final String AES_GCM_NO_PADDING = "AES/GCM/NoPadding";
	/** GCMのIVの長さ */
	public static final int GCM_IV_LENGTH = 12;
	/** AESのキーの長さ ( 128 or 192 or 256 ) */
	public static final int AES_KEY_SIZE = 256;
	/** GCMのタグの長さ ( 96 or 104 or 112 or 120 or 128 ) */
	private static final int GCM_TAG_LENGTH = 128;

	// 平文
	private static String plainText = "こんにちは。この文を「AES-GCM」の暗号化アルゴリズムによって暗号化します。";
	// ADD (64KiB以内)
	private static String addText = "This is an additional authenticated data. It's called the AAD. You can use any value.";

	public static void main(String[] args) throws GeneralSecurityException {

		// 共通鍵の生成
		KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
		keyGenerator.init(AES_KEY_SIZE);
		SecretKey secretKey = keyGenerator.generateKey();

		// 初期化ベクトル（IV）の生成
		byte[] iv = new byte[GCM_IV_LENGTH];
		SecureRandom secureRandom = new SecureRandom();
		secureRandom.nextBytes(iv);

		// GCMParameterSpecインスタンス生成
		GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);

		// add（追加認証）生成
		byte[] aad = addText.getBytes(StandardCharsets.UTF_8);

		// 平文出力
		System.out.println("Original Text : " + plainText);
		byte[] plainTextData = plainText.getBytes(StandardCharsets.UTF_8);

		// 暗号化
		byte[] encryptedData = encrypt(plainTextData, secretKey, gcmParameterSpec, aad);

		// 暗号化データ出力
		System.out.println("Encrypted Text : " + Base64.getEncoder().encodeToString(encryptedData));

		// 複合化
		byte[] decryptedData = decrypt(encryptedData, secretKey, gcmParameterSpec, aad);

		// 複合化データ出力
		System.out.println("Decrypted Text : " + new String(decryptedData, StandardCharsets.UTF_8));
	}

	/**
	 * 暗号化
	 * 
	 * @param plainData 平文データ
	 * @param key       暗号化キー
	 * @param params    暗号パラメータ
	 * @param aad       追加認証
	 * @return 暗号化データ
	 * @throws GeneralSecurityException
	 */
	public static byte[] encrypt(byte[] plainData, SecretKey secretKey, AlgorithmParameterSpec params, byte[] aad)
			throws GeneralSecurityException {

		// Cipherインスタンス生成
		Cipher cipher = Cipher.getInstance(AES_GCM_NO_PADDING);

		// Cipherに暗号モードを設定
		cipher.init(Cipher.ENCRYPT_MODE, secretKey, params);

		// ADD（追加認証）設定
		if (aad != null) {
			cipher.updateAAD(aad);
		}

		// 暗号化
		byte[] cipherData = cipher.doFinal(plainData);

		return cipherData;
	}

	/**
	 * 複合化
	 * 
	 * @param cipherData 暗号化データ
	 * @param key        暗号キー
	 * @param params     暗号パラメータ
	 * @param aad        追加認証
	 * @return 複合化データ
	 * @throws GeneralSecurityException
	 */
	public static byte[] decrypt(byte[] cipherData, SecretKey key, AlgorithmParameterSpec params, byte[] aad)
			throws GeneralSecurityException {
		// Cipherインスタンス生成
		Cipher cipher = Cipher.getInstance(AES_GCM_NO_PADDING);

		// Cipherに複合モードを設定
		cipher.init(Cipher.DECRYPT_MODE, key, params);

		// ADD（追加認証）設定
		if (aad != null) {
			cipher.updateAAD(aad);
		}

		// 複合化
		byte[] decryptedData = cipher.doFinal(cipherData);

		return decryptedData;
	}

}
```

実行結果は以下となります。

```:実行結果
Original Text : こんにちは。この文を「AES-GCM」の暗号化アルゴリズムによって暗号化します。
Encrypted Text : uyHazXxFoNpN2N9TfK5kPE8+SCar2wPEdRT4t3kbVsaSneN9cNkDChECJVjPTZRlDGUNCQ2PNaX5rC5Fghu3SPxsPAAJMaw2KZwEvdnj9E0NwPSSsZTV2zkwmMopussfEzSErjc4FjcY0dQy6hA+x5ryHxgw6HvKvyU=
Decrypted Text : こんにちは。この文を「AES-GCM」の暗号化アルゴリズムによって暗号化します。
```

このプログラムは、JavaでAES（Advanced Encryption Standard）を使用してGCM（Galois/Counter Mode）モードでデータを暗号化および復号化する方法を示しています。GCMモードは、高いセキュリティと認証機能を提供する暗号化モードであり、追加認証データ（AAD）をサポートします。以下は、プログラムの各部分の説明です。

### 4.1. クラスの定義と定数の宣言

```java:java
public class AesGcm {
    public static final String AES = "AES";
    public static final String AES_GCM_NO_PADDING = "AES/GCM/NoPadding";
    public static final int GCM_IV_LENGTH = 12;
    public static final int AES_KEY_SIZE = 256;
    private static final int GCM_TAG_LENGTH = 128;
}
```

+ **AES**: AESアルゴリズムを使用するための定数
+ **AES_GCM_NO_PADDING**: AESをGCMモードで使用し、パディングなしで動作させるための定数
+ **GCM_IV_LENGTH**: GCMモードで使用する初期化ベクトル（IV）の長さを12バイトに指定
+ **AES_KEY_SIZE**: AESキーの長さを256ビットに指定
+ **GCM_TAG_LENGTH**: GCMモードで生成される認証タグの長さを128ビットに指定

### 4.2. 平文と追加認証データ（AAD）の定義

```java:java
private static String plainText = "こんにちは。この文を「AES-GCM」の暗号化アルゴリズムによって暗号化します。";
private static String addText = "This is an additional authenticated data. It's called the AAD. You can use any value.";
```

+ **plainText**: 暗号化する平文
+ **addText**: AADとして使用される追加認証データ（このデータは暗号化されませんが、認証の一部として使用されます。）

### 4.3. main メソッド

```java:java
public static void main(String[] args) throws GeneralSecurityException {
```

`main`メソッドでは、以下の手順で暗号化と復号化を行います。

###### 4.3.1 共通鍵の生成

```java:java
KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
keyGenerator.init(AES_KEY_SIZE);
SecretKey secretKey = keyGenerator.generateKey();
```

AESの共通鍵（秘密鍵）を生成します。256ビットのキーを生成するために`KeyGenerator`を使用しています。

###### 4.3.2 初期化ベクトル（IV）の生成

```java:java
byte[] iv = new byte[GCM_IV_LENGTH];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(iv);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
```

暗号化の際に必要となる初期化ベクトル（IV）を生成します。`SecureRandom`を使用してランダムなIVを生成し、`GCMParameterSpec`に渡します。

###### 4.3.3 AADの生成と平文の表示

```java:java
byte[] aad = addText.getBytes(StandardCharsets.UTF_8);
System.out.println("Original Text : " + plainText);
byte[] plainTextData = plainText.getBytes(StandardCharsets.UTF_8);
```

AADをUTF-8でバイト配列に変換し、平文を表示した後に同じくバイト配列に変換します。

###### 4.3.4 暗号化

```java:java
byte[] encryptedData = encrypt(plainTextData, secretKey, gcmParameterSpec, aad);
System.out.println("Encrypted Text : " + Base64.getEncoder().encodeToString(encryptedData));
```

`encrypt`メソッドを使用して、平文をAES-GCMモードで暗号化し、その結果をBase64エンコードして表示します。

###### 4.3.5 複合化

```java:java
byte[] decryptedData = decrypt(encryptedData, secretKey, gcmParameterSpec, aad);
System.out.println("Decrypted Text : " + new String(decryptedData, StandardCharsets.UTF_8));
```

`decrypt`メソッドを使用して、暗号文を復号化し、元のテキストに戻して表示します。

### 4.4. encrypt メソッド

```java:java
public static byte[] encrypt(byte[] plainData, SecretKey secretKey, AlgorithmParameterSpec params, byte[] aad)
        throws GeneralSecurityException {
    Cipher cipher = Cipher.getInstance(AES_GCM_NO_PADDING);
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, params);

    if (aad != null) {
        cipher.updateAAD(aad);
    }

    byte[] cipherData = cipher.doFinal(plainData);
    return cipherData;
}
```

`Cipher`インスタンスを生成し、暗号化モードに初期化します。
AADが指定されていれば、それを`Cipher`に設定します。
暗号化処理を行い、暗号文をバイト配列として返します。

### 4.5. decrypt メソッド

```java:java
public static byte[] decrypt(byte[] cipherData, SecretKey key, AlgorithmParameterSpec params, byte[] aad)
        throws GeneralSecurityException {
    Cipher cipher = Cipher.getInstance(AES_GCM_NO_PADDING);
    cipher.init(Cipher.DECRYPT_MODE, key, params);

    if (aad != null) {
        cipher.updateAAD(aad);
    }

    byte[] decryptedData = cipher.doFinal(cipherData);
    return decryptedData;
}
```

`Cipher`インスタンスを生成し、復号化モードに初期化します。
AADが指定されていれば、それを`Cipher`に設定します。
複合化処理を行い、復号されたデータをバイト配列として返します。

### 4.6. まとめ（AES-GCMのサンプル）

このプログラムは、AES-GCMモードでデータを暗号化および復号化する方法を示しています。GCMモードは、暗号化と認証を同時に行うため、データの機密性と整合性を確保します。追加認証データ（AAD）は、暗号文の認証を強化するために使用されますが、暗号化されません。プログラムの実行により、元のテキストが正しく復号され、認証に成功したことを確認できます。

# 5. AES-CBCとAES-GCMのサンプルプログラムの違い

上記の2つのプログラムは、どちらもAES（Advanced Encryption Standard）を使用してデータを暗号化および復号化するものですが、使用している暗号モードが異なります。それぞれの違いについて説明します。

### 5.1. 暗号モード

###### 5.1.1. AES-CBC
使用するモードはCBC（Cipher Block Chaining）です。
ブロック暗号モードの一種で、各ブロックが前の暗号ブロックに依存します。最初のブロックには初期化ベクトル（IV）が使用されます。
特徴としては単純で広く使われていますが、認証機能がなく、暗号文の整合性は保証されません。また、パディングを必要とします（`PKCS5Padding`）。

###### 5.1.2. AES-GCM

使用するモードはGCM（Galois/Counter Mode）です。
これは認証付き暗号モードで、暗号化と認証を同時に行うことができます。データの機密性だけでなく、整合性も保証されます。
特徴としては高速で、追加認証データ（AAD）を扱えるため、セキュリティが向上しています。パディングが不要です（`NoPadding`）。

### 5.2. 初期化ベクトル（IV）
###### 5.2.1. AES-CBC
初期化ベクトルの長さは16バイト（128ビット）です。これはAESブロックのサイズに一致しています。

###### 5.2.2. AES-GCM
初期化ベクトルの長さは12バイト（96ビット）です。GCMモードでは、12バイトのIVが推奨されています。

### 5.3. 認証
###### 5.3.1. AES-CBC
認証機能がありません。したがって、暗号文が改ざんされた場合、その検出は困難です。

###### 5.3.2. AES-GCM
認証機能が組み込まれています。認証タグが生成され、復号時にデータの整合性を検証します。
このプログラムでは、追加認証データ（AAD）を使用して追加の認証を行います。AADは暗号文の一部として暗号化されず、認証だけに使われます。

### 5.4. パディング
###### 5.4.1. AES-CBC
PKCS5パディングを使用します。平文の長さがブロックサイズの倍数でない場合、パディングを追加してサイズを調整します。

###### 5.4.2. AES-GCM
パディングが不要です。GCMモードはストリーム暗号に近い動作をするため、平文の長さに関係なく暗号化できます。

### 5.5. セキュリティ面
###### 5.5.1. AES-CBC
シンプルで広く使用されていますが、特定の攻撃に対して脆弱性がある場合があります（例: パディングオラクル攻撃）。

###### 5.5.2. AES-GCM
より安全で、現代のセキュリティ要件に適合した認証付き暗号モードです。多くの新しいプロトコルやシステムで推奨されています。

### 5.6. まとめ（AES-CBCとAES-GCMのサンプルプログラムの違い）
**AES-CBC**は、基本的な暗号化モードであり、主に機密性を提供しますが、認証機能がないため、データの改ざん検出ができません。

**AES-GCM**は、暗号化と同時に認証も行うモードであり、より高いセキュリティを提供します。追加認証データ（AAD）を使用することで、さらに柔軟な認証が可能です。

このように、AES-GCMはAES-CBCに比べてセキュリティが強化されており、現代の多くのセキュリティプロトコルで採用されています。

---

以上