【人人都懂密碼學(xué)】一篇最易懂的Java密碼學(xué)入門教程(上) 【人人都懂密碼學(xué)】一篇最易懂的Java密碼學(xué)入門教程(中) 【人人都懂密碼學(xué)】一篇最易懂的Java密碼學(xué)入門教程(下)(JAVA密碼)
密碼與我們的生活息息相關(guān),遠(yuǎn)到國家機(jī)密,近到個(gè)人賬戶,我們每天都在跟密碼打交道:
那么,密碼從何而來?生活中常見的加密是怎么實(shí)現(xiàn)的?怎么保證個(gè)人信息安全?本文將從這幾方面進(jìn)行淺談,如有紕漏,敬請(qǐng)各位大佬指正。
代碼部分從第二章節(jié)——常見加密算法開始,對(duì)代碼比較感興趣的鐵子們可以從第二章節(jié)開始看。
一、 密碼學(xué)發(fā)展史
密碼學(xué)是網(wǎng)絡(luò)安全、信息安全、區(qū)塊鏈等產(chǎn)品的基礎(chǔ),常見的非對(duì)稱加密、對(duì)稱加密、散列函數(shù)等,都屬于密碼學(xué)范疇。
密碼學(xué)有數(shù)千年的歷史,從最開始的替換法到如今的非對(duì)稱加密算法,經(jīng)歷了古典密碼學(xué),近代密碼學(xué)和現(xiàn)代密碼學(xué)三個(gè)階段。密碼學(xué)不僅僅是數(shù)學(xué)家們的智慧,更是如今網(wǎng)絡(luò)空間安全的重要基礎(chǔ)。
1.1 古典密碼學(xué)
古典密碼的加密方式主要有替換法和移位法。古典密碼雖然很簡單,但是在密碼史上是使用的最久的加密方式,直到“概率論”的數(shù)學(xué)方法被發(fā)現(xiàn),古典密碼就被破解了。
1.2 近代密碼學(xué)
古典密碼的安全性受到了威脅,外加使用便利性較低,到了工業(yè)化時(shí)代,近現(xiàn)代密碼被廣泛應(yīng)用。
恩尼格瑪機(jī)
恩尼格瑪機(jī)是二戰(zhàn)時(shí)期納粹德國使用的加密機(jī)器,后被英國破譯,參與破譯的人員有被稱為計(jì)算機(jī)科學(xué)之父、人工智能之父的圖靈。
1.3 現(xiàn)代密碼學(xué)
① 散列函數(shù)
散列函數(shù),也見雜湊函數(shù)、摘要函數(shù)或哈希函數(shù),可將任意長度的消息經(jīng)過運(yùn)算,變成固定長度數(shù)值,常見的有MD5、SHA-1、SHA256,多應(yīng)用在文件校驗(yàn),數(shù)字簽名中。
MD5 可以將任意長度的原文生成一個(gè)128位(16字節(jié))的哈希值
SHA-1可以將任意長度的原文生成一個(gè)160位(20字節(jié))的哈希值
② 對(duì)稱密碼
對(duì)稱密碼應(yīng)用了相同的加密密鑰和解密密鑰。對(duì)稱密碼分為:序列密碼(流密碼),分組密碼(塊密碼)兩種。流密碼是對(duì)信息流中的每一個(gè)元素(一個(gè)字母或一個(gè)比特)作為基本的處理單元進(jìn)行加密,塊密碼是先對(duì)信息流分塊,再對(duì)每一塊分別加密。
例如原文為1234567890,流加密即先對(duì)1進(jìn)行加密,再對(duì)2進(jìn)行加密,再對(duì)3進(jìn)行加密……最后拼接成密文;塊加密先分成不同的塊,如1234成塊,5678成塊,90XX(XX為補(bǔ)位數(shù)字)成塊,再分別對(duì)不同塊進(jìn)行加密,最后拼接成密文。前文提到的古典密碼學(xué)加密方法,都屬于流加密。
③ 非對(duì)稱密碼
對(duì)稱密碼的密鑰安全極其重要,加密者和解密者需要提前協(xié)商密鑰,并各自確保密鑰的安全性,一但密鑰泄露,即使算法是安全的也無法保障原文信息的私密性。
在實(shí)際的使用中,遠(yuǎn)程的提前協(xié)商密鑰不容易實(shí)現(xiàn),即使協(xié)商好,在遠(yuǎn)程傳輸過程中也容易被他人獲取,因此非對(duì)稱密鑰此時(shí)就凸顯出了優(yōu)勢(shì)。
非對(duì)稱密碼有兩支密鑰,公鑰(publickey)和私鑰(privatekey),加密和解密運(yùn)算使用的密鑰不同。用公鑰對(duì)原文進(jìn)行加密后,需要由私鑰進(jìn)行解密;用私鑰對(duì)原文進(jìn)行加密后(此時(shí)一般稱為簽名),需要由公鑰進(jìn)行解密(此時(shí)一般稱為驗(yàn)簽)。公鑰可以公開的,大家使用公鑰對(duì)信息進(jìn)行加密,再發(fā)送給私鑰的持有者,私鑰持有者使用私鑰對(duì)信息進(jìn)行解密,獲得信息原文。因?yàn)樗借€只有單一人持有,因此不用擔(dān)心被他人解密獲取信息原文。
________________________________________
二、常見加密算法
讓我們來看看生活中常見的幾種加密方式:
____________________________________
2.1 對(duì)稱加密算法
________________________________________
采用單鑰密碼系統(tǒng)的加密方法,同一個(gè)密鑰可以同時(shí)用作信息的加密和解密,這種加密方法稱為對(duì)稱加密,也稱為單密鑰加密。
示例
? 我們現(xiàn)在有一個(gè)原文3要發(fā)送給B
? 設(shè)置密鑰為108, 3 * 108 = 324, 將324作為密文發(fā)送給B
? B拿到密文324后, 使用324/108 = 3 得到原文
常見加密算法
DES : Data Encryption Standard,即數(shù)據(jù)加密標(biāo)準(zhǔn),是一種使用密鑰加密的塊算法,1977年被美國聯(lián)邦政府的國家標(biāo)準(zhǔn)局確定為聯(lián)邦資料處理標(biāo)準(zhǔn)(FIPS),并授權(quán)在非密級(jí)政府通信中使用,隨后該算法在國際上廣泛流傳開來。
________________________________________
AES : Advanced Encryption Standard, 高級(jí)加密標(biāo)準(zhǔn) .在密碼學(xué)中又稱Rijndael加密法,是美國聯(lián)邦政府采用的一種區(qū)塊加密標(biāo)準(zhǔn)。這個(gè)標(biāo)準(zhǔn)用來替代原先的DES,已經(jīng)被多方分析且廣為全世界所使用。
特點(diǎn)
? 加密速度快, 可以加密大文件
? 密文可逆, 一旦密鑰文件泄漏, 就會(huì)導(dǎo)致數(shù)據(jù)暴露
? 加密后編碼表找不到對(duì)應(yīng)字符, 出現(xiàn)亂碼
? 一般結(jié)合Base64使用
2.1.1 DES加密
示例代碼 des加密算法
Cipher :文檔 https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html#getInstance-java.lang.String-
import?javax.crypto.Cipher; import?javax.crypto.spec.SecretKeySpec; /** ?* ?* ?*?@author?cWX970190 ?*?@since?2020-10-10?21:50 ?*/ public?class?DesAesDemo?{ ????public?static?void?main(String[]?args)?throws?Exception{ ????????//?原文 ????????String?input?=?"華為"; ????????//?des加密必須是8位 ????????String?key?=?"123456"; ????????//?算法 ????????String?algorithm?=?"DES"; ????????String?transformation?=?"DES"; ????//?Cipher:密碼,獲取加密對(duì)象 ????//?transformation:參數(shù)表示使用什么類型加密 ????Cipher?cipher?=?Cipher.getInstance(transformation); ????//?指定秘鑰規(guī)則 ????//?第一個(gè)參數(shù)表示:密鑰,key的字節(jié)數(shù)組 ????//?第二個(gè)參數(shù)表示:算法 ????SecretKeySpec?sks?=?new?SecretKeySpec(key.getBytes(),?algorithm); ????//?對(duì)加密進(jìn)行初始化 ????//?第一個(gè)參數(shù):表示模式,有加密模式和解密模式 ????//?第二個(gè)參數(shù):表示秘鑰規(guī)則 ????cipher.init(Cipher.ENCRYPT_MODE,sks); ????//?進(jìn)行加密 ????byte[]?bytes?=?cipher.doFinal(input.getBytes()); ????//?打印字節(jié),因?yàn)閍scii碼有負(fù)數(shù),解析不出來,所以亂碼 //????????for?(byte?b?:?bytes)?{ //????????????System.out.println(b); //????????} ????????//?打印密文 ????????System.out.println(new?String(bytes)); ????} }
運(yùn)行:
出現(xiàn)這個(gè)bug的原因是DES算法規(guī)定,key必須是8個(gè)字節(jié);
修改 密鑰 key = “12345678” ,再次運(yùn)行 ,出現(xiàn)亂碼是因?yàn)閷?duì)應(yīng)的字節(jié)出現(xiàn)負(fù)數(shù),但負(fù)數(shù)沒有出現(xiàn)在 ascii 碼表里面,所以出現(xiàn)亂碼,需要配合base64進(jìn)行轉(zhuǎn)碼
________________________________________
2.1.2 拓展:base64編碼
________________________________________
在Java 8中,Base64編碼已經(jīng)成為Java類庫的標(biāo)準(zhǔn)。
Java 8 內(nèi)置了 Base64 編碼的編碼器和解碼器。
Base64工具類提供了一套靜態(tài)方法獲取下面三種BASE64編解碼器:
- 基本:輸出被映射到一組字符A-Za-z0-9+/,編碼不添加任何行標(biāo),輸出的解碼僅支持A-Za-z0-9+/。
- URL:輸出映射到一組字符A-Za-z0-9+_,輸出是URL和文件。
- MIME:輸出隱射到MIME友好格式。輸出每行不超過76字符,并且使用’\r’并跟隨’\n’作為分割。編碼輸出最后沒有行分割。
上面的例子用Java8自帶的base64進(jìn)行編碼:
import?javax.crypto.Cipher; import?javax.crypto.spec.SecretKeySpec; import?java.util.Base64; /** ?*?DesAesDemo ?* ?*?@Author:?陳志強(qiáng) ?*?@CreateTime:?2020-10-10 ?*?@Description: ?*/ public?class?DesAesDemo?{ ????public?static?void?main(String[]?args)?throws?Exception{ ????????//?原文 ????????String?input?=?"華為"; ????????//?des加密必須是8位 ????????String?key?=?"12345678"; ????????//?算法 ????????String?algorithm?=?"DES"; ????????String?transformation?=?"DES"; ????????//?Cipher:密碼,獲取加密對(duì)象 ????????//?transformation:參數(shù)表示使用什么類型加密 ????????Cipher?cipher?=?Cipher.getInstance(transformation); ????????//?指定秘鑰規(guī)則 ????????//?第一個(gè)參數(shù)表示:密鑰,key的字節(jié)數(shù)組 ????????//?第二個(gè)參數(shù)表示:算法 ????????SecretKeySpec?sks?=?new?SecretKeySpec(key.getBytes(),?algorithm); ????????//?對(duì)加密進(jìn)行初始化 ????????//?第一個(gè)參數(shù):表示模式,有加密模式和解密模式 ????????//?第二個(gè)參數(shù):表示秘鑰規(guī)則 ????????cipher.init(Cipher.ENCRYPT_MODE,sks); ????????//?進(jìn)行加密 ????????byte[]?bytes?=?cipher.doFinal(input.getBytes()); ????????//對(duì)數(shù)據(jù)進(jìn)行base64編碼處理 ????????String?encode?=?Base64.getEncoder().encodeToString(bytes); ????????//?打印密文 ????????System.out.println(encode); ????} }
運(yùn)行:
除了上面的編碼方式外,base64還有其他的編碼方式,由于筆者時(shí)間有限,沒有過多研究,在此放入一個(gè)demo,供大家參考:
import?org.junit.Test; import?java.io.UnsupportedEncodingException; import?java.util.Base64; import?java.util.UUID; /** ?*?在Java?8中,Base64編碼已經(jīng)成為Java類庫的標(biāo)準(zhǔn)。 ?*?Java?8?內(nèi)置了?Base64?編碼的編碼器和解碼器。 ?*?Base64工具類提供了一套靜態(tài)方法獲取下面三種BASE64編解碼器: ?*?
?*?基本:輸出被映射到一組字符A-Za-z0-9+/,編碼不添加任何行標(biāo),輸出的解碼僅支持A-Za-z0-9+/。 ?*?URL:輸出被映射到一組字符A-Za-z0-9+_,輸出是URL和文件。 ?*?MIME:輸出隱射到MIME友好格式。輸出每行不超過76字符,并且使用'\r'并跟隨'\n'作為分割。編碼輸出最后沒有行分割。 ?*/ public?class?Base64Test?{ ????private?static?final?String?UTF_8?=?"utf-8"; ????private?static?final?int?MAX?=?10; ????@Test ????public?void?base64()?throws?UnsupportedEncodingException?{ //????????test(); //????????basic(); ????????url(); //????????mime(); ????} ????/** ?????*?測(cè)試幾個(gè)特殊字符 ?????*/ ????private?void?test()?throws?UnsupportedEncodingException?{ ????????String?ss?=?"星期五?/\\|"; ????????System.out.println("ordinal?????????:?"?+?ss); ????????byte[]?encode?=?Base64.getEncoder().encode(ss.getBytes(UTF_8)); ????????System.out.println("basic?encode????:?"?+?new?String(encode,?UTF_8)); ????????byte[]?decode?=?Base64.getDecoder().decode(encode); ????????System.out.println("Using?Basic?????:?"?+?new?String(decode,?UTF_8)); ????????byte[]?decode1?=?Base64.getUrlDecoder().decode(encode); ????????System.out.println("Using?URL???????:?"?+?new?String(decode1,?UTF_8)); ????????byte[]?decode2?=?Base64.getMimeDecoder().decode(encode); ????????System.out.println("Using?MIME??????:?"?+?new?String(decode2,?UTF_8)); ????????System.out.println(); ????} ????/** ?????*?MIME編碼器會(huì)使用基本的字母數(shù)字產(chǎn)生BASE64輸出, ?????*?而且對(duì)MIME格式友好:每一行輸出不超過76個(gè)字符,而且每行以“\r\n”符結(jié)束 ?????*/ ????private?void?mime()?throws?UnsupportedEncodingException?{ ????????StringBuilder?sb?=?new?StringBuilder(); ????????for?(int?t?=?0;?t?
運(yùn)行:
2.1.3 DES解密
在2.1.1中的例子基礎(chǔ)上加入解密方法
import?javax.crypto.Cipher; import?javax.crypto.spec.SecretKeySpec; import?java.util.Base64; public?class?DesDemo?{ ????//?DES加密算法,key的大小必須是8個(gè)字節(jié) public?static?void?main(String[]?args)?throws?Exception?{ ????String?input?="華為"; ????//?DES加密算法,key的大小必須是8個(gè)字節(jié) ????String?key?=?"12345678"; ????String?transformation?=?"DES";?//?9PQXVUIhaaQ= ????//?指定獲取密鑰的算法 ????String?algorithm?=?"DES"; ????String?encryptDES?=?encryptDES(input,?key,?transformation,?algorithm); ????System.out.println("加密:"?+?encryptDES); ????String?s?=?decryptDES(encryptDES,?key,?transformation,?algorithm); ????System.out.println("解密:"?+?s); } /** ?*?使用DES加密數(shù)據(jù) ?* ?*?@param?input??????????:?原文 ?*?@param?key????????????:?密鑰(DES,密鑰的長度必須是8個(gè)字節(jié)) ?*?@param?transformation?:?獲取Cipher對(duì)象的算法 ?*?@param?algorithm??????:?獲取密鑰的算法 ?*?@return?:?密文 ?*?@throws?Exception ?*/ private?static?String?encryptDES(String?input,?String?key,?String?transformation,?String?algorithm)?throws?Exception?{ ????//?獲取加密對(duì)象 ????Cipher?cipher?=?Cipher.getInstance(transformation); ????//?創(chuàng)建加密規(guī)則 ????//?第一個(gè)參數(shù)key的字節(jié) ????//?第二個(gè)參數(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?=?new?String(Base64.getEncoder().encode(bytes),?"UTF-8"); //????????System.out.println(encode); ????????return?encode; ????} /** ?*?使用DES解密 ?* ?*?@param?input??????????:?密文 ?*?@param?key????????????:?密鑰 ?*?@param?transformation?:?獲取Cipher對(duì)象的算法 ?*?@param?algorithm??????:?獲取密鑰的算法 ?*?@throws?Exception ?*?@return:?原文 ?*/ private?static?String?decryptDES(String?input,?String?key,?String?transformation,?String?algorithm)?throws?Exception?{ ????//?1,獲取Cipher對(duì)象 ????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.getDecoder().decode(input)); //????????System.out.println("解密"?+?new?String(decode,?"UTF-8")); ????????//??因?yàn)槭敲魑模灾苯臃祷?????????return?new?String(bytes); ????} }
運(yùn)行:
2.1.4 AES加密解密
AES 加密解密和 DES 加密解密代碼一樣,只需要修改加密算法就行,在此不做過多闡述,值得注意的是:AES 加密的密鑰key , 需要傳入16個(gè)字節(jié).
2.1.5 加密模式
AES的加密模式如下:
參考鏈接:https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html
這里主要介紹兩種加密模式:ECB和CBC
ECB
Electronic codebook, 電子密碼本. 需要加密的消息按照塊密碼的塊大小被分為數(shù)個(gè)塊,并對(duì)每個(gè)塊進(jìn)行獨(dú)立加密
? 優(yōu)點(diǎn) : 可以并行處理數(shù)據(jù)
? 缺點(diǎn) : 同樣的原文生成同樣的密文, 不能很好的保護(hù)數(shù)據(jù)
? 同時(shí)加密,原文是一樣的,加密出來的密文也是一樣的
CBC
Cipher-block chaining, 密碼塊鏈接. 每個(gè)明文塊先與前一個(gè)密文塊進(jìn)行異或后,再進(jìn)行加密。在這種方法中,每個(gè)密文塊都依賴于它前面的所有明文塊
? 優(yōu)點(diǎn) : 同樣的原文生成的密文不一樣
? 缺點(diǎn) : 串行處理數(shù)據(jù).
2.1.6 填充模式
當(dāng)需要按塊處理的數(shù)據(jù), 數(shù)據(jù)長度不符合塊處理需求時(shí), 按照一定的方法填充滿塊長的規(guī)則,這里主要介紹以下兩種:
NoPadding
? 不填充.
? 在DES加密算法下, 要求原文長度必須是8byte的整數(shù)倍
? 在AES加密算法下, 要求原文長度必須是16byte的整數(shù)倍
PKCS5Padding
? 數(shù)據(jù)塊的大小為8位, 不夠就補(bǔ)足
Tips
? 默認(rèn)情況下, 加密模式和填充模式為 : ECB/PKCS5Padding
? 如果使用CBC模式, 在初始化Cipher對(duì)象時(shí), 需要增加參數(shù), 初始化向量IV : IvParameterSpec iv = new IvParameterSpec(key.getBytes());
加密模式和填充模式:其中括號(hào)里數(shù)字表示加密位數(shù),位數(shù)越高,則越安全
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)
加密模式和填充模式例子
/* ?*?Copyright?(c)?Huawei?Technologies?Co.,?Ltd.?2020-2020.?All?rights?reserved. ?*/ package?com.huawei.it.jalor.boot.test; /** ?*?功能描述:?加密模式和填充模式例子 ?* ?*?@author?cWX970190 ?*?@since?2020-10-11 ?*/ import?com.sun.org.apache.xml.internal.security.utils.Base64; import?javax.crypto.Cipher; import?javax.crypto.spec.SecretKeySpec; public?class?DesDemo?{ ????//?DES加密算法,key的大小必須是8個(gè)字節(jié) ????public?static?void?main(String[]?args)?throws?Exception?{ ????????String?input?="華為"; ????????//?DES加密算法,key的大小必須是8個(gè)字節(jié) ????????String?key?=?"12345678"; ????????//?指定獲取Cipher的算法,如果沒有指定加密模式和填充模式,ECB/PKCS5Padding就是默認(rèn)值 ????????//?????String?transformation?=?"DES";?//?9PQXVUIhaaQ= ????????//String?transformation?=?"DES/ECB/PKCS5Padding";?//?9PQXVUIhaaQ= ????????//?CBC模式,必須指定初始向量,初始向量中密鑰的長度必須是8個(gè)字節(jié) //????????String?transformation?=?"DES/CBC/PKCS5Padding";?//?9PQXVUIhaaQ= ????????//?NoPadding模式,原文的長度必須是8個(gè)字節(jié)的整倍數(shù)?,所以必須把?硅谷改成硅谷12 ????????String?transformation?=?"DES/CBC/NoPadding";?//?9PQXVUIhaaQ= ????????//?指定獲取密鑰的算法 ????????String?algorithm?=?"DES"; ????????String?encryptDES?=?encryptDES(input,?key,?transformation,?algorithm); ????????System.out.println("加密:"?+?encryptDES); ????????String?s?=?dncryptDES(encryptDES,?key,?transformation,?algorithm); ????????System.out.println("解密:"?+?s); ????} ????/** ?????*?使用DES加密數(shù)據(jù) ?????* ?????*?@param?input??????????:?原文 ?????*?@param?key????????????:?密鑰(DES,密鑰的長度必須是8個(gè)字節(jié)) ?????*?@param?transformation?:?獲取Cipher對(duì)象的算法 ?????*?@param?algorithm??????:?獲取密鑰的算法 ?????*?@return?:?密文 ?????*?@throws?Exception ?????*/ ????private?static?String?encryptDES(String?input,?String?key,?String?transformation,?String?algorithm)?throws?Exception?{ ????????//?獲取加密對(duì)象 ????????Cipher?cipher?=?Cipher.getInstance(transformation); ????????//?創(chuàng)建加密規(guī)則 ????????//?第一個(gè)參數(shù)key的字節(jié) ????????//?第二個(gè)參數(shù)表示加密算法 ????????SecretKeySpec?sks?=?new?SecretKeySpec(key.getBytes(),?algorithm); ????????//?ENCRYPT_MODE:加密模式 ????????//?DECRYPT_MODE:?解密模式 ????????//?初始向量,參數(shù)表示跟誰進(jìn)行異或,初始向量的長度必須是8位 //????????IvParameterSpec?iv?=?new?IvParameterSpec(key.getBytes()); ????????//?初始化加密模式和算法 ????????cipher.init(Cipher.ENCRYPT_MODE,sks); ????????//?加密 ????????byte[]?bytes?=?cipher.doFinal(input.getBytes()); ????????//?輸出加密后的數(shù)據(jù) ????????String?encode?=?Base64.encode(bytes); ????????return?encode; ????} ????/** ?????*?使用DES解密 ?????* ?????*?@param?input??????????:?密文 ?????*?@param?key????????????:?密鑰 ?????*?@param?transformation?:?獲取Cipher對(duì)象的算法 ?????*?@param?algorithm??????:?獲取密鑰的算法 ?????*?@throws?Exception ?????*?@return:?原文 ?????*/ ????private?static?String?dncryptDES(String?input,?String?key,?String?transformation,?String?algorithm)?throws?Exception?{ ????????//?1,獲取Cipher對(duì)象 ????????Cipher?cipher?=?Cipher.getInstance(transformation); ????????//?指定密鑰規(guī)則 ????????SecretKeySpec?sks?=?new?SecretKeySpec(key.getBytes(),?algorithm); //????????IvParameterSpec?iv?=?new?IvParameterSpec(key.getBytes()); ????????cipher.init(Cipher.DECRYPT_MODE,?sks); ????????//?3.?解密 ????????byte[]?bytes?=?cipher.doFinal(Base64.decode(input)); ????????return?new?String(bytes); ????} }
運(yùn)行:
非填充模式下,原文必須是8個(gè)字節(jié),修改加密模式為:
String?transformation?=?"DES/CBC/PKCS5Padding";
再次運(yùn)行:
發(fā)現(xiàn)加密沒有問題,但是解密時(shí)需要添加一個(gè)參數(shù),添加參數(shù)并修改初始化規(guī)則:
//?初始向量,參數(shù)表示跟誰進(jìn)行異或,初始向量的長度必須是8位 ????????IvParameterSpec?iv?=?new?IvParameterSpec(key.getBytes()); ????????//?初始化加密模式和算法 ????????cipher.init(Cipher.ENCRYPT_MODE,sks,iv);
再次運(yùn)行:
在測(cè)試 AES 的時(shí)候需要注意,key需要16個(gè)字節(jié),加密向量也需要16個(gè)字節(jié) ,其他方式跟 DES 一樣
________________________________________
2.2 消息摘要(單向散列)函數(shù)
________________________________________
? 消息摘要(Message Digest)又稱為數(shù)字摘要(Digital Digest)
? 它是一個(gè)唯一對(duì)應(yīng)一個(gè)消息或文本的固定長度的值,它由一個(gè)單向Hash加密函數(shù)對(duì)消息進(jìn)行作用而產(chǎn)生
? 使用數(shù)字摘要生成的值是不可以篡改的,為了保證文件或者值的安全
2.2.1 特點(diǎn)
無論輸入的消息有多長,計(jì)算出來的消息摘要的長度總是固定的。例如應(yīng)用MD5算法摘要的消息有128個(gè)比特位,用SHA-1算法摘要的消息最終有160比特位的輸出
只要輸入的消息不同,對(duì)其進(jìn)行摘要以后產(chǎn)生的摘要消息也必不相同;但相同的輸入必會(huì)產(chǎn)生相同的輸出
消息摘要是單向、不可逆的
常見算法 :
? MD5
? SHA1
? SHA256
? SHA512
瀏覽器搜索 tomcat ,進(jìn)入官網(wǎng)下載 ,會(huì)經(jīng)常發(fā)現(xiàn)有 sha1,sha512 , 這些都是數(shù)字摘要
2.2.2 獲取字符串消息摘要
/* ?*?Copyright?(c)?Huawei?Technologies?Co.,?Ltd.?2020-2020.?All?rights?reserved. ?*/ package?com.huawei.it.jalor.boot.test; /** ?*?功能描述 ?* ?*?@author?cWX970190 ?*?@since?2020-10-11 ?*/ import?com.sun.org.apache.xml.internal.security.utils.Base64; import?java.security.MessageDigest; public?class?DigestDemo1?{ ????public?static?void?main(String[]?args)?throws?Exception{ ????????//?原文 ????????String?input?=?"aa"; ????????//?算法 ????????String?algorithm?=?"MD5"; ????????//?獲取數(shù)字摘要對(duì)象 ????????MessageDigest?messageDigest?=?MessageDigest.getInstance(algorithm); ????????//?獲取消息數(shù)字摘要的字節(jié)數(shù)組 ????????byte[]?digest?=?messageDigest.digest(input.getBytes("UTF-8")); ????????//需要進(jìn)行base64編碼,不然輸出亂碼 ????????System.out.println(Base64.encode(digest)); ????} }
運(yùn)行:
使用在線 md5 加密 ,發(fā)現(xiàn)我們生成的值和代碼生成的值不一樣,那是因?yàn)橄⒄皇鞘褂胋ase64進(jìn)行編碼的,所以我們需要把值轉(zhuǎn)成16進(jìn)制
數(shù)字摘要轉(zhuǎn)換成 16 進(jìn)制
package?com.huawei.it.jalor.boot.test; /** ?*?功能描述 ?* ?*?@author?cWX970190 ?*?@since?2020-10-11 ?*/ import?com.sun.org.apache.xml.internal.security.utils.Base64; import?java.security.MessageDigest; public?class?DigestDemo1?{ ????public?static?void?main(String[]?args)?throws?Exception{ ????????//?原文 ????????String?input?=?"aa"; ????????//?算法 ????????String?algorithm?=?"MD5"; ????????//?獲取數(shù)字摘要對(duì)象 ????????MessageDigest?messageDigest?=?MessageDigest.getInstance(algorithm); ????????//?獲取消息數(shù)字摘要的字節(jié)數(shù)組 ????????byte[]?digest?=?messageDigest.digest(input.getBytes("UTF-8")); ????????//????????System.out.println(new?String(digest)); ????????//?base64編碼 //????????System.out.println(Base64.encode(digest)); ????????//?創(chuàng)建對(duì)象用來拼接 ????????StringBuilder?sb?=?new?StringBuilder(); ????????for?(byte?b?:?digest)?{ ????????????//?轉(zhuǎn)成?16進(jìn)制 ????????????String?s?=?Integer.toHexString(b?&?0xff); ????????????//System.out.println(s); ????????????if?(s.length()?==?1){ ????????????????//?如果生成的字符只有一個(gè),前面補(bǔ)0 ????????????????s?=?"0"+s; ????????????} ????????????sb.append(s); ????????} ????????System.out.println(sb.toString()); ????} }
運(yùn)行,結(jié)果和在線一致:
2.2.3 其他消息摘要算法
/** ?*?功能描述 ?* ?*?@author?cWX970190 ?*?@since?2020-10-11 ?*/ import?java.security.MessageDigest; /** ?*?DigestDemo1 ?* ?*?@Author:?陳志強(qiáng) ?*?@CreateTime:?2020-03-17 ?*?@Description: ?*/ public?class?DigestDemo1?{ ????public?static?void?main(String[]?args)?throws?Exception{ ????????//?4124bc0a9335c27f086f24ba207a4912?????md5?在線校驗(yàn) ????????//?QSS8CpM1wn8IbyS6IHpJEg==?????????????消息摘要使用的是16進(jìn)制 ????????//?原文 ????????String?input?=?"aa"; ????????//?算法 ????????String?algorithm?=?"MD5"; ????????//?獲取數(shù)字摘要對(duì)象 ????????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)建對(duì)象用來拼接 ????????StringBuilder?sb?=?new?StringBuilder(); ????????for?(byte?b?:?digest)?{ ????????????//?轉(zhuǎn)成?16進(jìn)制 ????????????String?s?=?Integer.toHexString(b?&?0xff); ????????????if?(s.length()?==?1){ ????????????????//?如果生成的字符只有一個(gè),前面補(bǔ)0 ????????????????s?=?"0"+s; ????????????} ????????????sb.append(s); ????????} ????????System.out.println("16進(jìn)制數(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); ????} }
運(yùn)行:
----未完,待續(xù)---
【人人都懂密碼學(xué)】一篇最易懂的Java密碼學(xué)入門教程(中)
【人人都懂密碼學(xué)】一篇最易懂的Java密碼學(xué)入門教程(下)
Java
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。