Java 簡易實現一段字符串的 DES 加密和解密

有時候逛羣的時候,總有大神將某句話進行某種算法加密,在羣裏聊來聊去,我真的特別羨慕,只由於我看不懂>_<!因此我突發奇想,試着寫了一個加密解密的小程序,妄圖向大神看齊。加解密過程採用 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);
	}

}
相關文章
相關標籤/搜索