【Java】3DESとBASE64での暗号化/復号化 実装メモ

# 3DESとは
3DESまたはTripleDesとはDES暗号化アルゴリズムを各データブロックに3回適用する対称鍵ブロック暗号です。

今回はJavaでの3DESによる暗号化/復号化、BASE64でのエンコード/デコードの処理を実装したので実装メモとして記載します。



# 1.秘密鍵の生成

#### キーの生成

```java
private static final byte[] SECRET_KEY = "6ydjai8dnfy4ud9d8dud7dud".getBytes();//24-byte
```

24バイトのキーを使用します。

#### キーをSecretKeySpecでラップし、アルゴリズムと組み合わせる

```java
private final String CRYPT_ALGORITHM = "DESede";

SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, CRYPT_ALGORITHM);
```

今回はDESedeを使用していますが、[Javaセキュリティ標準アルゴリズム](https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#algorithmparameters-algorithms)の中から適切なものを使用してください。


# 2.初期化ベクトルの生成

#### 初期化ベクトルの生成乱数と文字の8バイト配列を使用します。

```java
private static final byte[] IV = "ud8dks7s".getBytes();//8-byte
```

8バイト配列を使用します。

#### IvParameterSpecクラスでラップ

```java
IvParameterSpec ivSpec = new IvParameterSpec(IV);
```

# 3.暗号化

#### 使用する文字列を定義

```java
String srcText = "DesedeTest      ";
```

CBC/パディングなしで3DESアルゴリズムを使用するので、最後の8バイトブロックに対し、補完文字列が必要です。
`DeseseTe` `st□□□□□□` ※□=半角スペースで補完しています。

#### 暗号化モード、秘密鍵、および以前に生成した初期化ベクトルで初期化されたCipherオブジェクトを生成

```java
private static final String PADDING = "DESede/CBC/NoPadding";

Cipher cipher = Cipher.getInstance(PADDING);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
```

暗号化時は、`ENCRYPT_MODE`を使用します。

#### 暗号化

```java
byte[] encrypted = cipher.doFinal(srcText.getBytes());
```

Cipherを使用すると、 doFinalメソッドを実行してメッセージを暗号化できます。
これはバイト配列でのみ機能するため、最初に文字列を変換する必要があります。

#### BASE64でエンコード

```java
byte[] base64EncodedValue = Base64.getEncoder().encode(encrypted);
```

`java.util.Base64`のEncoderでエンコードすることでBASE64でエンコードされたバイト配列を取得できます。
`new String(base64EncodedValue)`とすることで、文字列に変換できます。


# 4.復号化

#### BASE64でデコード

```java
byte[] base64DecodedValue = Base64.getDecoder().decode(base64EncodedText);
```

#### 復号化用のCipherオブジェクトを生成
```java
Cipher cipher = Cipher.getInstance(PADDING);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
```

復号化時は、`DECRYPT_MODE`を使用します。

#### 復号化

```java
byte[] decrypted = cipher.doFinal(base64DecodedValue);
```


# コード
```java
package desedeSample;

import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * Encrypt/Decrypt by using Triple-DES
 *
 * @author ueD
 */
public class DesedeCrypter {

	private static final String CRYPT_ALGORITHM = "DESede";
	private static final String PADDING = "DESede/CBC/NoPadding";

	private static final byte[] SECRET_KEY = "6ydjai8dnfy4ud9d8dud7dud".getBytes();//24-byte
	private static final byte[] IV = "ud8dks7s".getBytes();//8-byte

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

		// 8バイトブロックに対し半角スペースで補完
		String srcText = "DesedeTest      ";
		System.out.println("srcText: " + srcText);

		DesedeCrypter crypter = new DesedeCrypter();
		// 秘密鍵
		SecretKeySpec secretKeySpec = new SecretKeySpec(SECRET_KEY, CRYPT_ALGORITHM);
		// 初期化ベクトル
		IvParameterSpec ivSpec = new IvParameterSpec(IV);


		// 暗号化処理
		byte[] encrypted = crypter.encrypt(srcText, secretKeySpec, ivSpec);
		System.out.println("3DES: " + new String(encrypted));

		//BASE64でエンコード
		String base64EncodedText = new String(Base64.getEncoder().encode(encrypted));
		System.out.println("BASE64エンコード: " + base64EncodedText);


		// 復号化処理
		// BASE64でデコード
		String base64DecodedText = new String(Base64.getDecoder().decode(base64EncodedText));
		System.out.println("3DES: " + base64DecodedText);

		byte[] decrypted = crypter.decrypt(Base64.getDecoder().decode(base64EncodedText), secretKeySpec, ivSpec);
		System.out.println("srcText復号化: " + new String(decrypted));

	}


	/**
	 * 3DESによる暗号化を行う
	 * @param value
	 * @return
	 * @throws Exception
	 */
	public byte[] encrypt(String value, SecretKeySpec secretKeySpec, IvParameterSpec ivSpec) throws Exception {
		try {
			Cipher cipher = Cipher.getInstance(PADDING);
			cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);

			// 暗号化
			byte[] encrypted = cipher.doFinal(value.getBytes());
			return encrypted;

		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
    }

	/**
	 * 3DESによる復号化を行う
	 * @param value
	 * @return
	 * @throws Exception
	 */
	public byte[] decrypt(byte[] value, SecretKeySpec secretKeySpec, IvParameterSpec ivSpec) throws Exception {

		try {
			Cipher cipher = Cipher.getInstance(PADDING);
			cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);

			byte[] decrypted = cipher.doFinal(value);

			return decrypted;

		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}
}
```


### 実行結果
```
srcText: DesedeTest      
3DES: P�-�����'�~�A
BASE64エンコード: UOQACy3u+qz0FOYnnn6OQQ==
3DES: P�-�����'�~�A
srcText復号化: DesedeTest      
```

# 参考

- https://www.baeldung.com/java-3des

- https://gist.github.com/riversun/6e15306cd6e3b1b37687a0e5cec1cef1


#### git
https://github.com/ueD-ryu/desede