【Java】基于朴素贝叶斯算法破解基于哈希表的随机字符替换加密算法

【Java】基于朴素贝叶斯算法破解基于哈希表的随机字符替换加密算法
  不用看了,这篇文章是错的。得出的结果也不是正确的结果。我想错了,因为这个解密算法的前提是已经知道哈希表的情况下去计算的。而实际上应该是只靠统计去分析密文,所以实际破解所需要的密文字符数应该是要大于本文所计算出来的那张图

前言

  本文介绍了关于使用基于朴素贝叶斯算法训练模型破解随机字符替换加密算法,并通过解密后的数据统计对加密算法进行安全性分析。


摘要

  本文研究了基于哈希表的随机字符替换加密算法及其安全性问题,采用朴素贝叶斯算法对该加密算法进行破解,并对破解结果进行分析和讨论。研究结果表明,该加密算法的安全性不足,易被破解。本文提出了改进措施,提高了算法的安全性。

关键词:哈希表、随机字符替换加密、朴素贝叶斯算法、安全性


一、代码部分

代码部分如下所示:

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();
        }
    }
}

二、基于哈希表的随机字符替换加密算法

基于哈希表的随机字符替换加密算法的加密过程如下:

  1. 随机生成一个包含37个字符的字符集,其中包括26个英文字母、0-9共10个数字和一个空格字符;
  2. 对于每个字符,随机分配一个Unicode编码的字符,共分配1771个字符,将这些字符用哈希表进行存储;
  3. 需要加密时,将明文中的每个字符替换为哈希表中的其他字符,即随机抽取一个哈希表中的字符进行替换。

更多关于这个加密算法的内容可以参考我之前的一篇文章:

【Java】基于哈希表的随机字符替换加密算法 --by 何故不嗣音


三、基于朴素贝叶斯算法训练模型

  朴素贝叶斯分类是一种基于贝叶斯定理和特征条件独立假设的分类方法。它可以用来对输入数据进行分类。它的假设是特征之间相互独立,这样可以简化计算。

  本代码中,朴素贝叶斯算法用于加密字符串的解密。首先,使用随机函数生成一定数量的原字符串,并将其通过哈希表进行加密,生成密文对。然后,使用朴素贝叶斯训练出一个解密的模型。

模型训练过程:

  1. 遍历数据集中的每一对密文和原文,将原文每个字符作为标签,将密文每个字符作为特征,计算每个特征在各标签中出现的频率,并存储在特征及其标签的计数器中。

  2. 对每个标签,计算在训练集中出现的次数,并存储在标签计数器中。

  3. 使用贝叶斯公式计算每个特征对应每个标签的概率,并将计算结果存储在模型中。

  训练完成后,对输入的密文,可以使用模型计算每个特征对应的标签概率,然后取概率最大的标签作为解密结果。


四、朴素贝叶斯算法破解基于哈希表的随机字符替换加密算法

  1. 准备数据集:
      随机生成一定数量的字符串,然后通过哈希表进行随机字符替换,将得到的密文与原字符串构成对应关系,作为训练数据集使用。

  2. 构建模型:
      采用朴素贝叶斯算法构建训练模型,计算每个字符被替换成另一个字符的概率。

  3. 破解并计算准确率:
      重新随机生成足够数量的字符串,同样通过基于哈希表的随机字符替换加密算法进行加密,得到测试集。根据训练出来的模型,利用朴素贝叶斯算法对测试集密文进行破解,计算并统计模型准确率。


五、安全性分析

  代码通过朴素贝叶斯算法对其进行破解,并统计绘制出密文字符个数与准确率的关系曲线。如下图所示:

  从图中曲线可以看出,模型在大概截取60000左右的密文字符数时,能破解百分之60~70的密文。截取100000左右的密文字符数时,能破解百分之八十左右的密文。所以安全性方面较差。


六、可行的改进方法

这里给出两种可行的改进加密算法,以增强其安全性的方法:

  1. 增加字符集字符个数:原有的字符集大小为37,可以通过增加字符集的个数,以增加加密的安全性,但是这种方式需要考虑到是否使用某个字符,然后再加入到字符集中。如果明文中没有必要使用某字符的话,那么也不需要加入,所以我认为通过这种方式加强加密的安全性并不方便。

  2. 增加为每个字符分配的字符个数:原有的分配个数为1771,可以通过增加为每个字符分配的字符个数的方式以提高加密的安全性。并且这种增加可以是无上限的,也就是在分配的字符个数足够多的情况下,可以无限提高该加密方式的安全性。


七、总结

  本文研究了基于哈希表的随机字符替换加密算法及其安全性问题,采用朴素贝叶斯算法对该加密算法进行破解,并对破解结果进行分析和讨论。结果表明,该加密算法的安全性不足,易被破解。

猜你喜欢

转载自blog.csdn.net/weixin_57807777/article/details/129146843