密码学(二):Byte和常见加密方式
目录
频度分析法破解恺撒加密
Byte和bit
获取字符串byte
byte对应bit
中文对应的字节
英文对应的字节
常见加密方式
对称加密
DES加密
DES解密
base64补等号测试
AES加密解密
toString()与new String ()用法区别
频度分析法破解恺撒加密
密码棒
公元前5世纪的时候,斯巴达人利用一根木棒,缠绕上皮革或者羊皮纸,在上面横向写下信息,解下这条皮带。展开来看,这长串字母没有任何意义。
比如这样:
![]()

信差可以将这条皮带当成腰带,系在腰上。
比如这样:

然后收件人将这条皮带缠绕在相同的木棒上,就能恢复信息了。
前404年,一位遍体鳞伤的信差来到斯巴达将领利桑德面前,这趟波斯之旅只有他和四位同伴幸存,利桑德接下腰带,缠绕到他的密码棒上,得知波斯的发那巴祖斯准备侵袭他,多亏密码棒利桑德才能够预先防范,击退敌军。
频率分析解密法
密码棒是不是太简单了些?
加密者选择将组成信息的字母替代成别的字母,比如说将a写成1,这样就不能被解密者直接拿到信息了。
这难不倒解密者,以英文字母为例,为了确定每个英文字母的出现频率,分析一篇或者数篇普通的英文文章,英文字母出现频率最高的是e,接下来是t,然后是a……,然后检查要破解的密文,也将每个字母出现的频率整理出来,假设密文中出现频率最高的字母是j,那么就可能是e的替身,如果密码文中出现频率次高的但是P,那么可能是t的替身,以此类推便就能解开加密信息的内容。这就是频率分析法。
- 将明文字母的出现频率与密文字母的频率相比较的过程
- 通过分析每个符号出现的频率而轻易地破译代换式密码
- 在每种语言中,冗长的文章中的字母表现出一种可对之进行分辨的频率。
- e是英语中最常用的字母,其出现频率为八分之一
拷贝资料里面的 Util.java 和 FrequencyAnalysis.java 到项目的 com.atguigu.kaiser包下面 , article.txt 拷贝到项目文件夹的根目录
运行 FrequencyAnalysis.java 用来统计每个字符出现的次数
![]()

运行 FrequencyAnalysis.java 里面 main 函数里面的 encryptFile 方法 对程序进行加密
public static void main(String[] args) throws Exception {//测试1,统计字符个数//printCharCount("article.txt");//加密文件int key = 3;encryptFile("article.txt", "article_en.txt", key);//读取加密后的文件// String artile = Util.file2String("article_en.txt");//解密(会生成多个备选文件)// decryptCaesarCode(artile, "article_de.txt");}
在根目录会生成一个 article_en.txt 文件,然后我们统计这个文件当中每个字符出现的次数
public static void main(String[] args) throws Exception {//测试1,统计字符个数printCharCount("article_en.txt");//加密文件int key = 3;//encryptFile("article.txt", "article_en.txt", key);//读取加密后的文件// String artile = Util.file2String("article_en.txt");//解密(会生成多个备选文件)// decryptCaesarCode(artile, "article_de.txt");}
运行程序
![]()

我们来看看 频度分析法如何工作的
public static void main(String[] args) throws Exception {//测试1,统计字符个数//printCharCount("article_en.txt");//加密文件int key = 3;//encryptFile("article.txt", "article_en.txt", key);//读取加密后的文件String artile = Util.file2String("article_en.txt");//解密(会生成多个备选文件)decryptCaesarCode(artile, "article_de.txt");}
运行程序
![]()

运行结果 # 出现次数最多, 我们知道在英文当中 e 出现的频率是最高的,我们假设现在 # 号,就是 e ,变形而来的 ,我们可以对照 ascii 编码表 ,我们的凯撒加密当中位移是加了一个 key ,所以我们 猜测 两个值直接相差 -66 ,我们现在就以 -66 进行解密 生成一个文件,我们查看第一个文件发现,根本读不懂,所以解密失败,我们在猜测 h 是 e ,h 和 e 之间相差3 ,所以我们在去看第二个解密文件,发现我们可以读懂,解密成功

Byte和bit
Byte : 字节. 数据存储的基本单位,比如移动硬盘1T , 单位是byte
bit : 比特, 又叫位. 一个位要么是0要么是1. 数据传输的单位 , 比如家里的宽带100MB,下载速度并没有达到100MB,一般都是12-13MB,那么是因为需要使用 100 / 8
关系: 1Byte = 8bit
获取字符串byte
public class ByteBit {public static void main(String[] args) {String a = "a";byte[] bytes = a.getBytes();for (byte b : bytes) {int c=b;// 打印发现byte实际上就是ascii码System.out.println(c);}}
}
运行程序
![]()

byte对应bit
public class ByteBit {public static void main(String[] args) {String a = "a";byte[] bytes = a.getBytes();for (byte b : bytes) {int c=b;// 打印发现byte实际上就是ascii码System.out.println(c);// 我们在来看看每个byte对应的bit,byte获取对应的bitString s = Integer.toBinaryString(c);System.out.println(s);}}
}
运行程序
打印出来应该是8个bit,但前面是0,没有打印 ,从打印结果可以看出来,一个英文字符 ,占一个字节
![]()

中文对应的字节
public class ByteBitDemo {public static void main(String[] args) throws Exception{String a = "尚";byte[] bytes = a.getBytes();for (byte b : bytes) {System.out.print(b + " ");String s = Integer.toBinaryString(b);System.out.println(s);}}}
运行程序:我们发现一个中文是有 3 个字节组成
![]()

我们修改 编码格式 , 编码格式改成 GBK ,我们在运行发现变成了 2 个字节
public static void main(String[] args) throws Exception{String a = "尚";// 在中文情况下,不同的编码格式,对应不同的字节//GBK :编码格式占2个字节// UTF-8:编码格式占3个字节byte[] bytes = a.getBytes("GBK");// byte[] bytes = a.getBytes("UTF-8");for (byte b : bytes) {System.out.print(b + " ");String s = Integer.toBinaryString(b);System.out.println(s);}}
运行程序
![]()

英文对应的字节
我们在看看英文,在不同的编码格式占用多少字节
public class ByteBit {public static void main(String[] args) throws Exception{String a = "A";byte[] bytes = a.getBytes();// 在中文情况下,不同的编码格式,对应不同的字节
// byte[] bytes = a.getBytes("GBK");for (byte b : bytes) {System.out.print(b + " ");String s = Integer.toBinaryString(b);System.out.println(s);}}
}
运行程序
![]()

常见加密方式

对称加密
- 采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
- 示例
- 我们现在有一个原文3要发送给B
- 设置密钥为108, 3 * 108 = 324, 将324作为密文发送给B
- B拿到密文324后, 使用324/108 = 3 得到原文
- 常见加密算法
- DES : Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
- AES : Advanced Encryption Standard, 高级加密标准 .在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
- 特点
- 加密速度快, 可以加密大文件
- 密文可逆, 一旦密钥文件泄漏, 就会导致数据暴露
- 加密后编码表找不到对应字符, 出现乱码
- 一般结合Base64使用
DES加密
示例代码 des加密算法
Cipher :文档 https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html#getInstance-java.lang.String-
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;public class DesAesDemo {public static void main(String[] args) throws Exception{// 原文String input = "硅谷";// des加密必须是8位String key = "123456";// 算法String algorithm = "DES";String transformation = "DES";// Cipher:密码,获取加密对象// transformation:参数表示使用什么类型加密Cipher cipher = Cipher.getInstance(transformation);// 指定秘钥规则// 第一个参数表示:密钥,key的字节数组// 第二个参数表示:算法SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);// 对加密进行初始化// 第一个参数:表示模式,有加密模式和解密模式// 第二个参数:表示秘钥规则cipher.init(Cipher.ENCRYPT_MODE,sks);// 进行加密byte[] bytes = cipher.doFinal(input.getBytes());// 打印字节,因为ascii码有负数,解析不出来,所以乱码
// for (byte b : bytes) {
// System.out.println(b);
// }// 打印密文System.out.println(new String(bytes));}
}
运行:
![]()

修改 密钥 key = “12345678” ,再次运行 ,出现乱码是因为对应的字节出现负数,但负数,没有出现在 ascii 码表里面,所以出现乱码,需要配合base64进行转码

使用 base64 进行编码
base64 导包的时候,需要注意 ,别导错了,需要导入 apache 包
![]()

运行程序
![]()

DES解密
使用 ctrl + alt + m 快捷键抽取代码
import com.sun.org.apache.xml.internal.security.utils.Base64;import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;public class DesDemo {// DES加密算法,key的大小必须是8个字节public static void main(String[] args) throws Exception {String input ="硅谷";// DES加密算法,key的大小必须是8个字节String key = "12345678";String transformation = "DES"; // 9PQXVUIhaaQ=// 指定获取密钥的算法String algorithm = "DES";String encryptDES = encryptDES(input, key, transformation, algorithm);System.out.println("加密:" + encryptDES);String s = decryptDES(encryptDES, key, transformation, algorithm);System.out.println("解密:" + s);}/*** 使用DES加密数据** @param input : 原文* @param key : 密钥(DES,密钥的长度必须是8个字节)* @param transformation : 获取Cipher对象的算法* @param algorithm : 获取密钥的算法* @return : 密文* @throws Exception*/private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {// 获取加密对象Cipher cipher = Cipher.getInstance(transformation);// 创建加密规则// 第一个参数key的字节// 第二个参数表示加密算法SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);// ENCRYPT_MODE:加密模式// DECRYPT_MODE: 解密模式// 初始化加密模式和算法cipher.init(Cipher.ENCRYPT_MODE,sks);// 加密byte[] bytes = cipher.doFinal(input.getBytes());// 输出加密后的数据String encode = Base64.encode(bytes);return encode;}/*** 使用DES解密** @param input : 密文* @param key : 密钥* @param transformation : 获取Cipher对象的算法* @param algorithm : 获取密钥的算法* @throws Exception* @return: 原文*/private static String decryptDES(String input, String key, String transformation, String algorithm) throws Exception {// 1,获取Cipher对象Cipher cipher = Cipher.getInstance(transformation);// 指定密钥规则SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);cipher.init(Cipher.DECRYPT_MODE, sks);// 3. 解密,上面使用的base64编码,下面直接用密文byte[] bytes = cipher.doFinal(Base64.decode(input));// 因为是明文,所以直接返回return new String(bytes);}
}
运行程序:
![]()

Base64 算法简介
Base64是网络上最常见的用于传输8Bit字节码的可读性编码算法之一
可读性编码算法不是为了保护数据的安全性,而是为了可读性
可读性编码不改变信息内容,只改变信息内容的表现形式
所谓Base64,即是说在编码过程中使用了64种字符:大写A到Z、小写a到z、数字0到9、“+”和“/”
Base58是Bitcoin(比特币)中使用的一种编码方式,主要用于产生Bitcoin的钱包地址
相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"i",以及"+"和"/"符号
Base64 算法原理
base64 是 3个字节为一组,一个字节 8位,一共 就是24位 ,然后,把3个字节转成4组,每组6位,
3 * 8 = 4 * 6 = 24 ,每组6位,缺少的2位,会在高位进行补0 ,这样做的好处在于 ,base取的是后面6位,去掉高2位 ,那么base64的取值就可以控制在0-63位了,所以就叫base64,111 111 = 32 + 16 + 8 + 4 + 2 + 1 =
base64 构成原则
① 小写 a - z = 26个字母
② 大写 A - Z = 26个字母
③ 数字 0 - 9 = 10 个数字
④ + / = 2个符号
大家可能发现一个问题,咱们的base64有个 = 号,但是在映射表里面没有发现 = 号 , 这个地方需要注意,等号非常特殊,因为base64是三个字节一组 ,如果当我们的位数不够的时候,会使用等号来补齐
![]()

base64补等号测试
import com.sun.org.apache.xml.internal.security.utils.Base64;public class TestBase64 {public static void main(String[] args) {// 1:MQ== 表示一个字节,不够三个字节,所以需要后面通过 == 号补齐System.out.println(Base64.encode("1".getBytes()));
// System.out.println(Base64.encode("12".getBytes()));
// System.out.println(Base64.encode("123".getBytes()));
// // 硅谷:中文占6个字节,6 * 8 = 48 ,刚刚好被整除,所以没有等号
// System.out.println(Base64.encode("硅谷".getBytes()));}
}
运行:
![]()

AES加密解密
AES 加密解密和 DES 加密解密代码一样,只需要修改加密算法就行,拷贝 ESC 代码
public class AesDemo {// DES加密算法,key的大小必须是8个字节public static void main(String[] args) throws Exception {String input ="硅谷";// AES加密算法,比较高级,所以key的大小必须是16个字节String key = "1234567812345678";String transformation = "AES"; // 9PQXVUIhaaQ=// 指定获取密钥的算法String algorithm = "AES";// 先测试加密,然后在测试解密String encryptDES = encryptDES(input, key, transformation, algorithm);System.out.println("加密:" + encryptDES);String s = dncryptDES(encryptDES, key, transformation, algorithm);System.out.println("解密:" + s);}/*** 使用DES加密数据** @param input : 原文* @param key : 密钥(DES,密钥的长度必须是8个字节)* @param transformation : 获取Cipher对象的算法* @param algorithm : 获取密钥的算法* @return : 密文* @throws Exception*/private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {// 获取加密对象Cipher cipher = Cipher.getInstance(transformation);// 创建加密规则// 第一个参数key的字节// 第二个参数表示加密算法SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);// ENCRYPT_MODE:加密模式// DECRYPT_MODE: 解密模式// 初始化加密模式和算法cipher.init(Cipher.ENCRYPT_MODE,sks);// 加密byte[] bytes = cipher.doFinal(input.getBytes());// 输出加密后的数据String encode = Base64.encode(bytes);return encode;}/*** 使用DES解密** @param input : 密文* @param key : 密钥* @param transformation : 获取Cipher对象的算法* @param algorithm : 获取密钥的算法* @throws Exception* @return: 原文*/private static String dncryptDES(String input, String key, String transformation, String algorithm) throws Exception {// 1,获取Cipher对象Cipher cipher = Cipher.getInstance(transformation);// 指定密钥规则SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);cipher.init(Cipher.DECRYPT_MODE, sks);// 3. 解密byte[] bytes = cipher.doFinal(Base64.decode(input));return new String(bytes);}
}
运行程序:AES 加密的密钥key , 需要传入16个字节
![]()

在运行程序
![]()

toString()与new String ()用法区别
举例子
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;public class TestBase64 {public static void main(String[] args) {String str="TU0jV0xBTiNVYys5bEdiUjZlNU45aHJ0bTdDQStBPT0jNjQ2NDY1Njk4IzM5OTkwMDAwMzAwMA==";String rlt1=new String(Base64.decode(str));String rlt2=Base64.decode(str).toString();System.out.println(rlt1);System.out.println(rlt2);}
}
结果是:
MM#WLAN#Uc+9lGbR6e5N9hrtm7CA+A==#646465698#399900003000[B@1540e19d
哪一个是正确的?为什么?
这里应该用new String()的方法,因为Base64加解密是一种转换编码格式的原理
toString()与new String ()用法区别
str.toString是调用了这个object对象的类的toString方法。一般是返回这么一个String:[class name]@[hashCode]
new String(str)是根据parameter是一个字节数组,使用java虚拟机默认的编码格式,将这个字节数组decode为对应的字符。若虚拟机默认的编码格式是ISO-8859-1,按照ascii编码表即可得到字节对应的字符。
什么时候用什么方法呢?
new String()一般使用字符转码的时候,byte[]数组的时候
toString()对象打印的时候使用
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
