[Java] 単純なベイジアン アルゴリズムに基づいて、ハッシュ テーブルに基づくランダム文字置換暗号化アルゴリズムを解読します。
この記事は間違っているので読まないでください。得られる結果も正しい結果ではありません。この復号化アルゴリズムの前提は、ハッシュ テーブルがすでにわかっているときに計算することであるため、私は間違っていました。実際には、統計にのみ依存して暗号文を分析する必要があるため、実際の解読に必要な暗号文の文字数は、この記事で計算した数値よりも多くなるはずです。
序文
この記事では、単純ベイジアン アルゴリズムを使用して、ランダム文字置換暗号化アルゴリズムを解読するモデルをトレーニングし、復号化されたデータの統計を通じて暗号化アルゴリズムのセキュリティを分析する方法を紹介します。
まとめ
この論文は、ハッシュ テーブルに基づくランダム文字置換暗号化アルゴリズムとそのセキュリティ問題を研究し、単純ベイジアン アルゴリズムを使用して暗号化アルゴリズムを解読し、解読結果を分析して議論します。研究結果によると、暗号化アルゴリズムの安全性は不十分であり、簡単に破られることがわかっています。本稿ではアルゴリズムの安全性を向上させるための改善策を提案する。
キーワード: ハッシュ テーブル、ランダム文字置換暗号化、単純ベイジアン アルゴリズム、セキュリティ
1. コード部分
コード部分は次のようになります。
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;
import java.util.*;
public class Main {
private static final int WIDTH = 600;
private static final int HEIGHT = 400;
private static final int BORDER_GAP = 30;
private static final Color LINE_COLOR = Color.BLUE;
private static final Stroke GRAPH_STROKE = new BasicStroke(2f);
private static final int GRAPH_POINT_WIDTH = 6;
public static void main(String[] args) {
//读取哈希表
Map<Character, List<Character>> hashTable;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hashtable.ser"))) {
hashTable = (Map<Character, List<Character>>) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return;
}
NaiveBayes naiveBayes = null;
ArrayList<Double> accuracyList = new ArrayList<>();
ArrayList<Integer> lengthList = new ArrayList<>();
for (int times = 0; times < 1000000; times += 1000) {
lengthList.add(times);
int length = 1;
//生成10000个长度为100的字符串
Random random = new Random();
List<String> originalStrings = getOriginalStrings(times, length, random);
List<String> encryptedStrings = getEncryptedStrings(hashTable, originalStrings, random);
Map<String, String> dataSet = getStringStringMap(originalStrings, encryptedStrings);
//使用朴素贝叶斯算法训练出一个解密的模型
naiveBayes = new NaiveBayes();
naiveBayes.train(dataSet);
accuracyList.add(accuracyTest(hashTable, length, random, naiveBayes));
if (accuracyList.get(accuracyList.size() - 1) > 0.99) {
System.out.println("准确率(0~1): " + accuracyList.get(accuracyList.size() - 1));
System.out.println("截取密文长度:" + times + '\n');
break;
}
}
drawGraph(accuracyList, lengthList, 20000, 0.1);
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要解密的密文:");
String encryptedString = scanner.nextLine();
System.out.println("解密结果:\n" + naiveBayes.predict(encryptedString));
}
private static List<String> getOriginalStrings(int times, int length, Random random) {
List<String> originalStrings = new ArrayList<>();
for (int i = 0; i < times; i++) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < length; j++) {
int r = random.nextInt(36);
if (r < 26) {
sb.append((char) ('a' + r));
} else if (r < 35) {
sb.append((char) ('0' + r - 26));
} else {
sb.append(' ');
}
}
originalStrings.add(sb.toString());
}
return originalStrings;
}
private static List<String> getEncryptedStrings(Map<Character, List<Character>> hashTable, List<String> originalStrings, Random random) {
//将这些字符串通过哈希表进行加密
List<String> encryptedStrings = new ArrayList<>();
for (String originalString : originalStrings) {
StringBuilder sb = new StringBuilder();
for (char c : originalString.toCharArray()) {
sb.append(hashTable.get(c).get(random.nextInt(hashTable.get(c).size())));
}
encryptedStrings.add(sb.toString());
}
return encryptedStrings;
}
private static Map<String, String> getStringStringMap(List<String> originalStrings, List<String> encryptedStrings) {
//将生成的密文和原字符串一一对应起来
Map<String, String> dataSet = new HashMap<>();
for (int i = 0; i < originalStrings.size(); i++) {
dataSet.put(encryptedStrings.get(i), originalStrings.get(i));
}
return dataSet;
}
private static double accuracyTest(Map<Character, List<Character>> hashTable, int length, Random random, NaiveBayes naiveBayes) {
int timesTest = 100000;
//生成10000个随机字符,用哈希表进行加密后,再用训练出的模型进行解密
List<String> testStrings = getOriginalStrings(timesTest, length, random);
//用哈希表加密
List<String> testEncryptedStrings = getEncryptedStrings(hashTable, testStrings, new Random());
//用训练出的模型进行解密
List<String> testDecryptedStrings = new ArrayList<>();
for (String testEncryptedString : testEncryptedStrings) {
testDecryptedStrings.add(naiveBayes.predict(testEncryptedString));
}
//将解密结果与原字符进行对比,评测模型的准确度并打印到输出台上
int correctCount = 0;
for (int i = 0; i < testStrings.size(); i++) {
if (testStrings.get(i).equals(testDecryptedStrings.get(i))) {
correctCount++;
}
}
return correctCount / (double) timesTest;
}
public static void drawGraph(ArrayList<Double> accuracyList, ArrayList<Integer> lengthList, int xUnitLength, double yUnitLength) {
JFrame frame = new JFrame("统计测试");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(WIDTH, HEIGHT);
JPanel panel = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
setBackground(Color.WHITE);
//绘制坐标系
g2.drawLine(BORDER_GAP, getHeight() - BORDER_GAP, BORDER_GAP, BORDER_GAP);
g2.drawLine(BORDER_GAP, getHeight() - BORDER_GAP, getWidth() - BORDER_GAP, getHeight() - BORDER_GAP);
//绘制数据点和连线
int pointX, pointY, prevPointX = 0, prevPointY = 0;
for (int i = 0; i < accuracyList.size(); i++) {
pointX = (int) ((double) (getWidth() - 2 * BORDER_GAP) / (lengthList.size() - 1) * i + BORDER_GAP);
pointY = (int) ((getHeight() - 2 * BORDER_GAP) * (1 - accuracyList.get(i)) + BORDER_GAP);
g2.setColor(LINE_COLOR);
g2.setStroke(GRAPH_STROKE);
if (i > 0) {
g2.drawLine(prevPointX, prevPointY, pointX, pointY);
}
g2.fillOval(pointX - GRAPH_POINT_WIDTH / 2, pointY - GRAPH_POINT_WIDTH / 2, GRAPH_POINT_WIDTH, GRAPH_POINT_WIDTH);
prevPointX = pointX;
prevPointY = pointY;
//在x轴上绘制坐标值
if (lengthList.get(i) % xUnitLength == 0) {
g2.drawString(String.format("%,d", lengthList.get(i)), pointX, getHeight() - BORDER_GAP / 2);
}
}
//在y轴上绘制坐标值
for (int i = 0; i <= 10; i++) {
int y = (int) ((getHeight() - 2 * BORDER_GAP) * (1 - i * 0.1) + BORDER_GAP);
g2.drawString(String.format("%.1f", i * yUnitLength), BORDER_GAP / 2, y);
}
//在x轴下标注密文长度
g2.drawString("密文长度", getWidth() / 2, getHeight() - BORDER_GAP / 4);
//在y轴旁边标注准确率
AffineTransform origTransform = g2.getTransform();
AffineTransform at = new AffineTransform();
at.rotate(-Math.PI / 2);
g2.setTransform(at);
g2.drawString("准确率", -getHeight() / 2, BORDER_GAP / 2);
g2.setTransform(origTransform);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(WIDTH, HEIGHT);
}
};
frame.add(panel);
frame.setVisible(true);
}
private static class NaiveBayes {
private Map<String, Map<Character, Integer>> featureCount;
private Map<String, Integer> labelCount;
public NaiveBayes() {
featureCount = new HashMap<>();
labelCount = new HashMap<>();
}
public void train(Map<String, String> dataSet) {
for (Map.Entry<String, String> entry : dataSet.entrySet()) {
String encryptedString = entry.getKey();
String originalString = entry.getValue();
for (int i = 0; i < encryptedString.length(); i++) {
String feature = encryptedString.substring(i, i + 1);
char label = originalString.charAt(i);
if (!featureCount.containsKey(feature)) {
featureCount.put(feature, new HashMap<>());
}
if (!featureCount.get(feature).containsKey(label)) {
featureCount.get(feature).put(label, 0);
}
featureCount.get(feature).put(label, featureCount.get(feature).get(label) + 1);
}
if (!labelCount.containsKey(originalString)) {
labelCount.put(originalString, 0);
}
labelCount.put(originalString, labelCount.get(originalString) + 1);
}
}
public String predict(String encryptedString) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < encryptedString.length(); i++) {
String feature = encryptedString.substring(i, i + 1);
char label = 'a';
int maxCount = 0;
//若找不到字符对应的指针,则解出的密文赋与*字符
if (!featureCount.containsKey(feature)) {
sb.append('*');
continue;
}
for (Map.Entry<Character, Integer> entry : featureCount.get(feature).entrySet()) {
if (entry.getValue() > maxCount) {
maxCount = entry.getValue();
label = entry.getKey();
}
}
sb.append(label);
}
return sb.toString();
}
}
}
2. ハッシュテーブルに基づくランダム文字置換暗号化アルゴリズム
ハッシュ テーブル ベースのランダム文字置換暗号化アルゴリズムの暗号化プロセスは次のとおりです。
- 26 個の英字、0 ~ 9 の 10 個の数字、スペース文字を含む 37 文字を含む文字セットをランダムに生成します。
- 文字ごとに、Unicode でエンコードされた文字がランダムに割り当てられ、合計 1771 文字が割り当てられ、これらの文字がハッシュ テーブルに格納されます。
- 暗号化が必要な場合、平文内の各文字はハッシュ テーブル内の他の文字に置き換えられます。つまり、ハッシュ テーブル内の文字がランダムに選択されて置き換えられます。
この暗号化アルゴリズムの詳細については、以前の記事を参照してください。
[Java] ハッシュ テーブルに基づくランダムな文字置換暗号化アルゴリズム -- なぜそうではないのでしょうか?
3. 単純ベイジアンアルゴリズムに基づくトレーニングモデル
単純ベイズ分類は、ベイズの定理と特徴条件の独立性の仮定に基づいた分類方法です。入力データを分類するために使用できます。特徴が互いに独立していると想定しているため、計算が簡素化されます。
このコードでは、Naive Bayes アルゴリズムを使用して暗号化された文字列を復号化します。まず、ランダム関数を使用して一定数の元の文字列を生成し、ハッシュ テーブルを通じてそれらを暗号化して暗号文のペアを生成します。次に、復号化されたモデルが Naive Bayes を使用してトレーニングされます。
モデルのトレーニング プロセス:
-
データセット内の暗号文と元のテキストの各ペアを走査し、元のテキストの各文字をラベルとして使用し、暗号文の各文字を特徴として使用し、各ラベルの各特徴の頻度を計算して、フィーチャのカウンターとそのラベルの中央。
-
ラベルごとに、トレーニング セット内の出現数がカウントされ、ラベル カウンターに格納されます。
-
ベイジアン公式を使用して、各ラベルに対応する各特徴の確率を計算し、計算結果をモデルに保存します。
トレーニングが完了すると、入力暗号文に対してモデルを使用して各特徴に対応するラベル確率を計算し、最も高い確率を持つラベルが復号結果として採用されます。
4. 単純なベイジアン アルゴリズムがハッシュ テーブルに基づくランダム文字置換暗号化アルゴリズムを解読する
-
データセットを準備します。
一定数の文字列をランダムに生成し、ハッシュ テーブルを通じてランダムな文字を置換し、取得した暗号文と元の文字列の対応関係を作成し、トレーニング データ セットとして使用します。 -
モデルを構築する:
単純なベイジアン アルゴリズムを使用してトレーニング モデルを構築し、各文字が別の文字に置き換えられる確率を計算します。 -
クラックして正解率を計算します。
十分な数の文字列を再度ランダムに生成し、ハッシュ テーブル ベースのランダム文字置換暗号化アルゴリズムで暗号化してテスト セットを取得します。トレーニングされたモデルに従って、単純なベイジアン アルゴリズムを使用してテスト セットの暗号文を解読し、モデルの精度が計算されてカウントされます。
5. 安全性分析
単純ベイジアンアルゴリズムにより暗号を解読し、統計的に暗号文の文字数と正解率の関係曲線を描きます。以下に示すように:
図の曲線から、モデルが約 60,000 個の暗号文文字を傍受すると、暗号文の 60 ~ 70% を解読できることがわかります。約10万文字の暗号文を傍受すると、暗号文の約80%を解読できる。したがって、セキュリティが低くなります。
6. 実現可能な改善方法
暗号化アルゴリズムを改善してセキュリティを強化するための 2 つの実行可能な方法を次に示します。
-
文字セットの文字数を増やします。元の文字セットのサイズは 37 です。暗号化のセキュリティを高めるために文字セットの数を増やすことができますが、この方法では特定の文字を使用するかどうかを検討する必要があります。キャラクター集中に追加します。平文で特定の文字を使用する必要がなければ、追加する必要はありませんので、このように暗号化のセキュリティを強化するのは不便だと思います。
-
各文字に割り当てられる文字数を増やす: 元の割り当て文字数は 1771 文字であり、各文字に割り当てられる文字数を増やすことで暗号化のセキュリティを向上させることができます。そして、この増加は無制限に可能です。つまり、割り当てられる文字数が十分に大きい場合、暗号化方式のセキュリティを無限に向上させることができます。
7. まとめ
この論文は、ハッシュ テーブルに基づくランダム文字置換暗号化アルゴリズムとそのセキュリティ問題を研究し、単純ベイジアン アルゴリズムを使用して暗号化アルゴリズムを解読し、解読結果を分析して議論します。その結果、暗号化アルゴリズムの安全性が不十分であり、容易に解読されてしまうことが分かりました。