狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

[轉帖]數(shù)據(jù)加密之加密算法

liguoquan
2023年6月2日 10:8 本文熱度 634
:數(shù)據(jù)加密之加密算法



encrypt-decrypt

1 密碼學基本概念

1.1 古典密碼學

  • 替換法

    替換法很好理解,就是用固定的信息將原文替換成無法直接閱讀的密文信息。例如將 b 替換成 w ,e 替換成p ,這樣bee 單詞就變換成了wpp,不知道替換規(guī)則的人就無法閱讀出原文的含義。

    替換法有單表替換和多表替換兩種形式。單表替換即只有一張原文密文對照表單,發(fā)送者和接收者用這張表單來加密解密。在上述例子中,表單即為:a b c d e - s w t r p 。

    多表替換即有多張原文密文對照表單,不同字母可以用不同表單的內容替換。

    例如約定好表單為:表單 1:abcde-swtrp 、表單2:abcde-chfhk 、表單 3:abcde-jftou

    規(guī)定第一個字母用第三張表單,第二個字母用第一張表單,第三個字母用第二張表單,這時 bee單詞就變成了(312)fpk ,破解難度更高,其中 312 又叫做密鑰,密鑰可以事先約定好,也可以在傳輸過程中標記出來。

  • 移位法

    移位法就是將原文中的所有字母都在字母表上向后(或向前)按照一個固定數(shù)目進行偏移后得出密文,典型的移位法應用有 “ 愷撒密碼 ”。

    例如約定好向后移動2位(abcde - cdefg),這樣 bee 單詞就變換成了dgg 。

    同理替換法,移位法也可以采用多表移位的方式,典型的多表案例是“維尼吉亞密碼”(又譯維熱納爾密碼),屬于多表密碼的一種形式。

古典密碼破解方式(頻率分析法):

古典密碼雖然很簡單,但是在密碼史上是使用的最久的加密方式,直到“概率論”的數(shù)學方法被發(fā)現(xiàn),古典密碼就被破解了。

將明文字母的出現(xiàn)頻率與密文字母的頻率相比較的過程。通過分析每個符號出現(xiàn)的頻率而輕易地破譯代換式密碼。在每種語言中,冗長的文章中的字母表現(xiàn)出一種可對之進行分辨的頻率。

分析方法:

英文單詞中字母出現(xiàn)的頻率是不同的,e以12.702%的百分比占比最高,z 只占到0.074%,感興趣的可以去百科查字母頻率詳細統(tǒng)計數(shù)據(jù)。如果密文數(shù)量足夠大,僅僅采用頻度分析法就可以破解單表的替換法或移位法。

多表的替換法或移位法雖然難度高一些,但如果數(shù)據(jù)量足夠大的話,也是可以破解的。以維尼吉亞密碼算法為例,破解方法就是先找出密文中完全相同的字母串,猜測密鑰長度,得到密鑰長度后再把同組的密文放在一起,使用頻率分析法破解。

1.2 近代密碼學

古典密碼的安全性受到了威脅,外加使用便利性較低,到了工業(yè)化時代,近現(xiàn)代密碼被廣泛應用。

恩尼格瑪機是二戰(zhàn)時期納粹德國使用的加密機器,后被英國破譯,參與破譯的人員有被稱為計算機科學之父、人工智能之父的圖靈。

恩尼格瑪機使用的加密方式本質上還是移位和替代,只不過因為密碼表種類極多,破解難度高,同時加密解密機器化,使用便捷,因而在二戰(zhàn)時期得以使用。

1.3 現(xiàn)代密碼學

  • 散列函數(shù)

    散列函數(shù),也見雜湊函數(shù)、摘要函數(shù)或哈希函數(shù),可將任意長度的消息經(jīng)過運算,變成固定長度數(shù)值,常見的有MD5SHA-1SHA256,多應用在文件校驗,數(shù)字簽名中。

    MD5 可以將任意長度的原文生成一個128位(16字節(jié))的哈希值

    SHA-1可以將任意長度的原文生成一個160位(20字節(jié))的哈希值

  • 對稱密碼

    對稱密碼應用了相同的加密密鑰和解密密鑰。

    對稱密碼分為:序列密碼(流密碼),分組密碼(塊密碼)兩種。

    流密碼是對信息流中的每一個元素(一個字母或一個比特)作為基本的處理單元進行加密,塊密碼是先對信息流分塊,再對每一塊分別加密。

    例如原文為1234567890,流加密即先對1進行加密,再對2進行加密,再對3進行加密……最后拼接成密文;

    塊加密先分成不同的塊,如1234成塊,5678成塊,90XX(XX為補位數(shù)字)成塊,再分別對不同塊進行加密,最后拼接成密文。前文提到的古典密碼學加密方法,都屬于流加密。

  • 非對稱密碼

    對稱密碼的密鑰安全極其重要,加密者和解密者需要提前協(xié)商密鑰,并各自確保密鑰的安全性,一但密鑰泄露,即使算法是安全的也無法保障原文信息的私密性。

    在實際的使用中,遠程的提前協(xié)商密鑰不容易實現(xiàn),即使協(xié)商好,在遠程傳輸過程中也容易被他人獲取,因此非對稱密鑰此時就凸顯出了優(yōu)勢。

    非對稱密碼有兩支密鑰,公鑰(publickey)和私鑰(privatekey),加密和解密運算使用的密鑰不同。

    用公鑰對原文進行加密后,需要由私鑰進行解密;用私鑰對原文進行加密后(此時一般稱為簽名),需要由公鑰進行解密(此時一般稱為驗簽)。

    公鑰可以公開的,大家使用公鑰對信息進行加密,再發(fā)送給私鑰的持有者,私鑰持有者使用私鑰對信息進行解密,獲得信息原文。因為私鑰只有單一人持有,因此不用擔心被他人解密獲取信息原文。

2 凱撒加密

2.1 凱撒加密解密的實現(xiàn)

凱撒密碼最早由古羅馬軍事統(tǒng)帥蓋烏斯·尤利烏斯·凱撒在軍隊中用來傳遞加密信息,故稱凱撒密碼。這是一種位移加密方式,只對26個字母進行位移替換加密,規(guī)則簡單,容易破解。下面是位移1次的對比:

在這里插入圖片描述

將明文字母表向后移動1位,A變成了B,B變成了C……,Z變成了A。同理,若將明文字母表向后移動3位:

在這里插入圖片描述

字母表最多可以移動25位。凱撒密碼的明文字母表向后或向前移動都是可以的,通常表述為向后移動,如果要向前移動1位,則等同于向后移動25位,位移選擇為25即可。

它是一種替換加密的技術,明文中的所有字母都在字母表上向后(或向前)按照一個固定數(shù)目進行偏移后被替換成密文。

簡單來說就是當秘鑰為n,其中一個待加密字符ch,加密之后的字符為ch+n,當ch+n超過’z’時,回到’a’計數(shù)。

凱撒加密解密工具類:

public class KaiSaUtil {
    // 加密
    public static String encryptKaiser(String original, Integer key) {
        // 1、將輸入的字符串轉換成字符數(shù)組
        char[] chars = original.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char aChar : chars) {
            // 2、獲取字符的ascii編碼
            int asciiCode = aChar;
            // 3、偏移數(shù)據(jù)
            asciiCode += key;
            // 4、將偏移后的數(shù)據(jù)轉為字符
            char result = (char) asciiCode;
            // 5、拼接數(shù)據(jù)
            sb.append(result);
        }
        return sb.toString();
    }
    // 解密
    public static String decryptKaiser(String encryptedData, int key) {
        // 1、將密文轉換成字符數(shù)組
        char[] chars = encryptedData.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char aChar : chars) {
            // 2、獲取字符的ascii編碼
            int asciiCode = aChar;
            // 3、偏移數(shù)據(jù)
            asciiCode -= key;
            // 4、將偏移后的數(shù)據(jù)轉為字符
            char result = (char) asciiCode;
            // 5、拼接數(shù)據(jù)
            sb.append(result);
        }
        return sb.toString();
    }}123456789101112131415161718192021222324252627282930313233343536

凱撒加密代碼測試:

public class KaiSaDemo {
    public static void main(String[] args) {
        String input = "welcome";
        Integer key = 3;
        System.out.println("加密前:" + input);
        String encryptKaiser = KaiSaUtil.encryptKaiser(input, key);
        System.out.println("加密后:" + encryptKaiser);
        String decryptKaiser = KaiSaUtil.decryptKaiser(encryptKaiser, key);
        System.out.println("解密后:" + decryptKaiser);
    }}測試結果:
	加密前:welcome
    加密后:zhofrph
    解密后:welcome123456789101112131415

2.2 使用頻度分析法破解凱撒加密

頻率分析法:

  • 將明文字母的出現(xiàn)頻率與密文字母的頻率相比較的過程

  • 通過分析每個符號出現(xiàn)的頻率而輕易地破譯代換式密碼

  • 在每種語言中,冗長的文章中的字母表現(xiàn)出一種可對之進行分辨的頻率。

  • e是英語中最常用的字母,其出現(xiàn)頻率為八分之一

實現(xiàn)步驟:

1、引入FrequencyUtil和FrequencyAnalysis兩個類,article.txt(隨便寫篇文章) 拷貝到項目文件夾的根目錄

2、運行 FrequencyAnalysis.java 用來統(tǒng)計每個字符出現(xiàn)的次數(shù)

3、運行 FrequencyAnalysis.java 里面 main 函數(shù)里面的 encryptFile 方法 對程序進行加密

public static void main(String[] args) throws Exception {
		//測試1,統(tǒng)計字符個數(shù)
		//printCharCount("article.txt");
		
		//加密文件
		int key = 3;
		encryptFile("article.txt", "article_en.txt", key);
		
		//讀取加密后的文件
	   // String artile = Util.file2String("article_en.txt");
	    //解密(會生成多個備選文件)
	   // decryptCaesarCode(artile, "article_de.txt");
	}12345678910111213

4、在根目錄會生成一個 article_en.txt 文件,然后我們統(tǒng)計這個文件當中每個字符出現(xiàn)的次數(shù)

public static void main(String[] args) throws Exception {
		//測試1,統(tǒng)計字符個數(shù)
		printCharCount("article_en.txt");
		
		//加密文件
		int key = 3;
		//encryptFile("article.txt", "article_en.txt", key);
		
		//讀取加密后的文件
	   // String artile = Util.file2String("article_en.txt");
	    //解密(會生成多個備選文件)
	   // decryptCaesarCode(artile, "article_de.txt");
	}12345678910111213

頻率分析工具類:

public class FrequencyUtil {
    public static void print(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            sb.append(bytes[i]).append(" ");
        }
        System.out.println(sb);
    }
    public static String file2String(String path) throws IOException {
        FileReader reader = new FileReader(new File(path));
        char[] buffer = new char[1024];
        int len = -1;
        StringBuffer sb = new StringBuffer();
        while ((len = reader.read(buffer)) != -1) {
            sb.append(buffer, 0, len);
        }
        return sb.toString();
    }
    public static void string2File(String data, String path) {
        FileWriter writer = null;
        try {
            writer = new FileWriter(new File(path));
            writer.write(data);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static String inputStream2String(InputStream in) throws IOException {
        int len = -1;
        byte[] buffer = new byte[1024];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while ((len = in.read(buffer)) != -1) {
            baos.write(buffer, 0, len);
        }
        baos.close();
        return baos.toString("UTF-8");
    }}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950

破解測試類:

public class FrequencyAnalysis {
    //英文里出現(xiàn)次數(shù)最多的字符
    private static final char MAGIC_CHAR = 'e';
    //破解生成的最大文件數(shù)
    private static final int DE_MAX_FILE = 4;
    public static void main(String[] args) throws Exception {
        //測試1,統(tǒng)計字符個數(shù)//        printCharCount("D:\\IDEA\\encrypt-decrypt\\src\\article.txt");
        //加密文件
        int key = 3;//        encryptFile("D:\\\\IDEA\\\\encrypt-decrypt\\\\src\\\\article.txt", "D:\\IDEA\\encrypt-decrypt\\src\\article_en.txt", key);
        //讀取加密后的文件
         String artile = FrequencyUtil.file2String("D:\\IDEA\\encrypt-decrypt\\src\\article_en.txt");
        //解密(會生成多個備選文件)
         decryptCaesarCode(artile, "article_de.txt");
    }
    public static void printCharCount(String path) throws IOException {
        String data = FrequencyUtil.file2String(path);
        List<Entry<Character, Integer>> mapList = getMaxCountChar(data);
        for (Entry<Character, Integer> entry : mapList) {
            //輸出前幾位的統(tǒng)計信息
            System.out.println("字符'" + entry.getKey() + "'出現(xiàn)" + entry.getValue() + "次");
        }
    }
    public static void encryptFile(String srcFile, String destFile, int key) throws IOException {
        String artile = FrequencyUtil.file2String(srcFile);
        //加密文件
        String encryptData = KaiSaUtil.encryptKaiser(artile, key);
        //保存加密后的文件
        FrequencyUtil.string2File(encryptData, destFile);
    }
    /**
     * 破解凱撒密碼
     *
     * @param input 數(shù)據(jù)源
     * @return 返回解密后的數(shù)據(jù)
     */
    public static void decryptCaesarCode(String input, String destPath) {
        int deCount = 0;//當前解密生成的備選文件數(shù)
        //獲取出現(xiàn)頻率最高的字符信息(出現(xiàn)次數(shù)越多越靠前)
        List<Entry<Character, Integer>> mapList = getMaxCountChar(input);
        for (Entry<Character, Integer> entry : mapList) {
            //限制解密文件備選數(shù)
            if (deCount >= DE_MAX_FILE) {
                break;
            }
            //輸出前幾位的統(tǒng)計信息
            System.out.println("字符'" + entry.getKey() + "'出現(xiàn)" + entry.getValue() + "次");
            ++deCount;
            //出現(xiàn)次數(shù)最高的字符跟MAGIC_CHAR的偏移量即為秘鑰
            int key = entry.getKey() - MAGIC_CHAR;
            System.out.println("猜測key = " + key + ", 解密生成第" + deCount + "個備選文件" + "\n");
            String decrypt = KaiSaUtil.decryptKaiser(input, key);
            String fileName = "de_" + deCount + destPath;
            FrequencyUtil.string2File(decrypt, fileName);
        }
    }
    //統(tǒng)計String里出現(xiàn)最多的字符
    public static List<Entry<Character, Integer>> getMaxCountChar(String data) {
        Map<Character, Integer> map = new HashMap<Character, Integer>();
        char[] array = data.toCharArray();
        for (char c : array) {
            if (!map.containsKey(c)) {
                map.put(c, 1);
            } else {
                Integer count = map.get(c);
                map.put(c, count + 1);
            }
        }
        //輸出統(tǒng)計信息
		for (Entry<Character, Integer> entry : map.entrySet()) {
			System.out.println(entry.getKey() + "出現(xiàn)" + entry.getValue() +  "次");
		}
        //獲取獲取最大值
        int maxCount = 0;
        for (Entry<Character, Integer> entry : map.entrySet()) {
            //不統(tǒng)計空格
            if (/*entry.getKey() != ' ' && */entry.getValue() > maxCount) {
                maxCount = entry.getValue();
            }
        }
        //map轉換成list便于排序
        List<Entry<Character, Integer>> mapList = new ArrayList<Map.Entry<Character, Integer>>(map.entrySet());
        //根據(jù)字符出現(xiàn)次數(shù)排序
        Collections.sort(mapList, new Comparator<Entry<Character, Integer>>() {
            public int compare(Entry<Character, Integer> o1,
                               Entry<Character, Integer> o2) {
                return o2.getValue().compareTo(o1.getValue());
            }
        });
        return mapList;
    }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106

3 常見加密方式

3.1 對稱加密

采用單鑰密碼系統(tǒng)的加密方法,同一個密鑰可以同時用作信息的加密和解密,這種加密方法稱為對稱加密,也稱為單密鑰加密。

示例 :

我們現(xiàn)在有一個原文3要發(fā)送給B

設置密鑰為108, 3 * 108 = 324, 將324作為密文發(fā)送給B

B拿到密文324后, 使用324/108 = 3 得到原文

常見加密算法:

  • DES : Data Encryption Standard,即數(shù)據(jù)加密標準,是一種使用密鑰加密的塊算法,1977年被美國聯(lián)邦政府的國家標準局確定為聯(lián)邦資料處理標準(FIPS),并授權在非密級政府通信中使用,隨后該算法在國際上廣泛流傳開來。

  • AES : Advanced Encryption Standard, 高級加密標準 .在密碼學中又稱Rijndael加密法,是美國聯(lián)邦政府采用的一種區(qū)塊加密標準。這個標準用來替代原先的DES,已經(jīng)被多方分析且廣為全世界所使用。

特點:

  • 加密速度快, 可以加密大文件

  • 密文可逆, 一旦密鑰文件泄漏, 就會導致數(shù)據(jù)暴露

  • 加密后編碼表找不到對應字符, 出現(xiàn)亂碼

  • 一般結合Base64使用

3.2 DES加密解密

加密示例代碼:

public class DesAesDemo {
    public static void main(String[] args) throws Exception {
        // 原文
        String input = "hello";
        // des加密必須是8位
        String key = "12345678";
        // 算法
        String algorithm = "DES";
        // 加密類型
        String transformation = "DES";
        // Cipher:密碼,獲取加密對象
        // transformation:參數(shù)表示使用什么類型加密
        Cipher cipher = Cipher.getInstance(transformation);
        // 指定秘鑰規(guī)則
        // 第一個參數(shù)表示:密鑰,key的字節(jié)數(shù)組
        // 第二個參數(shù)表示:算法
        SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
        // 對加密進行初始化
        // 第一個參數(shù):表示模式,有加密模式和解密模式
        // 第二個參數(shù):表示秘鑰規(guī)則
        cipher.init(Cipher.ENCRYPT_MODE, sks);
        // 進行加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        // 打印字節(jié),因為ascii碼有負數(shù),解析不出來,所以亂碼
        for (byte b : bytes) {
            System.out.println(b);
        }
        // 打印密文
        System.out.println(new String(bytes));
    }}測試結果:	-70
    22
    -58
    -96
    37
    113
    37
    -81
    �Ơ%q%�1234567891011121314151617181920212223242526272829303132333435363738394041

注意:

1、DES加密算法規(guī)定,密鑰key必須是8個字節(jié)

2、出現(xiàn)亂碼是因為對應的字節(jié)出現(xiàn)負數(shù),但負數(shù),沒有出現(xiàn)在 ascii 碼表里面,所以出現(xiàn)亂碼,需要配合base64進行轉碼

3、base64 導包的時候,需要注意 ,別導錯了,需要導入 apache 包

轉碼代碼:
String encode = Base64.encode(bytes);
測試結果:
	base64轉碼前:�i,6�M
    base64轉碼后:2GksNrsVEk0=123456

DES解密示例代碼:

    // 解密方法:
    public static String decryptDES(String input, String key, String transformation, String algorithm) throws Exception {
        // 1,獲取Cipher對象
        Cipher cipher = Cipher.getInstance(transformation);
        // 指定密鑰規(guī)則
        SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
        cipher.init(Cipher.DECRYPT_MODE, sks);
        // 3. 解密,上面使用的base64編碼,下面直接用密文
        byte[] bytes = cipher.doFinal(Base64.decode(input));
        //  因為是明文,所以直接返回
        return new String(bytes);
    }123456789101112

3.3 base64

Base64 算法簡介:

Base64是網(wǎng)絡上最常見的用于傳輸8Bit字節(jié)碼的可讀性編碼算法之一
可讀性編碼算法不是為了保護數(shù)據(jù)的安全性,而是為了可讀性
可讀性編碼不改變信息內容,只改變信息內容的表現(xiàn)形式
所謂Base64,即是說在編碼過程中使用了64種字符:大寫A到Z、小寫a到z、數(shù)字0到9、“+”和“/”
Base58是Bitcoin(比特幣)中使用的一種編碼方式,主要用于產生Bitcoin的錢包地址
相比Base64,Base58不使用數(shù)字"0",字母大寫"O",字母大寫"I",和字母小寫"i",以及"+"和"/"符號123456

Base64 算法原理:

base64 是 3個字節(jié)為一組,一個字節(jié) 8位,一共 就是24位 ,然后,把3個字節(jié)轉成4組,每組6位

3 * 8 = 4 * 6 = 24 ,每組6位,缺少的2位,會在高位進行補0 ,這樣做的好處在于 ,base取的是后面6位,去掉高2位 ,那么base64的取值就可以控制在0-63位了,所以就叫base64

base64 構成原則:

  • 小寫 a - z = 26個字母

  • 大寫 A - Z = 26個字母

  • 數(shù)字 0 - 9 = 10 個數(shù)字

  • + / = 2個符號

  • 大家可能發(fā)現(xiàn)一個問題,咱們的base64有個 = 號,但是在映射表里面沒有發(fā)現(xiàn) = 號 , 這個地方需要注意,等號非常特殊,因為base64是三個字節(jié)一組 ,如果當我們的位數(shù)不夠的時候,會使用等號來補齊
    在這里插入圖片描述

base64補等號測試:

public class TestBase64 {
    public static void main(String[] args) {
        //  1:MQ== 表示一個字節(jié),不夠三個字節(jié),所以需要后面通過 == 號補齊
        System.out.println(Base64.encode("1".getBytes()));//        System.out.println(Base64.encode("12".getBytes()));//        System.out.println(Base64.encode("123".getBytes()));//        // 你好:中文占6個字節(jié),6 * 8 = 48 ,剛剛好被整除,所以沒有等號//        System.out.println(Base64.encode("你好".getBytes()));
    }}測試結果:
	MQ==
	MTI=
	MTIZ123456789101112131415

3.4 AES加密解密

AES 加密解密和 DES 加密解密代碼一樣,只需要修改加密算法就行,拷貝 ESC 代碼

AES加密解密工具類:

public class AESUtil {
    // 加密方法
    public static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {
        // 獲取加密對象
        Cipher cipher = Cipher.getInstance(transformation);
        // 創(chuàng)建加密規(guī)則
        // 第一個參數(shù)key的字節(jié)
        // 第二個參數(shù)表示加密算法
        SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
        // ENCRYPT_MODE:加密模式
        // DECRYPT_MODE: 解密模式
        // 初始化加密模式和算法
        cipher.init(Cipher.ENCRYPT_MODE,sks);
        // 加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        // 輸出加密后的數(shù)據(jù)
        String encode = Base64.encode(bytes);
        return encode;
    }
    // 解密方法:
    public static String decryptDES(String input, String key, String transformation, String algorithm) throws Exception {
        // 1,獲取Cipher對象
        Cipher cipher = Cipher.getInstance(transformation);
        // 指定密鑰規(guī)則
        SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
        cipher.init(Cipher.DECRYPT_MODE, sks);
        // 3. 解密,上面使用的base64編碼,下面直接用密文
        byte[] bytes = cipher.doFinal(Base64.decode(input));
        //  因為是明文,所以直接返回
        return new String(bytes);
    }}1234567891011121314151617181920212223242526272829303132333435

測試類:

public class AESDemo {
    public static void main(String[] args) throws Exception {
        String input = "原文";
        // DES加密算法,key的大小必須是8個字節(jié)
        String key = "1234567812345678";
        String transformation = "AES";
        // 指定獲取密鑰的算法
        String algorithm = "AES";
        String encryptDES = AESUtil.encryptDES(input, key, transformation, algorithm);
        System.out.println("加密:" + encryptDES);
        String s = AESUtil.decryptDES(encryptDES, key, transformation, algorithm);
        System.out.println("解密:" + s);
    }}測試結果:
	加密:TUuxDy0h/Rx1KwhMFXEnog==
    解密:原文1234567891011121314151617

注意:

1、AES加密key必須是16個字節(jié)

3.5 toString()與new String ()用法區(qū)別

str.toString是調用了這個object對象的類的toString方法。一般是返回這么一個String:[class name]@[hashCode]

new String(str)是根據(jù)parameter是一個字節(jié)數(shù)組,使用java虛擬機默認的編碼格式,將這個字節(jié)數(shù)組decode為對應的字符。若虛擬機默認的編碼格式是ISO-8859-1,按照ascii編碼表即可得到字節(jié)對應的字符。

new String()一般使用字符轉碼的時候,byte[]數(shù)組的時候

toString()對象打印的時候使用

4 加密模式

加密模式:https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html

ECB: Electronic codebook, 電子密碼本. 需要加密的消息按照塊密碼的塊大小被分為數(shù)個塊,并對每個塊進行獨立加密

在這里插入圖片描述

  • 優(yōu)點 : 可以并行處理數(shù)據(jù)

  • 缺點 : 同樣的原文生成同樣的密文, 不能很好的保護數(shù)據(jù)。

  • 同時加密,原文是一樣的,加密出來的密文也是一樣的。

CBC : Cipher-block chaining, 密碼塊鏈接. 每個明文塊先與前一個密文塊進行異或后,再進行加密。在這種方法中,每個密文塊都依賴于它前面的所有明文塊

在這里插入圖片描述

  • 優(yōu)點 : 同樣的原文生成的密文不一樣。

  • 缺點 : 串行處理數(shù)據(jù)。加密比較慢

5 填充模式

當需要按塊處理的數(shù)據(jù), 數(shù)據(jù)長度不符合塊處理需求時, 按照一定的方法填充滿塊長的規(guī)則

NoPadding:

  • 不填充

  • 在DES加密算法下, 要求原文長度必須是8byte的整數(shù)倍

  • 在AES加密算法下, 要求原文長度必須是16byte的整數(shù)倍

PKCS5Pading:

數(shù)據(jù)塊的大小為8位, 不夠就補足

Tips:

默認情況下, 加密模式和填充模式為 : ECB/PKCS5Padding

如果使用CBC模式, 在初始化Cipher對象時, 需要增加參數(shù), 初始化向量IV : IvParameterSpec iv = new IvParameterSpec(key.getBytes());

加密模式和填充模式:

AES/CBC/NoPadding (128)AES/CBC/PKCS5Padding (128)AES/ECB/NoPadding (128)AES/ECB/PKCS5Padding (128)DES/CBC/NoPadding (56)DES/CBC/PKCS5Padding (56)DES/ECB/NoPadding (56)DES/ECB/PKCS5Padding (56)DESede/CBC/NoPadding (168)DESede/CBC/PKCS5Padding (168)DESede/ECB/NoPadding (168)DESede/ECB/PKCS5Padding (168)RSA/ECB/PKCS1Padding (1024, 2048)RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048)RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)123456789101112131415

6 消息摘要

什么是信息摘要:

  • 消息摘要(Message Digest)又稱為數(shù)字摘要(Digital Digest)

  • 是一個唯一對應一個消息或文本的固定長度的值,它由一個單向Hash加密函數(shù)對消息進行作用而產生

  • 使用數(shù)字摘要生成的值是不可以篡改的,為了保證文件或者值的安全

特點:

無論輸入的消息有多長,計算出來的消息摘要的長度總是固定的。例如應用MD5算法摘要的消息有128個比特位,用SHA-1算法摘要的消息最終有160比特位的輸出

只要輸入的消息不同,對其進行摘要以后產生的摘要消息也必不相同;但相同的輸入必會產生相同的輸出

消息摘要是單向、不可逆的

常見算法 :

- MD5- SHA1- SHA256- SHA5121234

百度搜索 tomcat ,進入官網(wǎng)下載 ,會經(jīng)常發(fā)現(xiàn)有 sha1sha512 , 這些都是數(shù)字摘要

在這里插入圖片描述

數(shù)字摘要:

bd465ea30ee7e0a66ed67e86d45a53aa5aba0c8d190934e7dfa58294a21ada7b967877d848e1836a19bf01437cab64f275ac827d81b3f3253eb961b60361a045 *apache-tomcat-10.0.2.exe1

獲取字符串信息摘要:

// 消息摘要算法,為了防止篡改public class DigestDemo1 {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        // 原文
        String input = "aa";
        // 算法
        String algorithm = "MD5";
        // 獲取數(shù)字摘要對象
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 獲取消息數(shù)字摘要的字節(jié)數(shù)組
        byte[] digest = messageDigest.digest(input.getBytes());
        System.out.println(new String(digest));
        // 結合base64解決轉碼
        System.out.println(Base64.encode(digest));
    }}測試結果:
	A$�
    �5�o$� zI
    轉碼后:QSS8CpM1wn8IbyS6IHpJEg==123456789101112131415161718192021

數(shù)字摘要轉換成 16 進制

// 4124bc0a9335c27f086f24ba207a4912     md5 在線校驗
// QSS8CpM1wn8IbyS6IHpJEg==             消息摘要使用的是16進制12

代碼轉成16進制

// 消息摘要算法,為了防止篡改public class DigestDemo1 {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        // 原文
        String input = "aa";
        // 算法
        String algorithm = "MD5";
        // 獲取數(shù)字摘要對象
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 獲取消息數(shù)字摘要的字節(jié)數(shù)組
        byte[] digest = messageDigest.digest(input.getBytes());
        // 創(chuàng)建對象用來拼接
        StringBuilder sb = new StringBuilder();
        // 對密文進行迭代
        for (byte b : digest) {
            // 轉成 16進制
            String s = Integer.toHexString(b & 0xff);//            System.out.println(s);
            if (s.length() == 1){
                // 如果生成的字符只有一個,前面補0
                s = "0"+s;
            }
            sb.append(s);
        }
        System.out.println(sb.toString());
    }}12345678910111213141516171819202122232425262728

其他數(shù)字摘要算法

public class DigestDemo12 {
    public static void main(String[] args) throws Exception {
        // 原文
        String input = "aa";
        // 算法
        String algorithm = "MD5";
        // 獲取數(shù)字摘要對象
        String md5 = getDigest(input, "MD5");
        System.out.println(md5);
        String sha1 = getDigest(input, "SHA-1");
        System.out.println(sha1);
        String sha256 = getDigest(input, "SHA-256");
        System.out.println(sha256);
        String sha512 = getDigest(input, "SHA-512");
        System.out.println(sha512);
    }
    private static String toHex(byte[] digest) throws Exception {//        System.out.println(new String(digest));
        // base64編碼//        System.out.println(Base64.encode(digest));
        // 創(chuàng)建對象用來拼接
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) {
            // 轉成 16進制
            String s = Integer.toHexString(b & 0xff);
            if (s.length() == 1) {
                // 如果生成的字符只有一個,前面補0
                s = "0" + s;
            }
            sb.append(s);
        }
        System.out.println("16進制數(shù)據(jù)的長度:" + sb.toString().getBytes().length);
        return sb.toString();
    }
    private static String getDigest(String input, String algorithm) throws Exception {
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 消息數(shù)字摘要
        byte[] digest = messageDigest.digest(input.getBytes());
        System.out.println("密文的字節(jié)長度:" + digest.length);
        return toHex(digest);
    }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051

獲取文件信息摘要

public class DigestDemo {
    public static void main(String[] args) throws Exception {
        String input = "aa";
        String algorithm = "MD5";
        // sha1 可以實現(xiàn)秒傳功能
        String sha1 = getDigestFile("D:\\apache-tomcat-9.0.41.zip", "SHA-1");
        System.out.println(sha1);
        String sha512 = getDigestFile("D:\\apache-tomcat-9.0.41.zip", "SHA-512");
        System.out.println(sha512);
        String md5 = getDigest("aa", "MD5");
        System.out.println(md5);
        String md51 = getDigest("aa ", "MD5");
        System.out.println(md51);
    }
    private static String getDigestFile(String filePath, String algorithm) throws Exception {
        FileInputStream fis = new FileInputStream(filePath);
        int len;
        byte[] buffer = new byte[1024];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while ((len = fis.read(buffer)) != -1) {
            baos.write(buffer, 0, len);
        }
        // 獲取消息摘要對象
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 獲取消息摘要
        byte[] digest = messageDigest.digest(baos.toByteArray());
        System.out.println("密文的字節(jié)長度:" + digest.length);
        return toHex(digest);
    }
    private static String getDigest(String input, String algorithm) throws Exception {
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        byte[] digest = messageDigest.digest(input.getBytes());
        System.out.println("密文的字節(jié)長度:" + digest.length);
        return toHex(digest);
    }
    private static String toHex(byte[] digest) {
        //        System.out.println(new String(digest));
        // 消息摘要進行表示的時候,是用16進制進行表示
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) {
            // 轉成16進制
            String s = Integer.toHexString(b & 0xff);
            // 保持數(shù)據(jù)的完整性,前面不夠的用0補齊
            if (s.length() == 1) {
                s = "0" + s;
            }
            sb.append(s);
        }
        System.out.println("16進制數(shù)據(jù)的長度:" + sb.toString().getBytes().length);
        return sb.toString();
    }}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061

運行程序 ,獲取 sha-1 和 sha-512 的值

在這里插入圖片描述

總結:

  • MD5算法 : 摘要結果16個字節(jié), 轉16進制后32個字節(jié)

  • SHA1算法 : 摘要結果20個字節(jié), 轉16進制后40個字節(jié)

  • SHA256算法 : 摘要結果32個字節(jié), 轉16進制后64個字節(jié)

  • SHA512算法 : 摘要結果64個字節(jié), 轉16進制后128個字節(jié)

7 非對稱加密

簡介:

  • 非對稱加密算法又稱現(xiàn)代加密算法

  • 非對稱加密是計算機通信安全的基石,保證了加密數(shù)據(jù)不會被破解

  • 與對稱加密算法不同,非對稱加密算法需要兩個密鑰:公開密鑰(publickey) 和私有密(privatekey)

  • 公開密鑰和私有密鑰是一對

  • 如果用公開密鑰對數(shù)據(jù)進行加密,只有用對應的私有密鑰才能解密

  • 如果用私有密鑰對數(shù)據(jù)進行加密,只有用對應的公開密鑰才能解密

  • 因為加密和解密使用的是兩個不同的密鑰,所以這種算法叫作非對稱加密算法

示例:

  • 首先生成密鑰對, 公鑰為(5,14), 私鑰為(11,14)

  • 現(xiàn)在A希望將原文2發(fā)送給B

  • A使用公鑰加密數(shù)據(jù). 2的5次方mod 14 = 4 , 將密文4發(fā)送給B

  • B使用私鑰解密數(shù)據(jù). 4的11次方mod14 = 2, 得到原文2

特點:

  • 加密和解密使用不同的密鑰

  • 如果使用私鑰加密, 只能使用公鑰解密

  • 如果使用公鑰加密, 只能使用私鑰解密

  • 處理數(shù)據(jù)的速度較慢, 因為安全級別高

常見算法:

  • RSA

  • ECC

7.1 生成公鑰和私鑰

public class RSAdemo {
    public static void main(String[] args) throws Exception {
        // 加密算法
        String algorithm = "RSA";
        //  創(chuàng)建密鑰對生成器對象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 生成密鑰對
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 生成私鑰
        PrivateKey privateKey = keyPair.getPrivate();
        // 生成公鑰
        PublicKey publicKey = keyPair.getPublic();
        // 獲取私鑰字節(jié)數(shù)組
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 獲取公鑰字節(jié)數(shù)組
        byte[] publicKeyEncoded = publicKey.getEncoded();
        // 對公私鑰進行base64編碼
        String privateKeyString = Base64.encode(privateKeyEncoded);
        String publicKeyString = Base64.encode(publicKeyEncoded);
        // 打印私鑰
        System.out.println(privateKeyString);
        // 打印公鑰
        System.out.println(publicKeyString);
    }}12345678910111213141516171819202122232425

7.2 私鑰加密

// 私鑰加密public class RSAdemo {
    public static void main(String[] args) throws Exception {
        String input = "hello";
        // 加密算法
        String algorithm = "RSA";
        //  創(chuàng)建密鑰對生成器對象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 生成密鑰對
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 生成私鑰
        PrivateKey privateKey = keyPair.getPrivate();
        // 生成公鑰
        PublicKey publicKey = keyPair.getPublic();
        // 獲取私鑰字節(jié)數(shù)組
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 獲取公鑰字節(jié)數(shù)組
        byte[] publicKeyEncoded = publicKey.getEncoded();
        // 對公私鑰進行base64編碼
        String privateKeyString = Base64.encode(privateKeyEncoded);
        String publicKeyString = Base64.encode(publicKeyEncoded);
        // 創(chuàng)建加密對象
        // 參數(shù)表示加密算法
        Cipher cipher = Cipher.getInstance(algorithm);
        // 初始化加密
        // 第一個參數(shù):加密的模式
        // 第二個參數(shù):使用私鑰進行加密
        cipher.init(Cipher.ENCRYPT_MODE,privateKey);
        // 私鑰加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        System.out.println(Base64.encode(bytes));
    }}123456789101112131415161718192021222324252627282930313233

7.3 私鑰加密私鑰解密

public class RSAdemo2 {
    public static void main(String[] args) throws Exception {
        String input = "你好";
        // 加密算法
        String algorithm = "RSA";
        //  創(chuàng)建密鑰對生成器對象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 生成密鑰對
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 生成私鑰
        PrivateKey privateKey = keyPair.getPrivate();
        // 生成公鑰
        PublicKey publicKey = keyPair.getPublic();
        // 獲取私鑰字節(jié)數(shù)組
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 獲取公鑰字節(jié)數(shù)組
        byte[] publicKeyEncoded = publicKey.getEncoded();
        // 對公私鑰進行base64編碼
        String privateKeyString = Base64.encode(privateKeyEncoded);
        String publicKeyString = Base64.encode(publicKeyEncoded);
        // 創(chuàng)建加密對象
        // 參數(shù)表示加密算法
        Cipher cipher = Cipher.getInstance(algorithm);
        // 初始化加密
        // 第一個參數(shù):加密的模式
        // 第二個參數(shù):使用私鑰進行加密
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        // 私鑰加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        System.out.println("加密后:"+Base64.encode(bytes));
        // 私鑰進行解密
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        // 對密文進行解密,不需要使用base64,因為原文不會亂碼
        byte[] bytes1 = cipher.doFinal(bytes);
        System.out.println("解密后:"+new String(bytes1));
    }}12345678910111213141516171819202122232425262728293031323334353637383940

運行程序 ,因為私鑰加密,只能公鑰解密:

在這里插入圖片描述

7.4 私鑰加密公鑰解密

public class RSAdemo2 {
    public static void main(String[] args) throws Exception {
        String input = "你好";
        // 加密算法
        String algorithm = "RSA";
        //  創(chuàng)建密鑰對生成器對象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 生成密鑰對
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 生成私鑰
        PrivateKey privateKey = keyPair.getPrivate();
        // 生成公鑰
        PublicKey publicKey = keyPair.getPublic();
        // 獲取私鑰字節(jié)數(shù)組
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 獲取公鑰字節(jié)數(shù)組
        byte[] publicKeyEncoded = publicKey.getEncoded();
        // 對公私鑰進行base64編碼
        String privateKeyString = Base64.encode(privateKeyEncoded);
        String publicKeyString = Base64.encode(publicKeyEncoded);
        // 創(chuàng)建加密對象
        // 參數(shù)表示加密算法
        Cipher cipher = Cipher.getInstance(algorithm);
        // 初始化加密
        // 第一個參數(shù):加密的模式
        // 第二個參數(shù):使用私鑰進行加密
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        // 私鑰加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        System.out.println("加密后:"+Base64.encode(bytes));
        // 公鑰進行解密
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        // 對密文進行解密,不需要使用base64,因為原文不會亂碼
        byte[] bytes1 = cipher.doFinal(bytes);
        System.out.println("解密后:"+new String(bytes1));
    }}12345678910111213141516171819202122232425262728293031323334353637383940

7.5 保存公鑰和私鑰

private static void generateKeyToFile(String algorithm, String pubPath, String priPath) throws Exception {
        // 獲取密鑰對生成器
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 獲取密鑰對
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 獲取公鑰
        PublicKey publicKey = keyPair.getPublic();
        // 獲取私鑰
        PrivateKey privateKey = keyPair.getPrivate();
        // 獲取byte數(shù)組
        byte[] publicKeyEncoded = publicKey.getEncoded();
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 進行Base64編碼
        String publicKeyString = Base64.encode(publicKeyEncoded);
        String privateKeyString = Base64.encode(privateKeyEncoded);
        // 保存文件
        FileUtils.writeStringToFile(new File(pubPath), publicKeyString, Charset.forName("UTF-8"));
        FileUtils.writeStringToFile(new File(priPath), privateKeyString, Charset.forName("UTF-8"));
    }12345678910111213141516171819

7.6 讀取私鑰

public static PrivateKey getPrivateKey(String priPath, String algorithm) throws Exception {
        // 將文件內容轉為字符串
        String privateKeyString = FileUtils.readFileToString(new File(priPath), Charset.defaultCharset());
        // 輸出私鑰
        System.out.println(privateKeyString);
        // 獲取密鑰工廠
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        // 構建密鑰規(guī)范 進行Base64解碼
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyString));
        // 生成私鑰
        return keyFactory.generatePrivate(spec);
    }123456789101112

7.7 讀取公鑰

 public static PublicKey getPublicKey(String pulickPath, String algorithm) throws Exception {
        // 將文件內容轉為字符串
        String publicKeyString = FileUtils.readFileToString(new File(pulickPath), Charset.defaultCharset());
        System.out.println(publicKeyString);
        // 獲取密鑰工廠
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        // 構建密鑰規(guī)范 進行Base64解碼
        X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decode(publicKeyString));
        // 生成公鑰
        return keyFactory.generatePublic(spec);
    }123456789101112

7.8 RSA工具類

  • 導入依賴

<dependency>
   <groupId>commons-io</groupId>
   <artifactId>commons-io</artifactId>
   <version>2.6</version></dependency>12345
  • 工具類

public class RSAUtil {
    private static final String KEY_ALGORITHM = "RSA";
    private static KeyPairGenerator keyPairGenerator;
    // 為每一個字段生成公私密鑰
    public static void makeRsaKeys(String pubPath, String priPath, String user) throws Exception {
        try {
            keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        //隨機數(shù)生成器
        SecureRandom random = new SecureRandom();
        //設置KEY_SIZE位長的秘鑰
        int KEY_SIZE = 1024;
        keyPairGenerator.initialize(KEY_SIZE, random);
        //開始創(chuàng)建
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();//公鑰
        PrivateKey privateKey = keyPair.getPrivate();//私鑰
        //使用Base64進行轉碼
        String publicKeyStr = Base64.encode(publicKey.getEncoded());
        String privateKeyStr = Base64.encode(privateKey.getEncoded());
        // 保存文件
        FileUtils.writeStringToFile(new File(pubPath), publicKeyStr, Charset.forName("UTF-8"));
        FileUtils.writeStringToFile(new File(priPath), privateKeyStr, Charset.forName("UTF-8"));
    }
    // 私鑰加密
    public static String privateKeyDecode(String data, int mode, String priPath) throws Exception {
        // 將文件內容轉為字符串
        String privateKeyString = FileUtils.readFileToString(new File(priPath), Charset.defaultCharset());
        // 輸出私鑰//        System.out.println(privateKeyString);
        // 獲取密鑰工廠
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        // 構建密鑰規(guī)范 進行Base64解碼
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyString));
        // 生成私鑰
        PrivateKey privateKey = keyFactory.generatePrivate(spec);
        try {
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            if (mode == Cipher.ENCRYPT_MODE) {//加密
                cipher.init(mode, privateKey);
                return Base64.encode(cipher.doFinal(data.getBytes()));
            } else if (mode == Cipher.DECRYPT_MODE) {//解密
                cipher.init(mode, privateKey);
                return new String(cipher.doFinal(Base64.decode(data)), StandardCharsets.UTF_8);
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    // 公鑰解密
    public static String publicKeyDecode(String data, String pulickPath, int mode) throws Exception {
        // 將文件內容轉為字符串
        String publicKeyString = FileUtils.readFileToString(new File(pulickPath), Charset.defaultCharset());//        System.out.println(publicKeyString);
        // 獲取密鑰工廠
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        // 構建密鑰規(guī)范 進行Base64解碼
        X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decode(publicKeyString));
        // 生成公鑰
        PublicKey publicKey = keyFactory.generatePublic(spec);
        try {
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            if (mode == Cipher.ENCRYPT_MODE) {//加密
                cipher.init(mode, publicKey);
                return Base64.encode(cipher.doFinal(data.getBytes()));
            } else if (mode == Cipher.DECRYPT_MODE) {//解密
                cipher.init(mode, publicKey);
                byte[] bytes = cipher.doFinal(Base64.decode(data));
                return new String(bytes, StandardCharsets.UTF_8);
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    public static void main(String[] args) throws Exception {
        String user = "root";
        // 公鑰路徑
        String pubPath = "D:\\IDEA\\encrypt-decrypt\\src\\main\\java\\com\\zwh\\key\\" + user + ".txt";
        // 私鑰路徑
        String priPath = "D:\\IDEA\\encrypt-decrypt\\src\\main\\java\\com\\zwh\\key\\" + user + "_en.txt";
        makeRsaKeys(pubPath, priPath, user);
        String str = "123456";
        System.out.println("明文:" + str);
        System.out.println("---------私鑰加密,公鑰解密-----------");
        String privateKeyDecode = privateKeyDecode(str, 1, priPath);
        System.out.println("私鑰加密:" + privateKeyDecode);
        String publicKeyDecode = publicKeyDecode(privateKeyDecode, pubPath, 2);
        System.out.println("公鑰解密:" + publicKeyDecode);
    }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102

8 數(shù)字簽名

基本介紹:

數(shù)字簽名(又稱公鑰數(shù)字簽名)是只有信息的發(fā)送者才能產生的別人無法偽造的一段數(shù)字串,這段數(shù)字串同時也是對信息的發(fā)送者發(fā)送信息真實性的一個有效證

明。它是一種類似寫在紙上的普通的物理簽名,但是使用了公鑰加密領域的技術來實現(xiàn)的,用于鑒別數(shù)字信息的方法。一套數(shù)字簽名通常定義兩種互補的運算,一

個用于簽名,另一個用于驗證。數(shù)字簽名是非對稱密鑰加密技術數(shù)字摘要技術的應用。

簡單認識:

相信我們都寫過信,在寫信的時候落款處總是要留下自己的名字,用來表示寫信的人是誰。我們簽的這個字就是生活中的簽名。

而數(shù)字簽名呢?其實也是同樣的道理,他的含義是:在網(wǎng)絡中傳輸數(shù)據(jù)時候,給數(shù)據(jù)添加一個數(shù)字簽名,表示是誰發(fā)的數(shù)據(jù),而且還能證明數(shù)據(jù)沒有被篡改。

OK,數(shù)字簽名的主要作用就是保證了數(shù)據(jù)的有效性(驗證是誰發(fā)的)和完整性(證明信息沒有被篡改)。下面我們就來好好地看一下他的底層實現(xiàn)原理是什么樣子的。

基本原理:

為了理解得清楚,我們通過案例一步一步來講解。話說張三有倆好哥們A、B。由于工作原因,張三和AB寫郵件的時候為了安全都需要加密。于是張三想到了數(shù)字簽名:

整個思路是這個樣子的:

第一步:加密采用非對稱加密,張三有三把鑰匙,兩把公鑰,送給朋友。一把私鑰留給自己。

第二步:A或者B寫郵件給張三:A先用公鑰對郵件加密,然后張三收到郵件之后使用私鑰解密。

第三步:張三寫郵件給A或者B:

(1)張三寫完郵件,先用hash函數(shù)生成郵件的摘要,附著在文章上面,這就完成了數(shù)字簽名,然后張三再使用私鑰加密。就可以把郵件發(fā)出去了。

(2)A或者是B收到郵件之后,先把數(shù)字簽名取下來,然后使用自己的公鑰解密即可。這時候取下來的數(shù)字簽名中的摘要若和張三的一致,那就認為是張三發(fā)來的,再對信件本身使用Hash函數(shù),將得到的結果,與上一步得到的摘要進行對比。如果兩者一致,就證明這封信未被修改過。

數(shù)字證書:

上面提到我們對簽名進行驗證時,需要用到公鑰。如果公鑰是偽造的,那我們無法驗證數(shù)字簽名了,也就根本不可能從數(shù)字簽名確定對方的合法性了。這時候證書就閃亮登場了。我們可能都有考各種證書的經(jīng)歷,比如說普通話證書,四六級證書等等,但是歸根結底,到任何場合我們都能拿出我們的證書來證明自己確實已經(jīng)考過了普通話,考過了四六級。這里的證書也是同樣的道理。

如果不理解證書的作用,我們可以舉一個例子,比如說我們的畢業(yè)證書,任何公司都會承認。為什么會承認?因為那是國家發(fā)得,大家都信任國家。也就是說只要是國家的認證機構,我們都信任它是合法的。

此時即使張三的朋友A把公鑰弄錯了,張三也可以通過這個證書驗證。

代碼實現(xiàn):

public class SignatureDemo {
    public static void main(String[] args) throws Exception {
        String a = "123";
        PublicKey publicKey = RSAdemo4.getPublicKey("a.pub", "RSA");
        PrivateKey privateKey = RSAdemo4.getPrivateKey("a.pri", "RSA");
        String signaturedData = getSignature(a, "sha256withrsa", privateKey);
        boolean b = verifySignature(a, "sha256withrsa", publicKey, signaturedData);
        System.out.println(b);
        System.out.println(signaturedData);
    }
    /**
     * 生成簽名
     *
     * @param input      : 原文
     * @param algorithm  : 算法
     * @param privateKey : 私鑰
     * @return : 簽名
     * @throws Exception
     */
    private static String getSignature(String input, String algorithm, PrivateKey privateKey) throws Exception {
        // 獲取簽名對象
        Signature signature = Signature.getInstance(algorithm);
        // 初始化簽名
        signature.initSign(privateKey);
        // 傳入原文
        signature.update(input.getBytes());
        // 開始簽名
        byte[] sign = signature.sign();
        // 對簽名數(shù)據(jù)進行Base64編碼
        return Base64.encode(sign);
    }
    /**
     * 校驗簽名
     *
     * @param input          : 原文
     * @param algorithm      : 算法
     * @param publicKey      : 公鑰
     * @param signaturedData : 簽名
     * @return : 數(shù)據(jù)是否被篡改
     * @throws Exception
     */
    private static boolean verifySignature(String input, String algorithm, PublicKey publicKey, String signaturedData) throws Exception {
        // 獲取簽名對象
        Signature signature = Signature.getInstance(algorithm);
        // 初始化簽名
        signature.initVerify(publicKey);
        // 傳入原文
        signature.update(input.getBytes());
        // 校驗數(shù)據(jù)
        return signature.verify(Base64.decode(signaturedData));
    }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657

該文章在 2023/6/2 10:10:50 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業(yè)的專業(yè)生產管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內大量中小企業(yè)的青睞。
點晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業(yè)務管理,結合碼頭的業(yè)務特點,圍繞調度、堆場作業(yè)而開發(fā)的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點晴WMS倉儲管理系統(tǒng)提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統(tǒng),標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved