目錄
- 1.1 ASCII編碼
- 1.2 凱撒加密
- 1.2.1 中國古代加密
- 1.2.2 外國加密
- 1.2.3 凱撒位移加密--JAVA代碼實作
- 1.2.4 頻度分析法破解愷撒加密
- 1.3現代常用的加密方式
- 1.3.1 對稱加密
- 1.3.2 DES加密
- 1.3.3 DES解密
- 1.3.4 AES加密解密
- 1.4 toString()與new String ()用法區別
1.1 ASCII編碼
ASCII(American Standard Code for Information Interchange,美國資訊交換標準代碼)是基于拉丁字母的一套電腦編碼系統,主要用于顯示現代英語和其他西歐語言,它是現今最通用的單位元組編碼系統,并等同于國際標準ISO/IEC 646,
1.2 凱撒加密
1.2.1 中國古代加密
看一個小故事 , 看看古人如何加密和解密:
公元683年,唐中宗即位,隨后,武則天廢唐中宗,立第四子李旦為皇帝,但朝政大事均由她自己專斷,
裴炎、徐敬業和駱賓王等人對此非常不滿,徐敬業聚兵十萬,在江蘇揚州起兵,裴炎做內應,欲以拆字手段為其傳遞秘密資訊,后因有人告密,裴炎被捕,未發出的密信落到武則天手中,這封密信上只有“青鵝”二字,群臣對此大惑不解,
武則天破解了“青鵝”的秘密:“青”字拆開來就是“十二月”,而“鵝”字拆開來就是“我自與”,密信的意思是讓徐敬業、駱賓王等率兵于十二月進發,裴炎在內部接應,“青鵝”破譯后,裴炎被殺,接著,武則天派兵擊敗了徐敬業和駱賓王,
之前有說過古代密碼學主要是替換和移位兩種操作,凱撒加密屬性移位加密,
替換法一般有單表替換法和多表替換法,
1.2.2 外國加密
在密碼學中,愷撒密碼是一種最簡單且最廣為人知的加密技術,
凱撒密碼最早由古羅馬軍事統帥蓋烏斯·尤利烏斯·凱撒在軍隊中用來傳遞加密資訊,故稱凱撒密碼,這是一種位移加密方式,只對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’計數,
1.2.3 凱撒位移加密--JAVA代碼實作
創建類 KaiserDemo,把 hello world 往右邊移動3位
public static void main(String[] args) {
// 定義原文
String input = "Hello World";
// 把原文右邊移動3位
int key = 3;
// 凱撒加密
String s = encrypt(input,key);
System.out.println("加密==" + s);
String s1 = decrypt(s,key);
System.out.println("明文=="+s1);
}
加入自定義的秘鑰加密和解密程序:
/**
* 解密
* @param s 密文
* @param key 密鑰
* @return
*/
public static String decrypt(String s, int key) {
char[] chars = s.toCharArray();
StringBuilder sb = new StringBuilder();
for (char aChar : chars) {
int b = aChar;
// 偏移資料
b -= key;
char newb = (char) b;
sb.append(newb);
}
return sb.toString();
}
/**
* 加密
* @param input 原文
* @return
*/
public static String encrypt(String input,int key) {
// 抽取快捷鍵 ctrl + alt + m
// 把字串變成位元組陣列
char[] chars = input.toCharArray();
StringBuilder sb = new StringBuilder();
for (char aChar : chars) {
int b = aChar;
// 往右邊移動3位
b = b + key;
char newb = (char) b;
sb.append(newb);
}
// System.out.println("密文==="+sb.toString());
return sb.toString();
}
1.2.4 頻度分析法破解愷撒加密
凱撒加密其實很容易從密文中猜測出明文所使用的秘鑰,當然只適用于英文文本的情況,畢竟咱們漢字做不到位移操作,最多拆字和組合,
如果是英文這難不倒解密者,以英文字母為例,為了確定每個英文字母的出現頻率,分析一篇或者數篇普通的英文文章,英文字母出現頻率最高的是e,接下來是t,然后是a……,然后檢查要破解的密文,也將每個字母出現的頻率整理出來,假設密文中出現頻率最高的字母是j,那么就可能是e的替身,如果密碼文中出現頻率次高的但是P,那么可能是t的替身,以此類推便就能解開加密資訊的內容,這就是頻率分析法,
1.將明文字母的出現頻率與密文字母的頻率相比較的程序
2.通過分析每個符號出現的頻率而輕易地破譯代換式密碼
3.在每種語言中,冗長的文章中的字母表現出一種可對之進行分辨的頻率,
4.e是英語中最常用的字母,其出現頻率為八分之一
有了這份頻率分析那么我們就可以對密文所對應的KEY進行一個猜測,
我們這里寫了一個頻率分析類FrequencyAnalysis,具體如下:
/**
* 頻率分析法破解凱撒密碼
*/
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_en.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 = https://www.cnblogs.com/xhj928675426/p/Util.file2String(path);
List> mapList = getMaxCountChar(data);
for (Entry 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 = https://www.cnblogs.com/xhj928675426/p/KaiserDemo.encrypt(artile, key);
//保存加密后的檔案
Util.string2File(encryptData, destFile);
}
/**
* 破解凱撒密碼
* @param input 資料源
* @return 回傳解密后的資料
*/
public static void decryptCaesarCode(String input, String destPath) {
int deCount = 0;//當前解密生成的備選檔案數
//獲取出現頻率最高的字符資訊(出現次數越多越靠前)
List> mapList = getMaxCountChar(input);
for (Entry 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>>(){
public int compare(Entry<Character, Integer> o1,
Entry<Character, Integer> o2) {
return o2.getValue().compareTo(o1.getValue());
}
});
return mapList;
}
}
這里用到的原文如下:
My father was a self-taught mandolin player. He was one of the best string instrument players in our town. He could not read music, but if he heard a tune a few times, he could play it. When he was younger, he was a member of a small country music band. They would play at local dances and on a few occasions would play for the local radio station. He often told us how he had auditioned and earned a position in a band that featured Patsy Cline as their lead singer. He told the family that after he was hired he never went back. Dad was a very religious man. He stated that there was a lot of drinking and cursing the day of his audition and he did not want to be around that type of environment.
運行 FrequencyAnalysis.java 里面 main 函式里面的 encryptFile 方法 對程式進行加密,在根目錄會生成一個 article_en.txt 檔案,然后我們統計這個檔案當中每個字符出現的次數,
這就是密文中的頻率分析情況,進行猜測的原則是從高到低,先從“#”開始,假設它對應的字母就是’e',那么密文所對應的秘鑰就是-66(參照ASCII表)
我們就可以以key=-66對密文進行一個解密,重復此步驟,直到得到正確的原文,
運行程式:
顯然第二個猜測檔案就已經正確了,
1.3現代常用的加密方式
1.3.1 對稱加密
采用單鑰密碼系統的加密方法,同一個密鑰可以同時用作資訊的加密和解密,這種加密方法稱為對稱加密,也稱為單密鑰加密,
- 示例:
- 我們現在有一個原文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使用
1.3.2 DES加密
這里的JAVA代碼展示用到了一個類Cipher.
Cipher :檔案 https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html#getInstance-java.lang.String-
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:密碼,獲取加密物件
// transformation:引數表示使用什么型別加密
Cipher cipher = Cipher.getInstance(transformation);
// 指定秘鑰規則
// 第一個引數表示:密鑰,key的位元組陣列
// 第二個引數表示:演算法
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// 對加密進行初始化
// 第一個引數:表示模式,有加密模式和解密模式
// 第二個引數:表示秘鑰規則
cipher.init(Cipher.ENCRYPT_MODE,sks);
// 進行加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 列印位元組,因為ascii碼有負數,決議不出來,所以亂碼
// for (byte b : bytes) {
// System.out.println(b);
// }
// 列印密文
System.out.println(new String(bytes));
}
}
首先無論是加密還是解密程序,期間用到的物件都是這個Cipher,
代碼涉及到的主要東西有:1.原文2.秘鑰key3.加密模式和加密演算法(我感覺這兩一樣的,,,但官方檔案說不一樣,可能是我太菜了)
這里需要注意的是如果使用des進行加密,那么密鑰必須是8個位元組 如果使用的是AES加密,那么密鑰必須是16個位元組
運行:
可以看到這里的密文是亂碼,出現亂碼是因為對應的位元組出現負數,但負數,沒有出現在 ascii 碼表里面,所以出現亂碼,需要配合base64進行轉碼,
使用 base64 進行編碼
base64 導包的時候,需要注意 ,別導錯了,需要匯入 apache 包:
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {
// 獲取加密物件
Cipher cipher = Cipher.getInstance(transformation);
// 創建加密規則
// 第一個引數key的位元組
// 第二個引數表示加密演算法
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// ENCRYPT_MODE:加密模式
// DECRYPT_MODE: 解密模式
// 初始化加密模式和演算法
cipher.init(Cipher.ENCRYPT_MODE,sks);
// 加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 輸出加密后的資料
String encode = Base64.encode(bytes);
return encode;
}
這樣一來密文也可以顯示出正常符號了,不過好像沒什么卵用,既然是密文看得懂與看不懂有什么區別?
運行程式:
1.3.3 DES解密
/**
* 解密
* @param encryptDES 密文
* @param key 密鑰
* @param transformation 加密演算法
* @param algorithm 加密型別
* @return
*/
private static String decryptDES(String encryptDES, String key, String transformation, String algorithm) throws Exception{
Cipher cipher = Cipher.getInstance(transformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(),algorithm);
//Cipher.DECRYPT_MODE:表示解密
// 解密規則
cipher.init(Cipher.DECRYPT_MODE,secretKeySpec);
// 解密,傳入密文
byte[] bytes = cipher.doFinal(Base64.decode(encryptDES));
return new String(bytes);
}
解密程序和加密程序是類似的,只是在選擇Cipher模式的時候選擇解密模式,Cilpher還有其他幾種模式,可以去官方檔案看看~~最后傳入密文的時候,如果之前用Base64轉碼一次了,這里需要再轉碼一次,
運行程式:
Base64 演算法簡介
Base64是網路上最常見的用于傳輸8Bit位元組碼的可讀性編碼演算法之一
可讀性編碼演算法不是為了保護資料的安全性,而是為了可讀性
可讀性編碼不改變資訊內容,只改變資訊內容的表現形式
所謂Base64,即是說在編碼程序中使用了64種字符:大寫A到Z、小寫a到z、數字0到9、“+”和“/”
Base58是Bitcoin(位元幣)中使用的一種編碼方式,主要用于產生Bitcoin的錢包地址
相比Base64,Base58不使用數字"0",字母大寫"O",字母大寫"I",和字母小寫"i",以及"+"和"/"符號
Base64 演算法原理
base64 是 3個位元組為一組,一個位元組 8位,一共 就是24位 ,然后,把3個位元組轉成4組,每組6位,
3 * 8 = 4 * 6 = 24 ,每組6位,缺少的2位,會在高位進行補0 ,這樣做的好處在于 ,base取的是后面6位,去掉高2位 ,那么base64的取值就可以控制在0-63位了,所以就叫base64,111 111 = 32 + 16 + 8 + 4 + 2 + 1 =
base64 構成原則
① 小寫 a - z = 26個字母
② 大寫 A - Z = 26個字母
③ 數字 0 - 9 = 10 個數字
④ + / = 2個符號
大家可能發現一個問題,咱們的base64有個 = 號,但是在映射表里面沒有發現 = 號 , 這個地方需要注意,等號非常特殊,因為base64是三個位元組一組 ,如果當我們的位數不夠的時候,會使用等號來補齊
1.3.4 AES加密解密
AES 加密解密和 DES 加密解密代碼一樣,只需要修改加密演算法就行,拷貝 ESC 代碼
public class AesDemo {
// DES加密演算法,key的大小必須是8個位元組
public static void main(String[] args) throws Exception {
String input ="硅谷";
// AES加密演算法,比較高級,所以key的大小必須是16個位元組
String key = "1234567812345678";
String transformation = "AES"; // 9PQXVUIhaaQ=
// 指定獲取密鑰的演算法
String algorithm = "AES";
// 先測驗加密,然后在測驗解密
String encryptDES = encryptDES(input, key, transformation, algorithm);
System.out.println("加密:" + encryptDES);
String s = dncryptDES(encryptDES, key, transformation, algorithm);
System.out.println("解密:" + s);
}
/**
* 使用DES加密資料
*
* @param input : 原文
* @param key : 密鑰(DES,密鑰的長度必須是8個位元組)
* @param transformation : 獲取Cipher物件的演算法
* @param algorithm : 獲取密鑰的演算法
* @return : 密文
* @throws Exception
*/
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {
// 獲取加密物件
Cipher cipher = Cipher.getInstance(transformation);
// 創建加密規則
// 第一個引數key的位元組
// 第二個引數表示加密演算法
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// ENCRYPT_MODE:加密模式
// DECRYPT_MODE: 解密模式
// 初始化加密模式和演算法
cipher.init(Cipher.ENCRYPT_MODE,sks);
// 加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 輸出加密后的資料
String encode = Base64.encode(bytes);
return encode;
}
/**
* 使用DES解密
*
* @param input : 密文
* @param key : 密鑰
* @param transformation : 獲取Cipher物件的演算法
* @param algorithm : 獲取密鑰的演算法
* @throws Exception
* @return: 原文
*/
private static String dncryptDES(String input, String key, String transformation, String algorithm) throws Exception {
// 1,獲取Cipher物件
Cipher cipher = Cipher.getInstance(transformation);
// 指定密鑰規則
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
cipher.init(Cipher.DECRYPT_MODE, sks);
// 3. 解密
byte[] bytes = cipher.doFinal(Base64.decode(input));
return new String(bytes);
}
}
運行程式:AES 加密的密鑰key , 需要傳入16個位元組
1.4 toString()與new String ()用法區別
public class TestBase64 {
public static void main(String[] args) {
String str="TU0jV0xBTiNVYys5bEdiUjZlNU45aHJ0bTdDQStBPT0jNjQ2NDY1Njk4IzM5OTkwMDAwMzAwMA==";
String rlt1=new String(Base64.decode(str));
String rlt2=Base64.decode(str).toString();
System.out.println(rlt1);
System.out.println(rlt2);
}
}
結果是:
MM#WLAN#Uc+9lGbR6e5N9hrtm7CA+A==#646465698#399900003000
[B@1540e19d
哪一個是正確的?為什么?
這里應該用new String()的方法,因為Base64加解密是一種轉換編碼格式的原理
toString()與new String ()用法區別
str.toString是呼叫了這個object物件的類的toString方法,一般是回傳這么一個String:[class name]@[hashCode]
new String(str)是根據parameter是一個位元組陣列,使用java虛擬機默認的編碼格式,將這個位元組陣列decode為對應的字符,若虛擬機默認的編碼格式是ISO-8859-1,按照ascii編碼表即可得到位元組對應的字符,
什么時候用什么方法呢?
new String()一般使用字符轉碼的時候,byte[]陣列的時候
toString()物件列印的時候使用
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/627.html
標籤:區塊鏈
上一篇:密碼學DAY1
下一篇:密碼學DAY2