個人部落格歡迎通路
總結不易,如果對你有幫助,請點贊關注支援一下
微信搜尋程式dunk,關注公衆号,擷取部落格源碼、資料結構與算法筆記、面試筆試題
本文加密算法源碼
文章目錄
- 密碼學淺嘗辄止
- 密碼學的基本概念
- 古典密碼學
-
- 替換法
- 移位法
- 恺撒加密
-
- 中國古代加密
- 外國加密
- 頻度分析法破解恺撒加密
-
- 密碼棒
- 頻率分析解密法
-
- 建立工具類負責檔案的寫入和讀取
- 破解密碼
- 古典密碼破解方式
-
- 頻率分析
- 近代密碼學
-
- 恩尼格瑪機
- 現代密碼學
-
- HTTPS的通信過程
- 常見的加密方式
-
- 對稱加密
-
- DES加密解密
- Base64
-
- base64 構成原則
- 原理
- AES加密解密
- 加密模式
-
- ECB
- CBC
- 填充模式
-
- NoPadding
- PKCS5Padding
- Tips
- 非對稱密碼
-
- 常見算法
-
- RSA
- ECC
- 散列函數
-
- 資訊摘要
-
- 特點
- 常見算法 :
- 數字簽名
-
- 基本原理
- 數字證書
- 網頁加密
- 如何設定密碼才安全
密碼學淺嘗辄止
密碼學主要研究編制密碼 和 破譯密碼的學科
密碼學的主要目的:研究如何隐藏資訊并且把資訊傳遞出去的一個學科
密碼學的基本概念
密碼在我們的生活中有着重要的作用,那麼密碼究竟來自何方,為何會産生呢?
密碼學是網絡安全、資訊安全、區塊鍊等産品的基礎,常見的非對稱加密、對稱加密、散列函數等,都屬于密碼學範疇。
密碼學有數千年的曆史,從最開始的替換法到如今的非對稱加密算法,經曆了古典密碼學,近代密碼學和現代密碼學三個階段。密碼學不僅僅是數學家們的智慧,更是如今網絡空間安全的重要基礎。
古典密碼學
在古代的戰争中,多見使用隐藏資訊的方式保護重要的通信資料。比如先把需要保護的資訊用化學藥水寫到紙上,藥水幹後,紙上看不出任何的資訊,需要使用另外的化學藥水塗抹後才可以閱讀紙上的資訊。
這些方法都是在保護重要的資訊不被他人擷取,但藏資訊的方式比較容易被他人識破,例如增加哨兵的排查力度,就會發現其中的存在某種問題或陰謀,因而随後發展出了較難破解的古典密碼學。
替換法
替換法很好了解,就是用固定的資訊将原文替換成無法直接閱讀的密文資訊。例如将 b 替換成 w ,e 替換成p ,這樣bee 單詞就變換成了wpp,不知道替換規則的人就無法閱讀出原文的含義。
替換法有單表替換和多表替換兩種形式。單表替換即隻有一張原文密文對照表單,發送者和接收者用這張表單來加密解密。在上述例子中,表單即為:a b c d e - s w t r p 。
多表替換即有多張原文密文對照表單,不同字母可以用不同表單的内容替換。
例如約定好表單為:表單 1:abcde-swtrp 、表單2:abcde-chfhk 、表單 3:abcde-jftou。
規定第一個字母用第三張表單,第二個字母用第一張表單,第三個字母用第二張表單,這時 bee單詞就變成了
(312)fpk ,破解難度更高,其中 312 又叫做密鑰,密鑰可以事先約定好,也可以在傳輸過程中标記出來。
移位法
移位法就是将原文中的所有字母都在字母表上向後(或向前)按照一個固定數目進行偏移後得出密文,典型的移位法應用有 “ 恺撒密碼 ”。
例如約定好向後移動2位(abcde - cdefg),這樣 bee 單詞就變換成了dgg 。
同理替換法,移位法也可以采用多表移位的方式,典型的多表案例是“維尼吉亞密碼”(又譯維熱納爾密碼),屬于多表密碼的一種形式。
恺撒加密
中國古代加密
公元683年,唐中宗即位。随後,武則天廢唐中宗,立第四子李旦為皇帝,但朝政大事均由她自己專斷。裴炎、徐敬業和駱賓王等人對此非常不滿。徐敬業聚兵十萬,在江蘇揚州起兵。裴炎做内應,欲以拆字手段為其傳遞秘密資訊。後因有人告密,裴炎被捕,未發出的密信落到武則天手中。這封密信上隻有“青鵝”二字,群臣對此大惑不解。武則天破解了“青鵝”的秘密:“青”字拆開來就是“十二月”,而“鵝”字拆開來就是“我自與”。密信的意思是讓徐敬業、駱賓王等率兵于十二月進發,裴炎在内部接應。“青鵝”破譯後,裴炎被殺。接着,武則天派兵擊敗了徐敬業和駱賓王。
外國加密
在密碼學中,恺撒密碼是一種最簡單且最廣為人知的加密技術。
凱撒密碼最早由古羅馬軍事統帥蓋烏斯·尤利烏斯·凱撒在軍隊中用來傳遞加密資訊,故稱凱撒密碼。這是一種位移加密方式,隻對26個字母進行位移替換加密,規則簡單,容易破解。下面是位移1次的對比:
将明文字母表向後移動1位,A變成了B,B變成了C……,Z變成了A。
同理,若将明文字母表向後移動3位:則A變成了D,B變成了E……,Z變成了C。
字母表最多可以移動25位。凱撒密碼的明文字母表向後或向前移動都是可以的,通常表述為向後移動,如果要向前移動1位,則等同于向後移動25位,位移選擇為25即可。
它是一種替換加密的技術,明文中的所有字母都在字母表上向後(或向前)按照一個固定數目進行偏移後被替換成密文。
例如,當偏移量是3的時候,所有的字母A将被替換成D,B變成E,以此類推。
這個加密方法是以恺撒的名字命名的,當年恺撒曾用此方法與其将軍們進行聯系。恺撒密碼通常被作為其他更複雜的加密方法中的一個步驟。簡單來說就是當秘鑰為n,其中一個待加密字元ch,加密之後的字元為ch+n,當ch+n超過’z’時,回到’a’計數。
/**
* @author :zsy
* @date :Created 2021/6/16 23:36
* @description:凱撒加密
*/
public class KaiSerDemo {
public static void main(String[] args) {
String encryptData = encryptKaiser("Hello World", 3);
System.out.println("加密後:" + encryptData);
String decryptDate = decryptKaiSer(encryptData, 3);
System.out.println("解密後:" + decryptDate);
}
public static String encryptKaiser(String orignal, int key) {
StringBuilder builder = new StringBuilder();
char[] chars = orignal.toCharArray();
for (char c : chars) {
//轉換為ascii碼值
int asciiCode = c;
//偏移資料
asciiCode += key;
//轉換回編碼
char newChar = (char)asciiCode;
builder.append(newChar);
}
return builder.toString();
}
public static String decryptKaiSer(String encryptData, int key) {
StringBuilder builder = new StringBuilder();
char[] chars = encryptData.toCharArray();
for (char c : chars) {
//轉換為ascii碼值
int asciiCode = c;
//偏移資料
asciiCode -= key;
//轉換回編碼
char newChar = (char)asciiCode;
builder.append(newChar);
}
return builder.toString();
}
}
頻度分析法破解恺撒加密
密碼棒
公元前5世紀的時候,斯巴達人利用一根木棒,纏繞上皮革或者羊皮紙,在上面橫向寫下資訊,解下這條皮帶。展開來看,這長串字母沒有任何意義。信差可以将這條皮帶當成腰帶,系在腰上。
然後收件人将這條皮帶纏繞在相同的木棒上,就能恢複資訊了。
前404年,一位遍體鱗傷的信差來到斯巴達将領利桑德面前,這趟波斯之旅隻有他和四位同伴幸存,利桑德接下腰帶,纏繞到他的密碼棒上,得知波斯的發那巴祖斯準備侵襲他,多虧密碼棒利桑德才能夠預先防範,擊退敵軍。
頻率分析解密法
加密者選擇将組成資訊的字母替代成别的字母,比如說将a寫成1,這樣就不能被解密者直接拿到資訊了。
這難不倒解密者,以英文字母為例,為了确定每個英文字母的出現頻率,分析一篇或者數篇普通的英文文章,英文字母出現頻率最高的是e,接下來是t,然後是a……,然後檢查要破解的密文,也将每個字母出現的頻率整理出來,假設密文中出現頻率最高的字母是j,那麼就可能是e的替身,如果密碼文中出現頻率次高的但是P,那麼可能是t的替身,以此類推便就能解開加密資訊的内容。這就是頻率分析法。
- 将明文字母的出現頻率與密文字母的頻率相比較的過程
- 通過分析每個符号出現的頻率而輕易地破譯代換式密碼
- 在每種語言中,冗長的文章中的字母表現出一種可對之進行分辨的頻率。
- e是英語中最常用的字母,其出現頻率為八分之一
建立工具類負責檔案的寫入和讀取
public class Util {
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");
}
}
破解密碼
/**
* 頻率分析法破解凱撒密碼
*/
public class FrequencyAnalysis {
//英文裡出現次數最多的字元
private static final char MAGIC_CHAR = 'e';
//破解生成的最大檔案數
private static final int DE_MAX_FILE = 4;
public static void main(String[] args) throws Exception {
//測試1,統計字元個數
//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");
}
public static void printCharCount(String path) throws IOException{
String data = Util.file2String(path);
List<Entry<Character, Integer>> mapList = getMaxCountChar(data);
for (Entry<Character, Integer> entry : mapList) {
//輸出前幾位的統計資訊
System.out.println("字元'" + entry.getKey() + "'出現" + entry.getValue() + "次");
}
}
public static void encryptFile(String srcFile, String destFile, int key) throws IOException {
String artile = Util.file2String(srcFile);
//加密檔案
String encryptData = KaiserDemo.encrypt(artile, key);
//儲存加密後的檔案
Util.string2File(encryptData, destFile);
}
/**
* 破解凱撒密碼
* @param input 資料源
* @return 傳回解密後的資料
*/
public static void decryptCaesarCode(String input, String destPath) {
int deCount = 0;//目前解密生成的備選檔案數
//擷取出現頻率最高的字元資訊(出現次數越多越靠前)
List<Entry<Character, Integer>> mapList = getMaxCountChar(input);
for (Entry<Character, Integer> entry : mapList) {
//限制解密檔案備選數
if (deCount >= DE_MAX_FILE) {
break;
}
//輸出前幾位的統計資訊
System.out.println("字元'" + entry.getKey() + "'出現" + entry.getValue() + "次");
++deCount;
//出現次數最高的字元跟MAGIC_CHAR的偏移量即為秘鑰
int key = entry.getKey() - MAGIC_CHAR;
System.out.println("猜測key = " + key + ", 解密生成第" + deCount + "個備選檔案" + "\n");
String decrypt = KaiserDemo.decrypt(input, key);
String fileName = "de_" + deCount + destPath;
Util.string2File(decrypt, fileName);
}
}
//統計String裡出現最多的字元
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);
}
}
//輸出統計資訊
/*for (Entry<Character, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + "出現" + entry.getValue() + "次");
}*/
//擷取擷取最大值
int maxCount = 0;
for (Entry<Character, Integer> entry : map.entrySet()) {
//不統計空格
if (/*entry.getKey() != ' ' && */entry.getValue() > maxCount) {
maxCount = entry.getValue();
}
}
//map轉換成list便于排序
List<Entry<Character, Integer>> mapList = new ArrayList<Entry<Character,Integer>>(map.entrySet());
//根據字元出現次數排序
Collections.sort(mapList, new Comparator<Entry<Character, Integer>>(){
@Override
public int compare(Entry<Character, Integer> o1,
Entry<Character, Integer> o2) {
return o2.getValue().compareTo(o1.getValue());
}
});
return mapList;
}
}
字元'#'出現989次
猜測key = -66, 解密生成第1個備選檔案
字元'h'出現478次
猜測key = 3, 解密生成第2個備選檔案
字元'd'出現344次
猜測key = -1, 解密生成第3個備選檔案
字元'w'出現327次
猜測key = 18, 解密生成第4個備選檔案
古典密碼破解方式
古典密碼雖然很簡單,但是在密碼史上是使用的最久的加密方式,直到“機率論”的數學方法被發現,古典密碼就被破解了。
多表的替換法或移位法雖然難度高一些,但如果資料量足夠大的話,也是可以破解的。以維尼吉亞密碼算法為例,破解方法就是先找出密文中完全相同的字母串,猜測密鑰長度,得到密鑰長度後再把同組的密文放在一起,使用頻率分析法破解。
頻率分析
頻率分析在數學、實體學和信号進行中是一種分解函數、波形、或者信号的頻率組成,以擷取頻譜的方法。在密碼學中,頻率分析是指研究字母或者字母組合在文本中出現的頻率。應用頻率分析可以破解古典密碼。
英文單詞中字母出現的頻率是不同的,e以12.702%的百分比占比最高,z 隻占到0.074%,感興趣的可以去百科查字母頻率詳細統計資料。如果密文數量足夠大,僅僅采用頻度分析法就可以破解單表的替換法或移位法。
近代密碼學
古典密碼的安全性受到了威脅,外加使用便利性較低,到了工業化時代,近現代密碼被廣泛應用。
恩尼格瑪機
恩尼格瑪密碼機,在密碼學史中是一種用于加密與解密檔案的密碼機。确切地說,恩尼格瑪是一系列相似的轉子機械的統稱,它包括了一系列不同的型号。恩尼格瑪在1920年代早期開始被用于商業,也被一些國家的軍隊與政府采用過,在這些國家中,最著名的是第二次世界大戰時的納粹德國。恩尼格瑪密碼機的大部分設定都會在一段時間(一般為一天)以後被更換。
恩尼格瑪機是二戰時期納粹德國使用的加密機器,後被英國破譯,參與破譯的人員有被稱為計算機科學之父、人工智能之父的圖靈。恩尼格瑪機使用的加密方式本質上還是移位和替代,隻不過因為密碼表種類極多,破解難度高,同時加密解密機器化,使用便捷,因而在二戰時期得以使用。
現代密碼學
HTTPS的通信過程
https通信是建立在SSL連接配接層之上的請求和響應,用戶端将加密元件發送到伺服器端,服務端進行比對後将數字證書等資訊發送到用戶端,用戶端進行證書驗證,驗證通過後使用非對稱加密對資料的密鑰進行協商,協商後得到對稱的加密密鑰,然後使用對稱算法進行TCP連結,然後與用戶端進行三次握手後,進行資料傳輸,傳輸完成後,四次揮手,斷開連結,通信結束。
- 用戶端和伺服器端通過TCP建立來連接配接,發送https請求
- 伺服器響應請求,并将數字證書發送給用戶端,數字證書包括公共密鑰、域名、申請證書的公司
- 用戶端收到伺服器端的數字證書之後,會驗證數字證書的合法性。
- 如果公鑰合格,那麼用戶端會生成client key,一個用于進行對稱加密的密鑰,并用伺服器的公鑰對用戶端密鑰進行非對稱加密。
- 用戶端會再次發起請求,将加密之後的用戶端密鑰發送給伺服器
- 伺服器接受加密文後,會用私鑰對其進行非對稱解密,得到用戶端秘鑰,并使用用戶端密鑰進行對稱加密,生成密鑰文并發送
- 用戶端收到密文,并使用用戶端密鑰進行解密獲得資料
常見的加密方式
對稱加密
對稱密碼應用了相同的加密密鑰和解密密鑰。對稱密碼分為:序列密碼(流密碼),分組密碼(塊密碼)兩種。流密碼是對資訊流中的每一個元素(一個字母或一個比特)作為基本的處理單元進行加密,塊密碼是先對資訊流分塊,再對每一塊分别加密。
例如原文為1234567890,流加密即先對1進行加密,再對2進行加密,再對3進行加密……最後拼接成密文;塊加密先分成不同的塊,如1234成塊,5678成塊,90XX(XX為補位數字)成塊,再分别對不同塊進行加密,最後拼接成密文。前文提到的古典密碼學加密方法,都屬于流加密。
- 采用單鑰密碼系統的加密方法,同一個密鑰可以同時用作資訊的加密和解密,這種加密方法稱為對稱加密,也稱為單密鑰加密。
- 示例
- 我們現在有一個原文3要發送給B
- 設定密鑰為108, 3 * 108 = 324, 将324作為密文發送給B
- B拿到密文324後, 使用324/108 = 3 得到原文
- 常見加密算法
- DES : Data Encryption Standard,即資料加密标準,是一種使用密鑰加密的塊算法,1977年被美國聯邦政府的國家标準局确定為聯邦資料處理标準(FIPS),并授權在非密級政府通信中使用,随後該算法在國際上廣泛流傳開來。
- AES : Advanced Encryption Standard, 進階加密标準 .在密碼學中又稱Rijndael加密法,是美國聯邦政府采用的一種區塊加密标準。這個标準用來替代原先的DES,已經被多方分析且廣為全世界所使用。
- 特點
- 加密速度快, 可以加密大檔案
- 密文可逆, 一旦密鑰檔案洩漏, 就會導緻資料暴露
- 加密後編碼表找不到對應字元, 出現亂碼
- 一般結合Base64使用
DES加密解密
加密的秘鑰必須為8個位元組,原文是8位元組的整數倍
/**
* @author :zsy
* @date :Created 2021/6/17 9:51
* @description:Des加密
*/
public class DesDemo {
public static void main(String[] args) throws Exception {
String input = "計算機網絡";
//加密密鑰
String key = "12345678";
//加密模式
String transformation = "DES";
//加密算法
String algorithm = "DES";
String encryptDES = encryptDES(input, key, transformation, algorithm);
System.out.println("加密後:" + encryptDES);
String decryptDES = decryptDES(encryptDES, key, transformation , algorithm);
System.out.println("解密後:" + decryptDES);
}
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {
//擷取加密對象
Cipher cipher = Cipher.getInstance(transformation);
//加密規則
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
//初始化加密模式和算法
cipher.init(Cipher.ENCRYPT_MODE, sks);
//加密
byte[] bytes = cipher.doFinal(input.getBytes());
String encode = Base64.encode(bytes);
return encode;
}
private static String decryptDES(String encryptDES, String key, String transformation, String algorithm) throws Exception {
//擷取加密對象
Cipher cipher = Cipher.getInstance(transformation);
//加密規則
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
//初始化加密模式和算法
cipher.init(Cipher.DECRYPT_MODE, sks);
//加密
byte[] bytes = cipher.doFinal(Base64.decode(encryptDES.getBytes()));
return new String(bytes);
}
}
加密後:gMC7DtqHnrg23svEN77jNQ==
解密後:計算機網絡
Base64
Base64是網絡上最常見的用于傳輸8Bit位元組碼的可讀性編碼算法之一,可讀性編碼算法不是為了保護資料的安全性,而是為了可讀性,可讀性編碼不改變資訊内容,隻改變資訊内容的表現形式,所謂Base64,即是說在編碼過程中使用了64種字元:大寫A到Z、小寫a到z、數字0到9、“+”和“/”,Base58是Bitcoin(比特币)中使用的一種編碼方式,主要用于産生Bitcoin的錢包位址,相比Base64,Base58不使用數字"0",字母大寫"O",字母大寫"I",和字母小寫"i",以及"+“和”/"符号
base64 構成原則
- 小寫 a - z = 26個字母
- 大寫 A - Z = 26個字母
- 數字 0 - 9 = 10 個數字
- / + =2個符号
原理
三個位元組為一組,一個位元組8位,一共就是24位,base64将三個位元組,轉換成4組,每組6位,一個位元組應該是8位,缺少兩位,在高位補0,保證了base64的資料在0~63之間(000 000 ~ 111 111)
如果當我們的位數不夠的時候,會使用等号來補齊
System.out.println(Base64.encode("1".getBytes()));//MQ==
System.out.println(Base64.encode("12".getBytes()));//MTI=
System.out.println(Base64.encode("123".getBytes()));//MTIz
//UTF-8編碼,一個中文占3個位元組,4*3*8 = 96 96 % 6 = 0
System.out.println(Base64.encode("網絡安全".getBytes()));//572R57uc5a6J5YWo
AES加密解密
加密的秘鑰必須為16個位元組,原文是16位元組的整數倍
/**
* @author :zsy
* @date :Created 2021/6/17 11:09
* @description:Aes加密
*/
public class AesDemo {
public static void main(String[] args) throws Exception {
String input = "計算機網絡";
//加密密鑰
String key = "1234567812345678";
//加密模式
String transformation = "AES";
//加密算法
String algorithm = "AES";
String encryptDES = encryptDES(input, key, transformation, algorithm);
System.out.println("加密後:" + encryptDES);
String decryptDES = decryptDES(encryptDES, key, transformation , algorithm);
System.out.println("解密後:" + decryptDES);
}
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {
//擷取加密對象
Cipher cipher = Cipher.getInstance(transformation);
//加密規則
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
//初始化加密模式和算法
cipher.init(Cipher.ENCRYPT_MODE, sks);
//加密
byte[] bytes = cipher.doFinal(input.getBytes());
String encode = Base64.encode(bytes);
return encode;
}
private static String decryptDES(String encryptDES, String key, String transformation, String algorithm) throws Exception {
//擷取加密對象
Cipher cipher = Cipher.getInstance(transformation);
//加密規則
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
//初始化加密模式和算法
cipher.init(Cipher.DECRYPT_MODE, sks);
//加密
byte[] bytes = cipher.doFinal(Base64.decode(encryptDES.getBytes()));
return new String(bytes);
}
}
加密模式
ECB
ECB : Electronic codebook, 電子密碼本. 需要加密的消息按照塊密碼的塊大小被分為數個塊,并對每個塊進行獨立加密
- 優點 : 可以并行處理資料
- 缺點 : 同樣的原文生成同樣的密文, 不能很好的保護資料
- 同時加密,原文是一樣的,加密出來的密文也是一樣的
CBC
CBC : Cipher-block chaining, 密碼塊連結. 每個明文塊先與前一個密文塊進行異或後,再進行加密。在這種方法中,每個密文塊都依賴于它前面的所有明文塊
Initialization Vector(IV,初始化向量)
- 優點 : 同樣的原文生成的密文不一樣
- 缺點 : 串行處理資料
填充模式
當需要按塊處理的資料, 資料長度不符合塊處理需求時, 按照一定的方法填充滿塊長的規則
NoPadding
不填充.
在DES加密算法下, 要求原文長度必須是8byte的整數倍
在AES加密算法下, 要求原文長度必須是16byte的整數倍
PKCS5Padding
資料塊的大小為8位, 不夠就補足
Tips
預設情況下, 加密模式和填充模式為 : ECB/PKCS5Padding
如果使用CBC模式, 在初始化Cipher對象時, 需要增加參數, 初始化向量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)
DES/CBC/PKCS5Padding
/**
* @author :zsy
* @date :Created 2021/6/17 9:51
* @description:Des加密
*/
public class DesDemo {
public static void main(String[] args) throws Exception {
String input = "計算機網絡";
//加密密鑰
String key = "12345678";
//加密模式
//String transformation = "DES/ECB/PKCS5Padding";
String transformation = "DES/CBC/PKCS5Padding";
//加密算法
String algorithm = "DES";
String encryptDES = encryptDES(input, key, transformation, algorithm);
System.out.println("加密後:" + encryptDES);
String decryptDES = decryptDES(encryptDES, key, transformation , algorithm);
System.out.println("解密後:" + decryptDES);
}
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {
//擷取加密對象
Cipher cipher = Cipher.getInstance(transformation);
//加密規則
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
//建立IV向量
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
//初始化加密模式和算法
cipher.init(Cipher.ENCRYPT_MODE, sks,iv);
//加密
byte[] bytes = cipher.doFinal(input.getBytes());
String encode = Base64.encode(bytes);
return encode;
}
private static String decryptDES(String encryptDES, String key, String transformation, String algorithm) throws Exception {
//擷取加密對象
Cipher cipher = Cipher.getInstance(transformation);
//加密規則
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
//建立IV向量
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
//初始化加密模式和算法
cipher. init(Cipher.DECRYPT_MODE, sks, iv);
//加密
byte[] bytes = cipher.doFinal(Base64.decode(encryptDES.getBytes()));
return new String(bytes);
}
}
非對稱密碼
對稱密碼的密鑰安全極其重要,加密者和解密者需要提前協商密鑰,并各自確定密鑰的安全性,一但密鑰洩露,即使算法是安全的也無法保障原文資訊的私密性。在實際的使用中,遠端的提前協商密鑰不容易實作,即使協商好,在遠端傳輸過程中也容易被他人擷取,是以非對稱密鑰此時就凸顯出了優勢。
非對稱密碼有兩支密鑰,公鑰(publickey)和私鑰(privatekey),加密和解密運算使用的密鑰不同。用公鑰對原文進行加密後,需要由私鑰進行解密;用私鑰對原文進行加密後(此時一般稱為簽名),需要由公鑰進行解密(此時一般稱為驗簽)。公鑰可以公開的,大家使用公鑰對資訊進行加密,再發送給私鑰的持有者,私鑰持有者使用私鑰對資訊進行解密,獲得資訊原文。因為私鑰隻有單一人持有,是以不用擔心被他人解密擷取資訊原文。
- 非對稱加密算法又稱
。現代加密算法
- 非對稱加密是計算機通信安全的基石,保證了加密資料
。不會被破解
- 與對稱加密算法不同,非對稱加密算法需要兩個密鑰:
和公開密鑰(publickey)
私有密鑰(privatekey)
- 公開密鑰和私有密鑰是
一對
- 如果用
對資料進行公開密鑰
,隻有用加密
才能對應的私有密鑰
。解密
- 如果用
對資料進行私有密鑰
,隻有用加密
才能對應的公開密鑰
。解密
- 因為加密和解密使用的是兩個
的密鑰,是以這種算法叫作不同
。非對稱加密算法
舉例
- 首先生成密鑰對, 公鑰為(5,14), 私鑰為(11,14)
- 現在A希望将原文2發送給B
- A使用公鑰加密資料. 2的5次方mod 14 = 4 , 将密文4發送給B
- B使用私鑰解密資料. 4的11次方mod14 = 2, 得到原文2
特點
- 加密和解密使用不同的密鑰
- 如果使用私鑰加密, 隻能使用公鑰解密
- 如果使用公鑰加密, 隻能使用私鑰解密
- 處理資料的速度較慢, 因為安全級别高
常見算法
RSA
RSA算法是一種基于大數不可能質數分解假設的公鑰體系。簡單的說,就是找兩個很大的質數,一個公開給世界,稱之為“公鑰”,另一個不告訴任何人,稱之為“私鑰”。兩把密鑰互補——用公鑰加密的密文可以使用私鑰解密,反過來也一樣。假設A寄信給B,他們知道對方的公鑰,A可用B的公鑰加密郵件寄出,B收到後用自己的私鑰解出A的原文,這樣就保證了郵件的安全性。
- 生成兩個大素數p和q
- 計算這兩個素數的乘積n = p * q
- 計算小于n并且與n互質的整數的個數,即歐拉函數φ(n) = (p - 1)(q- 1)
- 選擇一個随機數e 滿足1 < e < φ(n),并且e和φ(n)互質,即gcd(e, φ(n)) = 1
- 計算de = 1 mod φ(n)
- 保密d,p和q,公開n和e
明文以分組的形式加密,每一個分組小于log2n
加密x:c = x ^ e mod n 就可以得到密文c
解密c:x = c ^ d mod n 就可以得到明文x
公鑰:(e, n)
私鑰:(d, n)
/**
* @author :zsy
* @date :Created 2021/6/17 22:56
* @description:RSA算法
*/
public class RSADemo {
public static void main(String[] args) throws Exception {
String input = "計算機網絡安全";
// 加密算法
String algorithm = "RSA";
generateKeyToFile(algorithm, "a.pub", "a.pri");
String encrypted = encryptRSA(algorithm, "a.pub", input);
System.out.println("公鑰加密後:" + encrypted);
String original = decryptRSA(algorithm, "a.pri", encrypted);
System.out.println("私鑰解密後:" + original);
}
/**
* 生成公鑰和私鑰并儲存檔案
* @param algorithm
* @param pubPath
* @param priPath
* @throws Exception
*/
public 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[] publicKeyEncoded = publicKey.getEncoded();
byte[] privateKeyEncoded = privateKey.getEncoded();
//進行Base編碼
String publicKeyString = Base64.encode(publicKeyEncoded);
String privateKeyString = Base64.encode(privateKeyEncoded);
//System.out.println("私鑰:" + privateKeyString);
//持久化檔案
FileUtils.write(new File(pubPath), publicKeyString, Charset.forName("UTF-8"));
FileUtils.write(new File(priPath), privateKeyString, Charset.forName("UTF-8"));
}
/**
* 擷取檔案中的公鑰和私鑰
* @param algorithm
* @param path
* @return
* @throws Exception
*/
public static Key getKey(String algorithm, String path) throws Exception {
// 将檔案内容轉為字元串
String keyString = FileUtils.readFileToString(new File(path), Charset.defaultCharset());
// 擷取密鑰工廠
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 建構密鑰規範 進行Base64解碼
EncodedKeySpec spec;
if (path.contains("pub")) {
spec = new X509EncodedKeySpec(Base64.decode(keyString));
} else {
spec = new PKCS8EncodedKeySpec(Base64.decode(keyString));
}
// 生成密鑰
return path.contains("pub") ?
keyFactory.generatePublic(spec) : keyFactory.generatePrivate(spec);
}
/**
* 使用公鑰加密
* @param algorithm
* @param pubPath
* @param input
* @return
* @throws Exception
*/
private static String encryptRSA(String algorithm, String pubPath, String input) throws Exception {
//擷取公鑰
PublicKey publicKey = (PublicKey) getKey(algorithm, pubPath);
//System.out.println(Base64.encode(publicKey.getEncoded()));
//從檔案中擷取公鑰
String publicKeyString = FileUtils.readFileToString(new File(pubPath), Charset.forName("UTF-8"));
byte[] keyBytes = Base64.decode(publicKeyString.getBytes());
//擷取加密對象
Cipher cipher = Cipher.getInstance(algorithm);
//初始化加密對象
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
//加密
byte[] bytes = cipher.doFinal(input.getBytes());
return Base64.encode(bytes);
}
/**
* 使用私鑰解密
* @param algorithm
* @param priPath
* @param encrypted
* @return
* @throws Exception
*/
private static String decryptRSA(String algorithm, String priPath, String encrypted) throws Exception{
//擷取私鑰
PrivateKey privateKey = (PrivateKey) getKey(algorithm, priPath);
//System.out.println("檔案擷取私鑰:" + Base64.encode(privateKey.getEncoded()));
//擷取加密對象
Cipher cipher = Cipher.getInstance(algorithm);
//初始化加密對象
cipher.init(Cipher.DECRYPT_MODE, privateKey);
//解密
byte[] bytes = cipher.doFinal(Base64.decode(encrypted.getBytes()));
return new String(bytes);
}
}
公鑰加密後:AF9/11D7QVi7Z4cQqPcpRfLBt+/QPdDGtMPGcqaErvbNerSzZQNRlQijmudyW0PgEIGE56+d3N8R
jy8TfVcoNs6Xpj6DhwBVEvb23ebpUVTV/6buiONXxtyoW0tpAzGY2hB2XaaGB+pOsHG5tiwAxmTE
uk9H4B8HiroBTyNCan4=
私鑰解密後:計算機網絡安全
ECC
散列函數
散列函數,也見雜湊函數、摘要函數或哈希函數,可将任意長度的消息經過運算,變成固定長度數值,常見的有MD5、SHA-1、SHA256,多應用在檔案校驗,數字簽名中。
MD5 可以将任意長度的原文生成一個128位(16位元組)的哈希值
SHA-1可以将任意長度的原文生成一個160位(20位元組)的哈希值
資訊摘要
- 消息摘要(Message Digest)又稱為數字摘要(Digital Digest)
- 它是一個唯一對應一個消息或文本的固定長度的值,它由一個單向Hash加密函數對消息進行作用而産生
- 使用數字摘要生成的值是不可以篡改的,為了保證檔案或者值的安全
特點
無論輸入的消息有多長,計算出來的消息摘要的長度總是固定的。例如應用MD5算法摘要的消息有128個比特位,用SHA-1算法摘要的消息最終有160比特位的輸出,隻要輸入的消息不同,對其進行摘要以後産生的摘要消息也必不相同;但相同的輸入必會産生相同的輸出,消息摘要是單向、不可逆的
百度搜尋
tomcat
,進入官網下載下傳 ,會經常發現有
sha1
,
sha512
, 這些都是數字摘要
常見算法 :
- MD5算法 : 摘要結果16個位元組, 轉16進制後32個位元組
- SHA1算法 : 摘要結果20個位元組, 轉16進制後40個位元組
- SHA256算法 : 摘要結果32個位元組, 轉16進制後64個位元組
- SHA512算法 : 摘要結果64個位元組, 轉16進制後128個位元組
/**
* @author :zsy
* @date :Created 2021/6/17 15:59
* @description:消息摘要
*/
public class DigestDemo1 {
public static void main(String[] args) throws NoSuchAlgorithmException {
String input = "aa";
//算法
String algorithm = "MD5";
String encryptMD5 = getDigest(input, algorithm);
System.out.println(encryptMD5);
String encryptSHA1 = getDigest(input, "SHA-1");
System.out.println(encryptSHA1);
String encryptSHA256 = getDigest(input, "SHA-256");
System.out.println(encryptSHA256);
String encryptSHA512 = getDigest(input, "SHA-512");
System.out.println(encryptSHA512);
}
public static String getDigest(String input, String algorithm) throws NoSuchAlgorithmException {
//建立消息摘要對象
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
//執行消息摘要算法
byte[] digest = messageDigest.digest(input.getBytes());
System.out.println(algorithm + "加密密文長度:" + digest.length);
return toHex(digest);
}
private static String toHex(byte[] digest) {
StringBuilder builder = new StringBuilder();
for (byte b : digest) {
String string = Integer.toHexString(b & 0xff);
string = string.length() > 1 ? string : "0" + string;
builder.append(string);
}
return builder.toString();
}
}
數字簽名
數字簽名(又稱公鑰數字簽名)是隻有資訊的發送者才能産生的别人無法僞造的一段數字串,這段數字串同時也是對資訊的發送者發送資訊真實性的一個有效證明。它是一種類似寫在紙上的普通的實體簽名,但是使用了公鑰加密領域的技術來實作的,用于鑒别數字資訊的方法。一套數字簽名通常定義兩種互補的運算,一個用于簽名,另一個用于驗證。數字簽名是非對稱密鑰加密技術與數字摘要技術的應用。
含義是:在網絡中傳輸資料時候,給資料添加一個數字簽名,表示是誰發的資料,而且還能證明資料沒有被篡改。
數字簽名的主要作用就是保證了資料的有效性(驗證是誰發的)和完整性(證明資訊沒有被篡改)。下面我們就來好好地看一下他的底層實作原理是什麼樣子的。
基本原理
為了了解得清楚,我們通過案例一步一步來講解。話說張三有倆好哥們A、B。由于工作原因,張三和AB寫郵件的時候為了安全都需要加密。于是張三想到了數字簽名:
整個思路是這個樣子的:
第一步:加密采用非對稱加密,張三有三把鑰匙,兩把公鑰,送給朋友。一把私鑰留給自己。
第二步:A或者B寫郵件給張三:A先用公鑰對郵件加密,然後張三收到郵件之後使用私鑰解密。
第三步:張三寫郵件給A或者B:
(1)張三寫完郵件,先用hash函數生成郵件的摘要,附着在文章上面,這就完成了數字簽名,然後張三再使用私鑰加密。就可以把郵件發出去了。
(2)A或者是B收到郵件之後,先把數字簽名取下來,然後使用自己的公鑰解密即可。這時候取下來的數字簽名中的摘要若和張三的一緻,那就認為是張三發來的,再對信件本身使用Hash函數,将得到的結果,與上一步得到的摘要進行對比。如果兩者一緻,就證明這封信未被修改過。
上面的流程我們使用一張圖來示範一下:
首先把公鑰送給朋友A和B:
還有就是最後一個比較麻煩的,張三給A或者B發郵件:
數字證書
上面提到我們對簽名進行驗證時,需要用到公鑰。如果公鑰是僞造的,那我們無法驗證數字簽名了,也就根本不可能從數字簽名确定對方的合法性了。這時候證書就閃亮登場了。我們可能都有考各種證書的經曆,比如說國語證書,四六級證書等等,但是歸根結底,到任何場合我們都能拿出我們的證書來證明自己确實已經考過了國語,考過了四六級。這裡的證書也是同樣的道理。
如果不了解證書的作用,我們可以舉一個例子,比如說我們的畢業證書,任何公司都會承認。為什麼會承認?因為那是國家發得,大家都信任國家。也就是說隻要是國家的認證機構,我們都信任它是合法的。
那麼這個證書是如何生成的呢?我們再來看一張圖:
此時即使張三的朋友A把公鑰弄錯了,張三也可以通過這個證書驗證。
網頁加密
我們看一個應用“數字證書”的執行個體:https協定。這個協定主要用于網頁加密
首先,用戶端向伺服器發出加密請求。
如何設定密碼才安全
密碼不要太常見,不要使用類似于123456式的常用密碼。
各應用軟體密碼建議不同,撞庫:使用一個密碼去試其他的平台的密碼,避免出現一個應用資料庫被脫庫,全部應用密碼崩塌,
可在設定密碼時增加注冊時間、注冊地點、應用特性等方法。例如tianjin123456,表示在天津注冊的該應用。
參考資料:
密碼學
網絡安全原理及加密算法
MD5加密算法
幾種常見的加密算法
線上擷取消息摘要