關於加解密、加簽驗籤的那些事

面對MD五、SHA、DES、AES、RSA等等這些名詞你是否有不少問號?這些名詞都是什麼?還有什麼公鑰加密、私鑰解密、私鑰加簽、公鑰驗籤。這些都什麼鬼?或許在你平常工做沒有據說過這些名詞,可是一旦你要設計一個對外訪問的接口,或者安全性要求高的系統,那麼必然會接觸到這些名詞。因此加解密、加簽驗籤對於一個合格的程序員來講是必需要掌握的一個概念。那麼加解密相關的密碼學真的離咱們很遙遠嗎?其實生活中有不少常見的場景其實都用到了密碼學的相關知識,咱們不要把它想得太難,例如在《睡在我上鋪的兄弟》這一段中做弊繞口令中,小癟三表明A,小赤佬表明B,唉呀媽呀表明C,坑爹呀是D,這一段繞口令其實也是密碼學的一種。有興趣的小夥伴能夠看一下這一片斷繞口令片斷。因此其實密碼學與咱們生活息息相關,接下來咱們就一文完全搞懂這些概念。javascript

沒有硝煙的戰場——淺談密碼技術

沒有根基也許能夠建一座小屋,但絕對不能造一座堅固的大廈。php

密碼這個詞有不少種的解釋,在現代社會若是不接觸編程的話,那麼廣泛的認爲是咱們設置的登陸密碼、或者是去銀行取錢時輸入的數字。都是咱們在註冊時實現給提供服務的一方存儲一組數字,之後咱們登陸的時候就用這組數字至關於就證實了咱們的身份。這個數字一般來講就是叫作密碼。html

而咱們須要瞭解的不是上面說的密碼,而是一種「密碼術」,就是對於要傳遞的信息按照某種規則進行轉換,從而隱藏信息的內容。這種方法可使機密信息得以在公開的渠道傳遞而不泄密。使用這種方法,要通過加密過程。在加密過程當中咱們須要知道下面的這些概念:java

  • 原文:或者叫明文,就是被隱藏的文字
  • 加密法:指隱藏原文的法則
  • 密文:或者叫僞文,指對原文按照加密法處理事後生成的可公開傳遞的文字
  • 密鑰:在加密法中起決定性的因素,多是數字、詞彙,也多是一些字母,或者這些東西的組合

加密的結果生成了密文,要想讓接受者可以讀懂這些密文,那麼就要把加密法以及密鑰告訴接受者,否者接受者沒法對密文解密,也就沒法讀懂原文。git

從歷史的角度來看,密碼學大概能夠分爲古典密碼學和近現代密碼學兩個階段。二者以現代信息技術的誕生爲分界點,如今所討論的密碼學多指的是後者,創建在信息論和數學成果基礎之上的。程序員

古典密碼學

古典密碼學源自於數千年前,最先在公元前1900年左右的古埃及,就出現了經過使用特殊字符和簡單替換式密碼來保護信息。美索不達米亞平原上曾經出土一個公元前1500年左右的泥板,其上記錄了加密描述的陶瓷器上釉的工藝配方。古希臘時期(公元前800 ﹣前146 年)還發明瞭經過物理手段來隱藏信息的「隱寫術」,例如使用牛奶書寫、用蠟覆蓋文字等。後來在古羅馬時期還出現了基於替換加密的凱撒密碼,據稱凱撒曾用此方法與其部下通訊而得以命名。這些手段多數是採用簡單的機械工具來保護祕密,在今天看來毫無疑問是十分簡陋,很容易猜出來的。嚴格來看,可能都很難稱爲密碼科學。github

凱撒密碼是當偏移量是3的時候,全部的字母都A都將被替換成D,B變成E,以此類推。web

凱撒密碼
凱撒密碼

近代密碼學

近代密碼學的研究來自於第1、二次世界大戰中對於軍事通訊進行保護和猜出來的需求。1901年12月,意大利的工程師Guglielmo Marconi(奎里亞摩•馬可尼)成功完成了跨越大西洋的無線電通訊的實驗,在全球範圍內引起轟動,推進了無線電通訊時代的到來。無線電大大提升了遠程通訊的能力,可是它有一個自然的缺陷——很難限制接收方,這就意味着你所傳的信息有可能被攔截,所以就催生了加密技術的發展。算法

對於無線電信息進行加密和解密也直接促進了近現代密碼學和計算機技術的出現。反過來這些科技進步也影響了時代的發展。一戰時期德國外交部長Arthur Zimmermann(阿瑟•齊默爾曼)拉攏墨西哥構成抗美軍事同盟的電報(1917 年1月16日)被英國情報機構—40號辦公室破譯,直接致使了美國的參戰;二戰時期德國使用的恩尼格瑪(Enigma)密碼機(當時最早進的加密設備)被盟軍成功破譯(1939年到1941年),致使大西洋戰役德國失敗。據稱,二戰時期光英國從事密碼學研究的人員就達到7000人,而他們的成果使二戰結束的時間至少提早了一到兩年時間。編程

接下來就是能夠稱之爲是密碼學發展史上里程碑的事件了。1945年9月1日,Claude Elwood Shannon(克勞德•艾爾伍德•香農)完成了劃時代的內部報告《A Mathematical Theory of Cryptography(密碼術的一個數學理論)》,1949 年 10 月,該報告以《Communication Theory of Secrecy Systems(保密系統的通訊理論)》爲題在 Bell System Technical Journal(貝爾系統技術期刊)上正式發表。這篇論文首次將密碼學和信息論聯繫到一塊兒,爲對稱密碼技術提供了數學基礎。這也標誌着近現代密碼學的正式創建。這也是密碼學發展史上的第一座里程碑性事件。

密碼學發展史上的第二個里程碑性事件是DES的出現。DES全稱爲Data Encryption Standard,即數據加密標準,是一種使用密鑰加密的分組密碼算法,1977年被美國聯邦政府的國家標準局肯定爲聯邦資料處理標準(FIPS),並受權在非密級政府通訊中使用,隨後該算法在國際上普遍流傳開來。

密碼學發展史上的第三個里程碑性事件就是咱們區塊鏈中普遍應用的公鑰密碼,也就是非對稱密碼算法的出現。1976年11月,Whitfield Diffie 和 Martin E.Hellman 在 IEEE Transactions on Information Theory 上發表了論文《New Directions in Cryptography(密碼學的新方向)》,探討了無需傳輸密鑰的保密通訊和簽名認證體系問題,正式開創了現代公鑰密碼學體系的研究。在公鑰密碼發現之前,若是須要保密通訊,通訊雙方事先要對加解密的算法以及要使用的密鑰進行事先協商,包括送雞毛信,其實是在傳送密鑰。但自從有了公鑰密碼,須要進行祕密通訊的雙方再也不須要進行事前的密鑰協商了。公鑰密碼在理論上是不保密的,在其實是保密的。也就是說,公鑰密碼是能夠猜出來的,但須要極長的時間,等到猜出來了,這個祕密也沒有保密的必要了。

上面咱們說到了關於近現代的密碼學相關的東西,基本上總結下來咱們如今經常使用的就兩個,一個是對稱加密算法,一個是非對稱加密算法。那麼接下來咱們就以介紹這兩個概念爲主線引出開題中咱們提到的概念。

程序實現

對稱加密算法

對稱加密指的就是加密和解密使用同一個祕鑰,因此叫作對稱加密。對稱加密只有一個祕鑰,做爲私鑰。具體的算法有:DES、3DES、TDEA、Blowfish,RC5,IDEA。可是咱們常見的有:DES、AES等等。

那麼對稱加密的優勢是什麼呢?算法公開、計算量小、加密速度快、加密效率高。缺點就是祕鑰的管理和分發是很是困難的,不夠安全。在數據傳送前,發送方和接收方必須商定好祕鑰,而後雙方都必需要保存好祕鑰,若是一方的祕鑰被泄露了,那麼加密的信息也就不安全了。另外,每對用戶每次使用對稱加密算法時,都須要使用其餘人不知道的惟一祕鑰,這會使得收、發雙方所擁有的的鑰匙數量巨大,祕鑰管理也會成爲雙方的負擔。

加密的過程咱們能夠理解爲以下:

  • 加密:原文+祕鑰 = 密文
  • 解密:密文-祕鑰 = 原文

能夠看到兩次過程使用的都是一個祕鑰。用圖簡單表示以下:

實戰演練

既然咱們知道關於對稱加密算法的相關知識,那麼咱們平常用Java如何實現對稱加密的加密和解密動做呢?常見的對稱加密算法有:DES、AES等。

DES

DES加密算法是一種分組密碼,以64位爲分組對數據加密,它的密鑰長度是56位,加密解密用同一算法。DES加密算法是對密鑰進行保密,而公開算法,包括加密和解密算法。這樣,只有掌握了和發送方相同密鑰的人才能解讀由DES加密算法加密的密文數據。所以,破譯DES加密算法實際上就是搜索密鑰的編碼。對於56位長度的密鑰來講,若是用窮舉法來進行搜索的話,其運算次數爲2的56次方。

接下來用Java實現DES加密

 1 private final static String DES = "DES";
2
3    public static void main(String[] args) throws Exception {
4        String data = "123 456";
5        String key = "wang!@#$";
6        System.err.println(encrypt(data, key));
7        System.err.println(decrypt(encrypt(data, key), key));
8
9    }
10
11    /**
12     * Description 根據鍵值進行加密
13     * @param data
14     * @param key  加密鍵byte數組
15     * @return
16     * @throws Exception
17     */

18    public static String encrypt(String data, String key) throws Exception {
19        byte[] bt = encrypt(data.getBytes(), key.getBytes());
20        String strs = new BASE64Encoder().encode(bt);
21        return strs;
22    }
23
24    /**
25     * Description 根據鍵值進行解密
26     * @param data
27     * @param key  加密鍵byte數組
28     * @return
29     * @throws IOException
30     * @throws Exception
31     */

32    public static String decrypt(String data, String key) throws IOException,
33            Exception 
{
34        if (data == null)
35            return null;
36        BASE64Decoder decoder = new BASE64Decoder();
37        byte[] buf = decoder.decodeBuffer(data);
38        byte[] bt = decrypt(buf,key.getBytes());
39        return new String(bt);
40    }
41
42    /**
43     * Description 根據鍵值進行加密
44     * @param data
45     * @param key  加密鍵byte數組
46     * @return
47     * @throws Exception
48     */

49    private static byte[] encrypt(byte[] data, byte[] key) throws Exception {
50        // 生成一個可信任的隨機數源
51        SecureRandom sr = new SecureRandom();
52
53        // 從原始密鑰數據建立DESKeySpec對象
54        DESKeySpec dks = new DESKeySpec(key);
55
56        // 建立一個密鑰工廠,而後用它把DESKeySpec轉換成SecretKey對象
57        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
58        SecretKey securekey = keyFactory.generateSecret(dks);
59
60        // Cipher對象實際完成加密操做
61        Cipher cipher = Cipher.getInstance(DES);
62
63        // 用密鑰初始化Cipher對象
64        cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
65
66        return cipher.doFinal(data);
67    }
68
69
70    /**
71     * Description 根據鍵值進行解密
72     * @param data
73     * @param key  加密鍵byte數組
74     * @return
75     * @throws Exception
76     */

77    private static byte[] decrypt(byte[] data, byte[] key) throws Exception {
78        // 生成一個可信任的隨機數源
79        SecureRandom sr = new SecureRandom();
80
81        // 從原始密鑰數據建立DESKeySpec對象
82        DESKeySpec dks = new DESKeySpec(key);
83
84        // 建立一個密鑰工廠,而後用它把DESKeySpec轉換成SecretKey對象
85        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
86        SecretKey securekey = keyFactory.generateSecret(dks);
87
88        // Cipher對象實際完成解密操做
89        Cipher cipher = Cipher.getInstance(DES);
90
91        // 用密鑰初始化Cipher對象
92        cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
93
94        return cipher.doFinal(data);
95    }
複製代碼

輸出之後能夠看到數據被加密了

15fiw/XhRJ0E=
2123 456
複製代碼

在Java中用DES加密有一個特殊的地方

  1. 祕鑰設置的長度必須大於等於8
  2. 祕鑰設置的長度若是大於8的話,那麼只會取前8個字節做爲祕鑰

爲何呢,咱們能夠看到在初始化DESKeySpec類的時候有下面一段,其中var1是咱們傳的祕鑰。能夠看到他進行了截取。只截取前八個字節。

1public DESKeySpec(byte[] var1, int var2) throws InvalidKeyException {
2    if (var1.length - var2 < 8) {
3        throw new InvalidKeyException("Wrong key size");
4    } else {
5        this.key = new byte[8];
6        System.arraycopy(var1, var2, this.key, 08);
7    }
8}
複製代碼
AES

AES加密算法是密碼學中的高級加密標準,該加密算法採用對稱分組密碼體制,密鑰長度的最少支持爲12八、19二、256,分組長度128位,算法應易於各類硬件和軟件實現。這種加密算法是美國聯邦政府採用的區塊加密標準,AES標準用來替代原先的DES,已經被多方分析且廣爲全世界所使用。

JCE,Java Cryptography Extension,在早期JDK版本中,因爲受美國的密碼出口條例約束,Java中涉及加解密功能的API被限制出口,因此Java中安全組件被分紅了兩部分: 不含加密功能的JCA(Java Cryptography Architecture )和含加密功能的JCE(Java Cryptography Extension)。

JCE的API都在javax.crypto包下,核心功能包括:加解密、密鑰生成(對稱)、MAC生成、密鑰協商。

加解密功能由Cipher組件提供,其也是JCE中最核心的組件。

在設置Cipher類的時候有幾個注意點:

  1. Cipher在使用時需以參數方式指定transformation
  2. transformation的格式爲algorithm/mode/padding,其中algorithm爲必輸項,如: AES/DES/CBC/PKCS5Padding,具體有哪些可看下錶
  3. 缺省的mode爲ECB,缺省的padding爲PKCS5Padding
  4. 在block算法與流加密模式組合時, 需在mode後面指定每次處理的bit數, 如DES/CFB8/NoPadding, 如未指定則使用缺省值, SunJCE缺省值爲64bits
  5. Cipher有4種操做模式: ENCRYPT_MODE(加密), DECRYPT_MODE(解密), WRAP_MODE(導出Key), UNWRAP_MODE(導入Key),初始化時需指定某種操做模式
算法/模式/填充 16字節加密後數據長度 不滿16字節加密後長度
AES/CBC/NoPadding 16 不支持
AES/CBC/PKCS5Padding 32 16
AES/CBC/ISO10126Padding 32 16
AES/CFB/NoPadding 16 原始數據長度
AES/CFB/PKCS5Padding 32 16
AES/CFB/ISO10126Padding 32 16
AES/ECB/NoPadding 16 不支持
AES/ECB/PKCS5Padding 32 16
AES/ECB/ISO10126Padding 32 16
AES/OFB/NoPadding 16 原始數據長度
AES/OFB/PKCS5Padding 32 16
AES/OFB/ISO10126Padding 32 16
AES/PCBC/NoPadding 16 不支持
AES/PCBC/PKCS5Padding 32 16
AES/PCBC/ISO10126Padding 32 16

祕鑰的能夠由咱們本身定義,也能夠是由AES本身生成,當本身定義是須要是要注意:

  1. 根據 AES 規範,能夠是 16 字節、24 字節和32 字節長,分別對應 128 位、192 位和 256 位;
  2. 爲便於傳輸,通常對加密後的數據進行 base64 編碼:
 1    public static void main(String[] args) throws Exception {
2        /*
3         * 此處使用AES-128-ECB加密模式,key須要爲16位。
4         */

5        String cKey = "1234567890123456";
6        // 須要加密的字串
7        String cSrc = "buxuewushu";
8        System.out.println(cSrc);
9        // 加密
10        String enString = Encrypt(cSrc, cKey);
11        System.out.println("加密後的字串是:" + enString);
12
13        // 解密
14        String DeString = Decrypt(enString, cKey);
15        System.out.println("解密後的字串是:" + DeString);
16    }
17
18    // 加密
19    public static String Encrypt(String sSrc, String sKey) throws Exception {
20        if (sKey == null) {
21            System.out.print("Key爲空null");
22            return null;
23        }
24        // 判斷Key是否爲16位
25        if (sKey.length() != 16) {
26            System.out.print("Key長度不是16位");
27            return null;
28        }
29        byte[] raw = sKey.getBytes("utf-8");
30        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
31        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/補碼方式"
32        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
33        byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
34
35        return new Base64().encodeToString(encrypted);//此處使用BASE64作轉碼功能,同時能起到2次加密的做用。
36    }
37
38    // 解密
39    public static String Decrypt(String sSrc, String sKey) throws Exception {
40        try {
41            // 判斷Key是否正確
42            if (sKey == null) {
43                System.out.print("Key爲空null");
44                return null;
45            }
46            // 判斷Key是否爲16位
47            if (sKey.length() != 16) {
48                System.out.print("Key長度不是16位");
49                return null;
50            }
51            byte[] raw = sKey.getBytes("utf-8");
52            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
53            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
54            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
55            byte[] encrypted1 = new Base64().decode(sSrc);//先用base64解密
56            try {
57                byte[] original = cipher.doFinal(encrypted1);
58                String originalString = new String(original,"utf-8");
59                return originalString;
60            } catch (Exception e) {
61                System.out.println(e.toString());
62                return null;
63            }
64        } catch (Exception ex) {
65            System.out.println(ex.toString());
66            return null;
67        }
68    }
複製代碼

非對稱加密算法

非對稱加密算法中加密和解密用的不是同一個祕鑰,因此叫做非對稱加密算法。在非對稱加密算法每一個用戶都有兩把鑰匙,一把公鑰一把私鑰。公鑰是對外發布的,全部人都看的到全部人的公鑰,私鑰是本身保存,每一個人都只知道本身的私鑰而不知作別人的。而也正是在非對稱加密算法中有加密和解密、加簽和驗籤的概念。接下來咱們解釋一下這幾個概念是什麼意思。

加密和解密

用該用戶的公鑰加密後只能該用戶的私鑰才能解密。這種狀況下,公鑰是用來加密信息的,確保只有特定的人(用誰的公鑰就是誰)才能解密該信息。因此這種咱們稱之爲加密和解密

下面我拿A銀行和小明來舉例子吧。假設這2者之間是用不對稱的加密算法來保證信息傳輸的安全性(不被第三人知道信息的含義及篡改信息)。大體流程以下:首先小明發了一條信息給A銀行「我要存500元」。這條信息小明會根據A銀行的對外發布的公鑰把這條信息加密了,加密以後,變成「XXXXXXX」發給A銀行。中間被第三者截獲,因爲沒有A銀行的私鑰沒法解密,不能知道信息的含義,也沒法按正確的方式篡改。因此拿這條加密信息是沒辦法的。最後被A銀行接受,A銀行用本身的私鑰去解密這條信息,解密成功,讀取內容,執行操做。而後得知消息是小明發來的,便去拿小明的公鑰,把「操做成功(或失敗)」這條信息用小明的公鑰加密,發給小明。同理最後小明用本身的私鑰解開,得知知乎發來的信息內容。其餘人截獲由於沒有小明的私鑰因此也沒有用。

加簽和驗籤

還有第二種狀況,公鑰是用來解密信息的,確保讓別人知道這條信息是真的由我發佈的,是完整正確的。接收者由此可知這條信息確實來自於擁有私鑰的某人,這被稱做數字簽名,公鑰的形式就是數字證書。因此這種咱們稱之爲加簽和驗籤

繼續拿小明和銀行A舉例子。銀行A發佈了一個銀行客戶端的補丁供全部用戶更新,那爲了確保人家下載的是正確完整的客戶端,銀行A會爲這個程序打上一個數字簽名(就是用銀行A的私鑰對這個程序加密而後發佈),你須要在你的電腦裏裝上銀行A的數字證書(就是銀行對外發布的公鑰),而後下載好這個程序,數字證書會去解密這個程序的數字簽名,解密成功,補丁得以使用。同時你能知道這個補丁確實是來自這個銀行A,是由他發佈的,而不是其餘人發佈的。

實戰演練

咱們在開發過程當中常用的非對稱加密算法就是RSA算法。接下來咱們使用Java實現RSA算法。

生成密鑰

首先是生成key的部分,生成key有好多種作法,這裏我介紹三種

  1. 命令行:可使用openssl進行生成公鑰和私鑰

    1-- 生成公鑰和私鑰
    2openssl genrsa -out key.pem 1024
    3        -out 指定生成文件,此文件包含公鑰和私鑰兩部分,因此便可以加密,也能夠解密
    4        1024 生成密鑰的長度
    複製代碼
  2. 使用網站:生成密鑰的網站

  3. 使用代碼:能夠指定生成密鑰的長度,最低是512

    1    public static KeyPair buildKeyPair() throws NoSuchAlgorithmException {
    2    final int keySize = 2048;
    3    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
    4    keyPairGenerator.initialize(keySize);
    5    return keyPairGenerator.genKeyPair();
    6}
    複製代碼
加密

有了密鑰,就能夠進行加密的操做了,接下來就介紹關於RSA的加密操做,很是簡單隻要傳進來公鑰和須要加密的數據便可。

1--- 加密
2public static byte[] encrypt(PublicKey publicKey, String message) throws Exception {
3        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
4        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
5
6        return cipher.doFinal(message.getBytes(UTF8));
7    }
複製代碼
解密
1--- 解密
2public static byte[] decrypt(PrivateKey privateKey, byte [] encrypted) throws Exception {
3    Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
4    cipher.init(Cipher.DECRYPT_MODE, privateKey);
5
6    return cipher.doFinal(encrypted);
7}
複製代碼
加簽
 1/**
2 * 使用RSA簽名
3 */

4private static String signWithRSA(String content, PrivateKey privateKey) throws Exception {
5    Signature signature = Signature.getInstance("SHA1WithRSA");
6    signature.initSign(privateKey);
7    signature.update(content.getBytes("utf-8"));
8    byte[] signed = signature.sign();
9    return base64Encode(signed);
10}
複製代碼
驗籤
1/**
2 * 使用RSA驗籤
3 */

4private static boolean checkSignWithRSA(String content, PublicKey publicKey,String sign) throws Exception {
5    Signature signature = Signature.getInstance("SHA1WithRSA");
6    signature.initVerify(publicKey);
7    signature.update(content.getBytes("utf-8"));
8    return signature.verify(base64Decode(sign));
9}
複製代碼

在加簽驗籤的時候須要傳入一個數字簽名標準,咱們這裏填的是SHA1WithRSA,它的意思是用SHA算法進行簽名,用RSA算法進行加密。

算法說明:在對進行SHA1算法進行摘要計算後,要求對計算出的摘要進行處理,而不是直接進行RSA算法進行加密。要求把SHA1摘要的數據進行壓縮到20個字節。在前面插入15個字節標示數據。因此結構以下

130(數據類型結構)21(總長度)30(數據類型)09(長度)06 05 2B 0E 03 02 1A 0500【數據具體類型不清楚-請專家指正】 04 (數據類型) 14 (長度) + SHA1簽名數據
複製代碼

最後進行RSA加密。因此咱們填寫的XXXWithRSA,這個XXX表明的就是使用什麼摘要算法進行加簽,至於摘要算法是什麼,隨後會有詳細的說明。

調用實驗一下

 1public static void main(String[] args) throws Exception {
2    KeyPair keyPair = buildKeyPair();
3
4    byte[] encryptData = encrypt(keyPair.getPublic(), "不學無數");
5
6    System.out.println(String.format("加密後的數據:%s",base64Encode(encryptData)));
7
8    System.out.println(String.format("解密後的數據:%s",new String(decrypt(keyPair.getPrivate(),encryptData),UTF8)));
9
10    String context = "加簽的字符串";
11
12    String sign = signWithRSA(context, keyPair.getPrivate());
13
14    System.out.println(String.format("生成的簽名:%s",sign));
15
16    Boolean checkSignWithRSA = checkSignWithRSA(context, keyPair.getPublic(), sign);
17
18    System.out.println(String.format("校驗的結果:%s",checkSignWithRSA.toString()));
19}
複製代碼

輸出爲

 1加密後的數據:Bi8b4eqEp+rNRhDaij8vVlNwKuICbPJfFmyzmEXKuAgEgzMPb8hAmYiGN+rbUKWeZYJKJd0fiOXv
26YrYqd7fdast/m443qQreRLxdQFScwvCvj9g1YnPzbU2Q/jIwqAPopTyPHNNngBmFki+R/6V4DYt
3HA5gniaUMYzynHdD+/W+x8ZYmwiuuS63+7wXqL36aLKe0H50wELOpSn45Gvni8u+5zPIoHV7PBiz
4trCnQvne5LxFKDprrS3td1/76qyupFd+Ul3hsd+gjbAyN2MlXcAFMrGVaRkopWwc9hP1BsPvS52q
5/8jOVdbeyU9BziVhViz1V0TtGW8bfbEnIStc3Q==
6解密後的數據:不學無數
7生成的簽名:wvUXtr2UI0tUXmyMTTUBft8oc1dhvtXSBrFFetI5ZoxMm91TbXRWD31Pgqkg72ADxx9TEOAM3Bm1
8kyzfBCZZpoq6Y9SM4+jdJ4sMTVtw0wACPglnPDAGs8sG7nnLhXWNQ1Y4pl4ziY6uLxF1TzQLFTxu
9NAS7nyljbG69wrb9R3Sv5t8r1I54rYCVGSVFmTrGf+dSCjxABZv6mH8nygVif7zN1vU1+nSDKcON
10Vtrpv0xCQHVBqnHPA6OiDm5GzBQxjD5aQt8mfgv8JJrB52TEa4JPYoC5Zw4JHlL++OvPwMpJgnuG
11yg5vnWhxE2ncTzM+/pZ+CnXF2Dqv/JMQOfX6tA==
12校驗的結果:true
複製代碼

摘要算法

數據摘要算法是密碼學算法中很是重要的一個分支,它經過對全部數據提取指紋信息以實現數據簽名、數據完整性校驗等功能,因爲其不可逆性,有時候會被用作敏感信息的加密。數據摘要算法也被稱爲哈希(Hash)算法或散列算法。

消息摘要算法的主要特徵是加密過程不須要密鑰,而且通過加密的數據沒法被解密,只有輸入相同的明文數據通過相同的消息摘要算法才能獲得相同的密文。(摘要能夠比方爲指紋,消息摘要算法就是要獲得文件的惟一職位)

特色

不管輸入的消息有多長,計算出來的消息摘要的長度老是固定的。通常地,只要輸入的消息不一樣,對其進行摘要之後產生的摘要消息也必不相同;但相同的輸入必會產生相同的輸出。只能進行正向的信息摘要,而沒法從摘要中恢復出任何的消息,甚至根本就找不到任何與原信息相關的信息(不可逆性)。

好的摘要算法,沒有人能從中找到「碰撞」或者說極度難找到,雖然「碰撞」是確定存在的(碰撞即不一樣的內容產生相同的摘要)。

應用

通常地,把對一個信息的摘要稱爲該消息的指紋或數字簽名。數字簽名是保證信息的完整性和不能否認性的方法。數據的完整性是指信宿接收到的消息必定是信源發送的信息,而中間絕無任何更改;信息的不能否認性是指信源不可否認曾經發送過的信息。其實,經過數字簽名還能實現對信源的身份識別(認證),即肯定「信源」是不是信宿意定的通訊夥伴。 數字簽名應該具備惟一性,即不一樣的消息的簽名是不同的;同時還應具備不可僞造性,即不可能找到另外一個消息,使其簽名與已有的消息的簽名同樣;還應具備不可逆性,即沒法根據簽名還原被簽名的消息的任何信息。這些特徵偏偏都是消息摘要算法的特徵,因此消息摘要算法適合做爲數字簽名算法

有哪些具體的消息摘要算法?
  • CRC八、CRC1六、CRC32:CRC(Cyclic Redundancy Check,循環冗餘校驗)算法出現時間較長,應用也十分普遍,尤爲是通信領域,如今應用最多的就是 CRC32 算法,它產生一個4字節(32位)的校驗值,通常是以8位十六進制數,如FA 12 CD 45等。CRC算法的優勢在於簡便、速度快,嚴格的來講,CRC更應該被稱爲數據校驗算法,但其功能與數據摘要算法相似,所以也做爲測試的可選算法。
  • MD2 、MD四、MD5:這是應用很是普遍的一個算法家族,尤爲是 MD5(Message-Digest Algorithm 5,消息摘要算法版本5),它由MD二、MD三、MD4發展而來,由Ron Rivest(RSA公司)在1992年提出,目前被普遍應用於數據完整性校驗、數據(消息)摘要、數據加密等。MD二、MD四、MD5 都產生16字節(128位)的校驗值,通常用32位十六進制數表示。MD2的算法較慢但相對安全,MD4速度很快,但安全性降低,MD5比MD4更安全、速度更快。
  • SHA一、SHA25六、SHA38四、SHA512:SHA(Secure Hash Algorithm)是由美國專門制定密碼算法的標準機構——美國國家標準技術研究院(NIST)制定的,SHA系列算法的摘要長度分別爲:SHA爲20字節(160位)、SHA256爲32字節(256位)、 SHA384爲48字節(384位)、SHA512爲64字節(512位),因爲它產生的數據摘要的長度更長,所以更難以發生碰撞,所以也更爲安全,它是將來數據摘要算法的發展方向。因爲SHA系列算法的數據摘要長度較長,所以其運算速度與MD5相比,也相對較慢。
  • RIPEMD、PANAMA、TIGER、ADLER32 等: RIPEMD是Hans Dobbertin等3人在對MD4,MD5缺陷分析基礎上,於1996年提出來的,有4個標準12八、160、256和320,其對應輸出長度分別爲16字節、20字節、32字節和40字節。TIGER由Ross在1995年提出。Tiger號稱是最快的Hash算法,專門爲64位機器作了優化。
實戰演練

在單獨的使用摘要算法時咱們一般使用的MD5算法,因此咱們這裏就單獨說明使用Java實現MD5算法。

 1public static String getMD5Str(String str) throws Exception {
2    try {
3        // 生成一個MD5加密計算摘要
4        MessageDigest md = MessageDigest.getInstance("MD5");
5        // 計算md5函數
6        md.update(str.getBytes());
7        // digest()最後肯定返回md5 hash值,返回值爲8爲字符串。由於md5 hash值是16位的hex值,實際上就是8位的字符
8        // BigInteger函數則將8位的字符串轉換成16位hex值,用字符串來表示;獲得字符串形式的hash值
9        return new BigInteger(1, md .digest()).toString(16);
10    } catch (Exception e) {
11        throw new Exception("MD5加密出現錯誤,"+e.toString());
12    }
13}
複製代碼

文章中涉及到的代碼地址

參考

相關文章
相關標籤/搜索