有時候逛羣的時候,總有大神將某句話進行某種算法加密,在羣裏聊來聊去,我真的特別羨慕,只由於我看不懂>_<!因此我突發奇想,試着寫了一個加密解密的小程序,妄圖向大神看齊。加解密過程採用 DES 算法 + Base64碼轉換 進行加密和解密。java
1、DES 加密算法簡要原理:算法
畫得很差,見諒哈,更底層的原理暫時不去深究,密碼學原本就是個大坑,越鑽越深,不是相關專業的不推薦深刻學習。小程序
廢話很少說,上代碼:數組
import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; import java.util.Base64.Decoder; import java.util.Base64.Encoder; import java.util.Random; public class TestDES { public static void main(String[] args) { String plainText = "你今天吃飯了嗎,若是吃了,很好,那證實了你很愛我!"; //明文 //值得注意的是,以當前getKey()這個方法來看 //原始密匙的長度越大,可能產生的不一樣密文會越多(length個密文,線性增加) //設密文數目爲y,原始密匙長度爲x,則 y = x //當rawKey只有一個佔一個字節的字符時,則只產生一個密文 //(經我屢次試驗"01"這個原始密匙有2個字節,但彷佛也只產生一個密文,緣由不明) //我寫的這個getKey()確實雞肋>_< String rawKey = "ldl123789654"; //原始密匙 //給定一個隨機正整數做爲加密密匙字節數組的起始填充位置 int randomIndex = new Random().nextInt(rawKey.getBytes().length); String cipherText = TestDES.desEncrypt(plainText, rawKey, randomIndex); String toPlainText = TestDES.desDecrypt(cipherText, rawKey, randomIndex); System.out.println("密文:" + cipherText); System.out.println("明文:" + toPlainText); } public static String desEncrypt(String plainText, String rawKey, int randomIndex) { String cipherText = null; try { //這裏採用SecretKeySpec key是想自定義密匙的生產規則 SecretKeySpec key = TestDES.getKey(rawKey, randomIndex); //一、獲取加密算法工具類對象 Cipher cipher = Cipher.getInstance("DES"); //二、初始化加密算法工具類對象 //opmode爲操做模式,加密/解密 //key爲將原始密匙進行轉換後獲得的加密密匙SecretKeySpec對象 cipher.init(Cipher.ENCRYPT_MODE, key); //三、用加密算法工具類對象對明文進行加密 byte[] doFinal = cipher.doFinal(plainText.getBytes()); //四、經過base64編解碼解決編解碼後的亂碼問題,同時起到再次加密的效果 Encoder enCoder = Base64.getEncoder(); cipherText = enCoder.encodeToString(doFinal); } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { e.printStackTrace(); } return cipherText; } public static String desDecrypt(String cipherText, String rawKey, int randomIndex) { String plainText = null; try { SecretKeySpec key = TestDES.getKey(rawKey, randomIndex); Cipher cipher = Cipher.getInstance("DES"); cipher.init(Cipher.DECRYPT_MODE, key); Decoder deCoder = Base64.getDecoder(); byte[] deCodeBytes = deCoder.decode(cipherText); byte[] doFinal = cipher.doFinal(deCodeBytes); plainText = new String(doFinal); } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { e.printStackTrace(); } return plainText; } private static SecretKeySpec getKey(String rawKey, int randomIndex) { //將用戶輸入任意長度的原始密匙轉換爲8個字節的原始密匙 byte[] buffer = new byte[8]; byte[] rawKeyBytes = rawKey.getBytes(); //進行遍歷,從一個隨機的位置開始循環取值填充到buffer數組裏 //若小於8則剩餘元素使用byte 0填充,若大於8只取前8個字節 //這只是一種簡單的處理方式,固然還有更復雜的處理方式 //for循環結束後可能產生的加密密匙數爲rawKeyBytes.length //便可能產生rawKeyBytes.length個密文(等於可能產生的加密密匙的數目) for (int i = 0; i < 8 && i < rawKeyBytes.length; i++) { if (randomIndex >= rawKeyBytes.length) { randomIndex = 0; } buffer[i] = rawKeyBytes[randomIndex++]; } //DES密匙只支持64位即8個字節的大小,多一個少一個都不行 return new SecretKeySpec(buffer, "DES"); } }
碰到的問題:在對 byte[] 數組操做(如調用字符串的 getBytes() 方法或者調用算法工具類對象的 doFinal(byte[] arr) 方法)的過程當中,若不指定編碼方式,則採用系統默認的編碼方式,個人是 win10 系統,默認採用 GBK 編解碼。因爲 DES 加密算法過於複雜致使最後加密轉換後的 byte[] 數組裏的字節,極可能在GBK碼錶上找不到對應的字符(字節丟失),因此在解碼時會產生亂碼。dom
解決思路:找一個碼錶,在編解碼後,不管是怎樣的二進制字節,都能找到對應的字符。工具
這樣的碼錶就是 Base64 碼錶。學習
Base64碼錶編解碼簡要原理:編碼
將會出現的三種狀況(只有三種)加密
一、當字符串所佔字節數 % 3 = 0 時,依次取6位(2的6次方,恰好對應Base64碼錶上的64個字符),恰好拆分完。code
如字符串 「ABC」 對應的二進制:
01000001 01000010 01000011
010000 | 01 0100 | 0010 01 | 000011
Q | U | J | D
再將分割出來的二進制數轉爲十進制數,對照Base64碼錶可得出: 「ABC」 --> QUJD。
二、當字符串所佔字節數 % 3 = 1 時,依次取 6 位,將會剩下 4 位,缺乏的 2 位用 0 補足。
如字符串 「ce」 對應的二進制:
01100011 01100101
011000 | 11 0110 | 0101(00) ( )表示補足的位
Y | 2 | U
對照Base64碼錶: 「ce」 --> Y2U=(一個=表示補了00)
三、當字符串所佔字節數 % 3 = 2 時,依次取 6 位,將會剩下 2 位,缺乏的 4 位用 0 補足。
如字符串「{」對應的二進制:
01111011
011110 | 11(0000) ( )表示補足的位
e | w
對照Base64碼錶: 「{」 --> ew==(==表示補了0000)。
好了,該介紹的我都差很少介紹完了,只爲了寫一個小小的DES加密解密程序,得掌握一些有趣可是略微「偏門」的知識點。天啊,才反應過來,我又鑽了一個晚上的牛角尖!
溜了溜了,最後再亂入一個凱撒加密解密的源碼:
public class TestCaesarCipher { public static void main(String[] args) { String plainText = "Alei&Ali1314@0101_*"; int key = 3; String cipherText = TestCaesarCipher.encrypt(plainText, key); System.out.println("密文:" + cipherText); String toPlainText = TestCaesarCipher.decrypt(cipherText, key); System.out.println("明文:" + toPlainText); } public static String encrypt(String plainText, int key) { char[] tempCharArr = plainText.toCharArray(); for (int i = 0; i < tempCharArr.length; i++) { tempCharArr[i] += key; } return new String(tempCharArr); } public static String decrypt(String cipherText, int key) { char[] tempCharArr = cipherText.toCharArray(); for (int i = 0; i < tempCharArr.length; i++) { tempCharArr[i] -= key; } return new String(tempCharArr); } }