###【概要】
共通鍵暗号方式の1つであるDESでは、ファイステルネットワークという構造を使用しています。
簡単に説明すると、ラウンド関数という関数と排他的論理和を使用した暗号化方式です。
DESでは入力の平文は64ビット、鍵のビット長は56ビットなどの決まりがありますが、今回はファイステルネットワークを実装してみたかっただけなので、そのような制約は排除しています。
###【コード】
ここで掲載しているコードは、下記リンク先のGitHubに載せています。
feistelNetwork
Main.java
package main;
import common.AbstractEncryption;
import encryption.Decryption;
import encryption.Encryption;
public class Main {
// ラウンド回数
public static int ROUND_EXEC_TIME = 3;
// ラウンド関数で使用する秘密鍵
// ラウンド関数はビットシフト演算をしているため、8よりも小さい数を指定しなければ暗号化ができない
// 攻撃者に解読されないようにするため、この鍵は秘密にしなければならない
private static int[] SECRET_KEY = {2, 3, 6};
public static void main(String[] args) {
// 秘密データ
String secretData = "testcase";
AbstractEncryption encryption = new Encryption();
AbstractEncryption decryption = new Decryption();
// 暗号化
String encryptionData = encryption.execEncryption(secretData, SECRET_KEY);
// 復号化
String deryptionData = decryption.execEncryption(encryptionData, SECRET_KEY);
System.out.println("暗号化データ:" + encryptionData);
System.out.println("復号化データ:" + deryptionData);
}
}
AbstractEncryption.java
package common;
import main.Main;
/**
* 暗号化と復号化の両方を受け持つクラス
* @author nobu
*
*/
public abstract class AbstractEncryption {
/**
* 暗号化と復号化を実行する
* @param secretData 秘密データ
* @param key 秘密鍵
*/
public String execEncryption(String secretData, int key[]) {
String left = secretData.substring(0, secretData.length() / 2);
String right = secretData.substring(secretData.length() / 2, secretData.length());
// 左側の文字列データ、暗号化されたデータを入れるための変数
byte leftArray[][];
// 左側の文字は秘密データの文字数が偶数、奇数の場合を考慮している
if (secretData.length() % 2 == 0) {
leftArray = new byte[Main.ROUND_EXEC_TIME + 1][left.length()];
} else {
leftArray = new byte[Main.ROUND_EXEC_TIME + 1][left.length() + 1];
}
// 右側の文字列データ、暗号化されたデータを入れるための変数
byte rightArray[][] = new byte[Main.ROUND_EXEC_TIME + 1][right.length()];
// 入力値の初期化
for (int i = 0; i < left.length(); i++) {
leftArray[0][i] = (byte) left.charAt(i);
}
for (int i = 0; i < right.length(); i++) {
rightArray[0][i] = (byte) right.charAt(i);
}
// 暗号化, 復号化
for (int i = 0; i < Main.ROUND_EXEC_TIME; i++) {
for (int j = 0; j < right.length(); j++) {
rightArray[i + 1][j] = encryption(leftArray[i][j], rightArray[i][j], key, i);
leftArray[i + 1][j] = rightArray[i][j];
}
// 最終ラウンドでは、左右を交換しないため、既に交換済みのデータを元に戻す
if (i == Main.ROUND_EXEC_TIME - 1) {
byte tmp[];
tmp = leftArray[i + 1];
leftArray[i + 1] = rightArray[i + 1];
rightArray[i + 1] = tmp;
}
}
String returnData = "";
for (int i = 0; i < right.length(); i++) {
returnData = returnData + String.valueOf((char) leftArray[Main.ROUND_EXEC_TIME][i]);
}
for (int i = 0; i < right.length(); i++) {
returnData = returnData + String.valueOf((char) rightArray[Main.ROUND_EXEC_TIME][i]);
}
return returnData;
}
/**
* 暗号化
* @param left 左側のデータ
* @param right 右側のデータ
* @param key 秘密鍵
* @param roundNumber
* @return 暗号化の結果
*/
abstract protected byte encryption(byte left, byte right, int[] key, int roundNumber);
/**
* ラウンド関数、秘密鍵で指定された値だけ右シフトする
* @param right 右側データ
* @param key 秘密鍵
* @return ラウンド関数の結果
*/
protected byte roundFunction(byte right, int key) {
return (byte) (right << key);
}
}
Encryption.java
package encryption;
import common.AbstractEncryption;
/**
* 暗号化クラス
* @author nobu
*
*/
public class Encryption extends AbstractEncryption {
@Override
protected byte encryption(byte left, byte right, int[] key, int roundNumber) {
return (byte) (left ^ roundFunction(right, key[roundNumber]));
}
}
Decryption.java
package encryption;
import common.AbstractEncryption;
import main.Main;
/**
* 復号化クラス
* @author nobu
*
*/
public class Decryption extends AbstractEncryption {
@Override
protected byte encryption(byte left, byte right, int[] key, int roundNumber) {
return (byte) (left ^ roundFunction(right, key[Main.ROUND_EXEC_TIME - roundNumber - 1]));
}
}
###【実行結果】
"testcase"という文字列を暗号化および、その暗号化したデータを復号化した結果です。
※暗号化データは文字化けしています。
暗号化データ:8ᄀᅠᆪiヒe
復号化データ:testcase
###【参考書籍】
暗号技術入門 第3版