欢迎访问优讯网!
您当前的位置:首页 > 爱编程

Java 简易实现一段字符串的 DES 加密和解密

时间:2020-01-15 08:33:48  来源:优讯网  作者:小卡司  浏览次数:

有时候逛群的时候,总有大神将某句话进行某种算法加密,在群里聊来聊去,我真的特别羡慕,只因为我看不懂>_<!所以我突发奇想,试着写了一个加密解密的小程序,妄图向大神看齐。加解密过程采用 DES 算法 + Base64码转换 进行加密和解密。

一、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);
			//1、获取加密算法工具类对象
			Cipher cipher = Cipher.getInstance("DES");
			//2、初始化加密算法工具类对象
			//opmode为操作模式,加密/解密
			//key为将原始密匙进行转换后得到的加密密匙SecretKeySpec对象
			cipher.init(Cipher.ENCRYPT_MODE, key);
			//3、用加密算法工具类对象对明文进行加密
			byte[] doFinal = cipher.doFinal(plainText.getBytes());
			//4、通过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码表上找不到对应的字符(字节丢失),所以在解码时会产生乱码。

​​​​​​

解决思路:找一个码表,在编解码后,无论是怎样的二进制字节,都能找到对应的字符。

这样的码表就是 Base64 码表。

Base64码表编解码简易原理:

将会出现的三种情况(只有三种)

1、当字符串的字符数 % 3 = 0 时,依次取6位(2的6次方,刚好对应Base64码表上的64个字符),刚好拆分完。

如字符串 “ABC” 对应的二进制:

01000001  01000010  01000011

010000 | 01  0100 | 0010  01 | 000011

    Q      |       U      |        J       |      D

再将分割出来的二进制数转为十进制数,对照Base64码表可得出: “ABC” --> QUJD。

2、当字符串的字符数 % 3 = 1 时,依次取 6 位,将会剩下 4 位,缺少的 2 位用 0 补足。

如字符串 “ce” 对应的二进制:

01100011  01100101

011000 | 11  0110 | 0101(00)  ( )表示补足的位

     Y      |       2       |      U 

对照Base64码表: “ce” --> Y2U=(一个=表示补了00)

3、当字符串的字符数 % 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);
	}

}

 

来顶一下
返回首页
返回首页

原文链接:https://my.oschina.net/u/3637389/blog/3158402


推荐资讯
如何下载旧版centos iso镜像 如何下载迷你mini版的centos镜像
如何下载旧版centos i
计算机的正确使用姿势 电脑痴如何正确的使用电脑
计算机的正确使用姿势
好用的后台管理的前端框架模版H-ui H-ui框架模版分享
好用的后台管理的前端
微信电脑多开方法 无需辅助电脑版微信双开方法分享
微信电脑多开方法 无
相关文章
栏目更新
栏目热门