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);
	}

}
相关文章
相关标签/搜索