有时候逛群的时候,总有大神将某句话进行某种算法加密,在群里聊来聊去,我真的特别羡慕,只由于我看不懂>_<!因此我突发奇想,试着写了一个加密解密的小程序,妄图向大神看齐。加解密过程采用 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); } }