个人博客欢迎访问
总结不易,如果对你有帮助,请点赞关注支持一下
微信搜索程序dunk,关注公众号,获取博客源码、数据结构与算法笔记、面试笔试题
本文加密算法源码
文章目录
- 密码学浅尝辄止
- 密码学的基本概念
- 古典密码学
-
- 替换法
- 移位法
- 恺撒加密
-
- 中国古代加密
- 外国加密
- 频度分析法破解恺撒加密
-
- 密码棒
- 频率分析解密法
-
- 创建工具类负责文件的写入和读取
- 破解密码
- 古典密码破解方式
-
- 频率分析
- 近代密码学
-
- 恩尼格玛机
- 现代密码学
-
- HTTPS的通信过程
- 常见的加密方式
-
- 对称加密
-
- DES加密解密
- Base64
-
- base64 构成原则
- 原理
- AES加密解密
- 加密模式
-
- ECB
- CBC
- 填充模式
-
- NoPadding
- PKCS5Padding
- Tips
- 非对称密码
-
- 常见算法
-
- RSA
- ECC
- 散列函数
-
- 信息摘要
-
- 特点
- 常见算法 :
- 数字签名
-
- 基本原理
- 数字证书
- 网页加密
- 如何设置密码才安全
密码学浅尝辄止
密码学主要研究编制密码 和 破译密码的学科
密码学的主要目的:研究如何隐藏信息并且把信息传递出去的一个学科
密码学的基本概念
密码在我们的生活中有着重要的作用,那么密码究竟来自何方,为何会产生呢?
密码学是网络安全、信息安全、区块链等产品的基础,常见的非对称加密、对称加密、散列函数等,都属于密码学范畴。
密码学有数千年的历史,从最开始的替换法到如今的非对称加密算法,经历了古典密码学,近代密码学和现代密码学三个阶段。密码学不仅仅是数学家们的智慧,更是如今网络空间安全的重要基础。
古典密码学
在古代的战争中,多见使用隐藏信息的方式保护重要的通信资料。比如先把需要保护的信息用化学药水写到纸上,药水干后,纸上看不出任何的信息,需要使用另外的化学药水涂抹后才可以阅读纸上的信息。
这些方法都是在保护重要的信息不被他人获取,但藏信息的方式比较容易被他人识破,例如增加哨兵的排查力度,就会发现其中的猫腻,因而随后发展出了较难破解的古典密码学。
替换法
替换法很好理解,就是用固定的信息将原文替换成无法直接阅读的密文信息。例如将 b 替换成 w ,e 替换成p ,这样bee 单词就变换成了wpp,不知道替换规则的人就无法阅读出原文的含义。
替换法有单表替换和多表替换两种形式。单表替换即只有一张原文密文对照表单,发送者和接收者用这张表单来加密解密。在上述例子中,表单即为:a b c d e - s w t r p 。
多表替换即有多张原文密文对照表单,不同字母可以用不同表单的内容替换。
例如约定好表单为:表单 1:abcde-swtrp 、表单2:abcde-chfhk 、表单 3:abcde-jftou。
规定第一个字母用第三张表单,第二个字母用第一张表单,第三个字母用第二张表单,这时 bee单词就变成了
(312)fpk ,破解难度更高,其中 312 又叫做密钥,密钥可以事先约定好,也可以在传输过程中标记出来。
移位法
移位法就是将原文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后得出密文,典型的移位法应用有 “ 恺撒密码 ”。
例如约定好向后移动2位(abcde - cdefg),这样 bee 单词就变换成了dgg 。
同理替换法,移位法也可以采用多表移位的方式,典型的多表案例是“维尼吉亚密码”(又译维热纳尔密码),属于多表密码的一种形式。
恺撒加密
中国古代加密
公元683年,唐中宗即位。随后,武则天废唐中宗,立第四子李旦为皇帝,但朝政大事均由她自己专断。裴炎、徐敬业和骆宾王等人对此非常不满。徐敬业聚兵十万,在江苏扬州起兵。裴炎做内应,欲以拆字手段为其传递秘密信息。后因有人告密,裴炎被捕,未发出的密信落到武则天手中。这封密信上只有“青鹅”二字,群臣对此大惑不解。武则天破解了“青鹅”的秘密:“青”字拆开来就是“十二月”,而“鹅”字拆开来就是“我自与”。密信的意思是让徐敬业、骆宾王等率兵于十二月进发,裴炎在内部接应。“青鹅”破译后,裴炎被杀。接着,武则天派兵击败了徐敬业和骆宾王。
外国加密
在密码学中,恺撒密码是一种最简单且最广为人知的加密技术。
凯撒密码最早由古罗马军事统帅盖乌斯·尤利乌斯·凯撒在军队中用来传递加密信息,故称凯撒密码。这是一种位移加密方式,只对26个字母进行位移替换加密,规则简单,容易破解。下面是位移1次的对比:
将明文字母表向后移动1位,A变成了B,B变成了C……,Z变成了A。
同理,若将明文字母表向后移动3位:则A变成了D,B变成了E……,Z变成了C。
字母表最多可以移动25位。凯撒密码的明文字母表向后或向前移动都是可以的,通常表述为向后移动,如果要向前移动1位,则等同于向后移动25位,位移选择为25即可。
它是一种替换加密的技术,明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。
例如,当偏移量是3的时候,所有的字母A将被替换成D,B变成E,以此类推。
这个加密方法是以恺撒的名字命名的,当年恺撒曾用此方法与其将军们进行联系。恺撒密码通常被作为其他更复杂的加密方法中的一个步骤。简单来说就是当秘钥为n,其中一个待加密字符ch,加密之后的字符为ch+n,当ch+n超过’z’时,回到’a’计数。
/**
* @author :zsy
* @date :Created 2021/6/16 23:36
* @description:凯撒加密
*/
public class KaiSerDemo {
public static void main(String[] args) {
String encryptData = encryptKaiser("Hello World", 3);
System.out.println("加密后:" + encryptData);
String decryptDate = decryptKaiSer(encryptData, 3);
System.out.println("解密后:" + decryptDate);
}
public static String encryptKaiser(String orignal, int key) {
StringBuilder builder = new StringBuilder();
char[] chars = orignal.toCharArray();
for (char c : chars) {
//转换为ascii码值
int asciiCode = c;
//偏移数据
asciiCode += key;
//转换回编码
char newChar = (char)asciiCode;
builder.append(newChar);
}
return builder.toString();
}
public static String decryptKaiSer(String encryptData, int key) {
StringBuilder builder = new StringBuilder();
char[] chars = encryptData.toCharArray();
for (char c : chars) {
//转换为ascii码值
int asciiCode = c;
//偏移数据
asciiCode -= key;
//转换回编码
char newChar = (char)asciiCode;
builder.append(newChar);
}
return builder.toString();
}
}
频度分析法破解恺撒加密
密码棒
公元前5世纪的时候,斯巴达人利用一根木棒,缠绕上皮革或者羊皮纸,在上面横向写下信息,解下这条皮带。展开来看,这长串字母没有任何意义。信差可以将这条皮带当成腰带,系在腰上。
然后收件人将这条皮带缠绕在相同的木棒上,就能恢复信息了。
前404年,一位遍体鳞伤的信差来到斯巴达将领利桑德面前,这趟波斯之旅只有他和四位同伴幸存,利桑德接下腰带,缠绕到他的密码棒上,得知波斯的发那巴祖斯准备侵袭他,多亏密码棒利桑德才能够预先防范,击退敌军。
频率分析解密法
加密者选择将组成信息的字母替代成别的字母,比如说将a写成1,这样就不能被解密者直接拿到信息了。
这难不倒解密者,以英文字母为例,为了确定每个英文字母的出现频率,分析一篇或者数篇普通的英文文章,英文字母出现频率最高的是e,接下来是t,然后是a……,然后检查要破解的密文,也将每个字母出现的频率整理出来,假设密文中出现频率最高的字母是j,那么就可能是e的替身,如果密码文中出现频率次高的但是P,那么可能是t的替身,以此类推便就能解开加密信息的内容。这就是频率分析法。
- 将明文字母的出现频率与密文字母的频率相比较的过程
- 通过分析每个符号出现的频率而轻易地破译代换式密码
- 在每种语言中,冗长的文章中的字母表现出一种可对之进行分辨的频率。
- e是英语中最常用的字母,其出现频率为八分之一
创建工具类负责文件的写入和读取
public class Util {
public static void print(byte[] bytes) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
sb.append(bytes[i]).append(" ");
}
System.out.println(sb);
}
public static String file2String(String path) throws IOException {
FileReader reader = new FileReader(new File(path));
char[] buffer = new char[1024];
int len = -1;
StringBuffer sb = new StringBuffer();
while ((len = reader.read(buffer)) != -1) {
sb.append(buffer, 0, len);
}
return sb.toString();
}
public static void string2File(String data, String path){
FileWriter writer = null;
try {
writer = new FileWriter(new File(path));
writer.write(data);
} catch (Exception e) {
e.printStackTrace();
}finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static String inputStream2String(InputStream in) throws IOException {
int len = -1;
byte[] buffer = new byte[1024];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while((len = in.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
baos.close();
return baos.toString("UTF-8");
}
}
破解密码
/**
* 频率分析法破解凯撒密码
*/
public class FrequencyAnalysis {
//英文里出现次数最多的字符
private static final char MAGIC_CHAR = 'e';
//破解生成的最大文件数
private static final int DE_MAX_FILE = 4;
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");
}
public static void printCharCount(String path) throws IOException{
String data = Util.file2String(path);
List<Entry<Character, Integer>> mapList = getMaxCountChar(data);
for (Entry<Character, Integer> entry : mapList) {
//输出前几位的统计信息
System.out.println("字符'" + entry.getKey() + "'出现" + entry.getValue() + "次");
}
}
public static void encryptFile(String srcFile, String destFile, int key) throws IOException {
String artile = Util.file2String(srcFile);
//加密文件
String encryptData = KaiserDemo.encrypt(artile, key);
//保存加密后的文件
Util.string2File(encryptData, destFile);
}
/**
* 破解凯撒密码
* @param input 数据源
* @return 返回解密后的数据
*/
public static void decryptCaesarCode(String input, String destPath) {
int deCount = 0;//当前解密生成的备选文件数
//获取出现频率最高的字符信息(出现次数越多越靠前)
List<Entry<Character, Integer>> mapList = getMaxCountChar(input);
for (Entry<Character, Integer> entry : mapList) {
//限制解密文件备选数
if (deCount >= DE_MAX_FILE) {
break;
}
//输出前几位的统计信息
System.out.println("字符'" + entry.getKey() + "'出现" + entry.getValue() + "次");
++deCount;
//出现次数最高的字符跟MAGIC_CHAR的偏移量即为秘钥
int key = entry.getKey() - MAGIC_CHAR;
System.out.println("猜测key = " + key + ", 解密生成第" + deCount + "个备选文件" + "\n");
String decrypt = KaiserDemo.decrypt(input, key);
String fileName = "de_" + deCount + destPath;
Util.string2File(decrypt, fileName);
}
}
//统计String里出现最多的字符
public static List<Entry<Character, Integer>> getMaxCountChar(String data) {
Map<Character, Integer> map = new HashMap<Character, Integer>();
char[] array = data.toCharArray();
for (char c : array) {
if(!map.containsKey(c)) {
map.put(c, 1);
}else{
Integer count = map.get(c);
map.put(c, count + 1);
}
}
//输出统计信息
/*for (Entry<Character, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + "出现" + entry.getValue() + "次");
}*/
//获取获取最大值
int maxCount = 0;
for (Entry<Character, Integer> entry : map.entrySet()) {
//不统计空格
if (/*entry.getKey() != ' ' && */entry.getValue() > maxCount) {
maxCount = entry.getValue();
}
}
//map转换成list便于排序
List<Entry<Character, Integer>> mapList = new ArrayList<Entry<Character,Integer>>(map.entrySet());
//根据字符出现次数排序
Collections.sort(mapList, new Comparator<Entry<Character, Integer>>(){
@Override
public int compare(Entry<Character, Integer> o1,
Entry<Character, Integer> o2) {
return o2.getValue().compareTo(o1.getValue());
}
});
return mapList;
}
}
字符'#'出现989次
猜测key = -66, 解密生成第1个备选文件
字符'h'出现478次
猜测key = 3, 解密生成第2个备选文件
字符'd'出现344次
猜测key = -1, 解密生成第3个备选文件
字符'w'出现327次
猜测key = 18, 解密生成第4个备选文件
古典密码破解方式
古典密码虽然很简单,但是在密码史上是使用的最久的加密方式,直到“概率论”的数学方法被发现,古典密码就被破解了。
多表的替换法或移位法虽然难度高一些,但如果数据量足够大的话,也是可以破解的。以维尼吉亚密码算法为例,破解方法就是先找出密文中完全相同的字母串,猜测密钥长度,得到密钥长度后再把同组的密文放在一起,使用频率分析法破解。
频率分析
频率分析在数学、物理学和信号处理中是一种分解函数、波形、或者信号的频率组成,以获取频谱的方法。在密码学中,频率分析是指研究字母或者字母组合在文本中出现的频率。应用频率分析可以破解古典密码。
英文单词中字母出现的频率是不同的,e以12.702%的百分比占比最高,z 只占到0.074%,感兴趣的可以去百科查字母频率详细统计数据。如果密文数量足够大,仅仅采用频度分析法就可以破解单表的替换法或移位法。
近代密码学
古典密码的安全性受到了威胁,外加使用便利性较低,到了工业化时代,近现代密码被广泛应用。
恩尼格玛机
恩尼格玛密码机,在密码学史中是一种用于加密与解密文件的密码机。确切地说,恩尼格玛是一系列相似的转子机械的统称,它包括了一系列不同的型号。恩尼格玛在1920年代早期开始被用于商业,也被一些国家的军队与政府采用过,在这些国家中,最著名的是第二次世界大战时的纳粹德国。恩尼格玛密码机的大部分设置都会在一段时间(一般为一天)以后被更换。
恩尼格玛机是二战时期纳粹德国使用的加密机器,后被英国破译,参与破译的人员有被称为计算机科学之父、人工智能之父的图灵。恩尼格玛机使用的加密方式本质上还是移位和替代,只不过因为密码表种类极多,破解难度高,同时加密解密机器化,使用便捷,因而在二战时期得以使用。
现代密码学
HTTPS的通信过程
https通信是建立在SSL连接层之上的请求和响应,客户端将加密组件发送到服务器端,服务端进行匹配后将数字证书等信息发送到客户端,客户端进行证书验证,验证通过后使用非对称加密对数据的密钥进行协商,协商后得到对称的加密密钥,然后使用对称算法进行TCP链接,然后与客户端进行三次握手后,进行数据传输,传输完成后,四次挥手,断开链接,通信结束。
- 客户端和服务器端通过TCP建立来连接,发送https请求
- 服务器响应请求,并将数字证书发送给客户端,数字证书包括公共密钥、域名、申请证书的公司
- 客户端收到服务器端的数字证书之后,会验证数字证书的合法性。
- 如果公钥合格,那么客户端会生成client key,一个用于进行对称加密的密钥,并用服务器的公钥对客户端密钥进行非对称加密。
- 客户端会再次发起请求,将加密之后的客户端密钥发送给服务器
- 服务器接受加密文后,会用私钥对其进行非对称解密,得到客户端秘钥,并使用客户端密钥进行对称加密,生成密钥文并发送
- 客户端收到密文,并使用客户端密钥进行解密获得数据
常见的加密方式
对称加密
对称密码应用了相同的加密密钥和解密密钥。对称密码分为:序列密码(流密码),分组密码(块密码)两种。流密码是对信息流中的每一个元素(一个字母或一个比特)作为基本的处理单元进行加密,块密码是先对信息流分块,再对每一块分别加密。
例如原文为1234567890,流加密即先对1进行加密,再对2进行加密,再对3进行加密……最后拼接成密文;块加密先分成不同的块,如1234成块,5678成块,90XX(XX为补位数字)成块,再分别对不同块进行加密,最后拼接成密文。前文提到的古典密码学加密方法,都属于流加密。
- 采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
- 示例
- 我们现在有一个原文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加密解密
加密的秘钥必须为8个字节,原文是8字节的整数倍
/**
* @author :zsy
* @date :Created 2021/6/17 9:51
* @description:Des加密
*/
public class DesDemo {
public static void main(String[] args) throws Exception {
String input = "计算机网络";
//加密密钥
String key = "12345678";
//加密模式
String transformation = "DES";
//加密算法
String algorithm = "DES";
String encryptDES = encryptDES(input, key, transformation, algorithm);
System.out.println("加密后:" + encryptDES);
String decryptDES = decryptDES(encryptDES, key, transformation , algorithm);
System.out.println("解密后:" + decryptDES);
}
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {
//获取加密对象
Cipher cipher = Cipher.getInstance(transformation);
//加密规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
//初始化加密模式和算法
cipher.init(Cipher.ENCRYPT_MODE, sks);
//加密
byte[] bytes = cipher.doFinal(input.getBytes());
String encode = Base64.encode(bytes);
return encode;
}
private static String decryptDES(String encryptDES, String key, String transformation, String algorithm) throws Exception {
//获取加密对象
Cipher cipher = Cipher.getInstance(transformation);
//加密规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
//初始化加密模式和算法
cipher.init(Cipher.DECRYPT_MODE, sks);
//加密
byte[] bytes = cipher.doFinal(Base64.decode(encryptDES.getBytes()));
return new String(bytes);
}
}
加密后:gMC7DtqHnrg23svEN77jNQ==
解密后:计算机网络
Base64
Base64是网络上最常见的用于传输8Bit字节码的可读性编码算法之一,可读性编码算法不是为了保护数据的安全性,而是为了可读性,可读性编码不改变信息内容,只改变信息内容的表现形式,所谓Base64,即是说在编码过程中使用了64种字符:大写A到Z、小写a到z、数字0到9、“+”和“/”,Base58是Bitcoin(比特币)中使用的一种编码方式,主要用于产生Bitcoin的钱包地址,相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"i",以及"+“和”/"符号
base64 构成原则
- 小写 a - z = 26个字母
- 大写 A - Z = 26个字母
- 数字 0 - 9 = 10 个数字
- / + =2个符号
原理
三个字节为一组,一个字节8位,一共就是24位,base64将三个字节,转换成4组,每组6位,一个字节应该是8位,缺少两位,在高位补0,保证了base64的数据在0~63之间(000 000 ~ 111 111)
如果当我们的位数不够的时候,会使用等号来补齐
System.out.println(Base64.encode("1".getBytes()));//MQ==
System.out.println(Base64.encode("12".getBytes()));//MTI=
System.out.println(Base64.encode("123".getBytes()));//MTIz
//UTF-8编码,一个中文占3个字节,4*3*8 = 96 96 % 6 = 0
System.out.println(Base64.encode("网络安全".getBytes()));//572R57uc5a6J5YWo
AES加密解密
加密的秘钥必须为16个字节,原文是16字节的整数倍
/**
* @author :zsy
* @date :Created 2021/6/17 11:09
* @description:Aes加密
*/
public class AesDemo {
public static void main(String[] args) throws Exception {
String input = "计算机网络";
//加密密钥
String key = "1234567812345678";
//加密模式
String transformation = "AES";
//加密算法
String algorithm = "AES";
String encryptDES = encryptDES(input, key, transformation, algorithm);
System.out.println("加密后:" + encryptDES);
String decryptDES = decryptDES(encryptDES, key, transformation , algorithm);
System.out.println("解密后:" + decryptDES);
}
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {
//获取加密对象
Cipher cipher = Cipher.getInstance(transformation);
//加密规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
//初始化加密模式和算法
cipher.init(Cipher.ENCRYPT_MODE, sks);
//加密
byte[] bytes = cipher.doFinal(input.getBytes());
String encode = Base64.encode(bytes);
return encode;
}
private static String decryptDES(String encryptDES, String key, String transformation, String algorithm) throws Exception {
//获取加密对象
Cipher cipher = Cipher.getInstance(transformation);
//加密规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
//初始化加密模式和算法
cipher.init(Cipher.DECRYPT_MODE, sks);
//加密
byte[] bytes = cipher.doFinal(Base64.decode(encryptDES.getBytes()));
return new String(bytes);
}
}
加密模式
ECB
ECB : Electronic codebook, 电子密码本. 需要加密的消息按照块密码的块大小被分为数个块,并对每个块进行独立加密
- 优点 : 可以并行处理数据
- 缺点 : 同样的原文生成同样的密文, 不能很好的保护数据
- 同时加密,原文是一样的,加密出来的密文也是一样的
CBC
CBC : Cipher-block chaining, 密码块链接. 每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块
Initialization Vector(IV,初始化向量)
- 优点 : 同样的原文生成的密文不一样
- 缺点 : 串行处理数据
填充模式
当需要按块处理的数据, 数据长度不符合块处理需求时, 按照一定的方法填充满块长的规则
NoPadding
不填充.
在DES加密算法下, 要求原文长度必须是8byte的整数倍
在AES加密算法下, 要求原文长度必须是16byte的整数倍
PKCS5Padding
数据块的大小为8位, 不够就补足
Tips
默认情况下, 加密模式和填充模式为 : ECB/PKCS5Padding
如果使用CBC模式, 在初始化Cipher对象时, 需要增加参数, 初始化向量IV : IvParameterSpec iv = new IvParameterSpec(key.getBytes());
加密模式和填充模式
AES/CBC/NoPadding (128)
AES/CBC/PKCS5Padding (128)
AES/ECB/NoPadding (128)
AES/ECB/PKCS5Padding (128)
DES/CBC/NoPadding (56)
DES/CBC/PKCS5Padding (56)
DES/ECB/NoPadding (56)
DES/ECB/PKCS5Padding (56)
DESede/CBC/NoPadding (168)
DESede/CBC/PKCS5Padding (168)
DESede/ECB/NoPadding (168)
DESede/ECB/PKCS5Padding (168)
RSA/ECB/PKCS1Padding (1024, 2048)
RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048)
RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)
DES/CBC/PKCS5Padding
/**
* @author :zsy
* @date :Created 2021/6/17 9:51
* @description:Des加密
*/
public class DesDemo {
public static void main(String[] args) throws Exception {
String input = "计算机网络";
//加密密钥
String key = "12345678";
//加密模式
//String transformation = "DES/ECB/PKCS5Padding";
String transformation = "DES/CBC/PKCS5Padding";
//加密算法
String algorithm = "DES";
String encryptDES = encryptDES(input, key, transformation, algorithm);
System.out.println("加密后:" + encryptDES);
String decryptDES = decryptDES(encryptDES, key, transformation , algorithm);
System.out.println("解密后:" + decryptDES);
}
private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {
//获取加密对象
Cipher cipher = Cipher.getInstance(transformation);
//加密规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
//创建IV向量
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
//初始化加密模式和算法
cipher.init(Cipher.ENCRYPT_MODE, sks,iv);
//加密
byte[] bytes = cipher.doFinal(input.getBytes());
String encode = Base64.encode(bytes);
return encode;
}
private static String decryptDES(String encryptDES, String key, String transformation, String algorithm) throws Exception {
//获取加密对象
Cipher cipher = Cipher.getInstance(transformation);
//加密规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
//创建IV向量
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
//初始化加密模式和算法
cipher. init(Cipher.DECRYPT_MODE, sks, iv);
//加密
byte[] bytes = cipher.doFinal(Base64.decode(encryptDES.getBytes()));
return new String(bytes);
}
}
非对称密码
对称密码的密钥安全极其重要,加密者和解密者需要提前协商密钥,并各自确保密钥的安全性,一但密钥泄露,即使算法是安全的也无法保障原文信息的私密性。在实际的使用中,远程的提前协商密钥不容易实现,即使协商好,在远程传输过程中也容易被他人获取,因此非对称密钥此时就凸显出了优势。
非对称密码有两支密钥,公钥(publickey)和私钥(privatekey),加密和解密运算使用的密钥不同。用公钥对原文进行加密后,需要由私钥进行解密;用私钥对原文进行加密后(此时一般称为签名),需要由公钥进行解密(此时一般称为验签)。公钥可以公开的,大家使用公钥对信息进行加密,再发送给私钥的持有者,私钥持有者使用私钥对信息进行解密,获得信息原文。因为私钥只有单一人持有,因此不用担心被他人解密获取信息原文。
- 非对称加密算法又称
。现代加密算法
- 非对称加密是计算机通信安全的基石,保证了加密数据
。不会被破解
- 与对称加密算法不同,非对称加密算法需要两个密钥:
和公开密钥(publickey)
私有密钥(privatekey)
- 公开密钥和私有密钥是
一对
- 如果用
对数据进行公开密钥
,只有用加密
才能对应的私有密钥
。解密
- 如果用
对数据进行私有密钥
,只有用加密
才能对应的公开密钥
。解密
- 因为加密和解密使用的是两个
的密钥,所以这种算法叫作不同
。非对称加密算法
举例
- 首先生成密钥对, 公钥为(5,14), 私钥为(11,14)
- 现在A希望将原文2发送给B
- A使用公钥加密数据. 2的5次方mod 14 = 4 , 将密文4发送给B
- B使用私钥解密数据. 4的11次方mod14 = 2, 得到原文2
特点
- 加密和解密使用不同的密钥
- 如果使用私钥加密, 只能使用公钥解密
- 如果使用公钥加密, 只能使用私钥解密
- 处理数据的速度较慢, 因为安全级别高
常见算法
RSA
RSA算法是一种基于大数不可能质数分解假设的公钥体系。简单的说,就是找两个很大的质数,一个公开给世界,称之为“公钥”,另一个不告诉任何人,称之为“私钥”。两把密钥互补——用公钥加密的密文可以使用私钥解密,反过来也一样。假设A寄信给B,他们知道对方的公钥,A可用B的公钥加密邮件寄出,B收到后用自己的私钥解出A的原文,这样就保证了邮件的安全性。
- 生成两个大素数p和q
- 计算这两个素数的乘积n = p * q
- 计算小于n并且与n互质的整数的个数,即欧拉函数φ(n) = (p - 1)(q- 1)
- 选择一个随机数e 满足1 < e < φ(n),并且e和φ(n)互质,即gcd(e, φ(n)) = 1
- 计算de = 1 mod φ(n)
- 保密d,p和q,公开n和e
明文以分组的形式加密,每一个分组小于log2n
加密x:c = x ^ e mod n 就可以得到密文c
解密c:x = c ^ d mod n 就可以得到明文x
公钥:(e, n)
私钥:(d, n)
/**
* @author :zsy
* @date :Created 2021/6/17 22:56
* @description:RSA算法
*/
public class RSADemo {
public static void main(String[] args) throws Exception {
String input = "计算机网络安全";
// 加密算法
String algorithm = "RSA";
generateKeyToFile(algorithm, "a.pub", "a.pri");
String encrypted = encryptRSA(algorithm, "a.pub", input);
System.out.println("公钥加密后:" + encrypted);
String original = decryptRSA(algorithm, "a.pri", encrypted);
System.out.println("私钥解密后:" + original);
}
/**
* 生成公钥和私钥并保存文件
* @param algorithm
* @param pubPath
* @param priPath
* @throws Exception
*/
public static void generateKeyToFile(String algorithm, String pubPath, String priPath) throws Exception{
//获取密钥生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
//获取密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//获取公钥
PublicKey publicKey = keyPair.getPublic();
//获取密钥
PrivateKey privateKey = keyPair.getPrivate();
//获取密钥字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
byte[] privateKeyEncoded = privateKey.getEncoded();
//进行Base编码
String publicKeyString = Base64.encode(publicKeyEncoded);
String privateKeyString = Base64.encode(privateKeyEncoded);
//System.out.println("私钥:" + privateKeyString);
//持久化文件
FileUtils.write(new File(pubPath), publicKeyString, Charset.forName("UTF-8"));
FileUtils.write(new File(priPath), privateKeyString, Charset.forName("UTF-8"));
}
/**
* 获取文件中的公钥和私钥
* @param algorithm
* @param path
* @return
* @throws Exception
*/
public static Key getKey(String algorithm, String path) throws Exception {
// 将文件内容转为字符串
String keyString = FileUtils.readFileToString(new File(path), Charset.defaultCharset());
// 获取密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 构建密钥规范 进行Base64解码
EncodedKeySpec spec;
if (path.contains("pub")) {
spec = new X509EncodedKeySpec(Base64.decode(keyString));
} else {
spec = new PKCS8EncodedKeySpec(Base64.decode(keyString));
}
// 生成密钥
return path.contains("pub") ?
keyFactory.generatePublic(spec) : keyFactory.generatePrivate(spec);
}
/**
* 使用公钥加密
* @param algorithm
* @param pubPath
* @param input
* @return
* @throws Exception
*/
private static String encryptRSA(String algorithm, String pubPath, String input) throws Exception {
//获取公钥
PublicKey publicKey = (PublicKey) getKey(algorithm, pubPath);
//System.out.println(Base64.encode(publicKey.getEncoded()));
//从文件中获取公钥
String publicKeyString = FileUtils.readFileToString(new File(pubPath), Charset.forName("UTF-8"));
byte[] keyBytes = Base64.decode(publicKeyString.getBytes());
//获取加密对象
Cipher cipher = Cipher.getInstance(algorithm);
//初始化加密对象
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
//加密
byte[] bytes = cipher.doFinal(input.getBytes());
return Base64.encode(bytes);
}
/**
* 使用私钥解密
* @param algorithm
* @param priPath
* @param encrypted
* @return
* @throws Exception
*/
private static String decryptRSA(String algorithm, String priPath, String encrypted) throws Exception{
//获取私钥
PrivateKey privateKey = (PrivateKey) getKey(algorithm, priPath);
//System.out.println("文件获取私钥:" + Base64.encode(privateKey.getEncoded()));
//获取加密对象
Cipher cipher = Cipher.getInstance(algorithm);
//初始化加密对象
cipher.init(Cipher.DECRYPT_MODE, privateKey);
//解密
byte[] bytes = cipher.doFinal(Base64.decode(encrypted.getBytes()));
return new String(bytes);
}
}
公钥加密后:AF9/11D7QVi7Z4cQqPcpRfLBt+/QPdDGtMPGcqaErvbNerSzZQNRlQijmudyW0PgEIGE56+d3N8R
jy8TfVcoNs6Xpj6DhwBVEvb23ebpUVTV/6buiONXxtyoW0tpAzGY2hB2XaaGB+pOsHG5tiwAxmTE
uk9H4B8HiroBTyNCan4=
私钥解密后:计算机网络安全
ECC
散列函数
散列函数,也见杂凑函数、摘要函数或哈希函数,可将任意长度的消息经过运算,变成固定长度数值,常见的有MD5、SHA-1、SHA256,多应用在文件校验,数字签名中。
MD5 可以将任意长度的原文生成一个128位(16字节)的哈希值
SHA-1可以将任意长度的原文生成一个160位(20字节)的哈希值
信息摘要
- 消息摘要(Message Digest)又称为数字摘要(Digital Digest)
- 它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生
- 使用数字摘要生成的值是不可以篡改的,为了保证文件或者值的安全
特点
无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。例如应用MD5算法摘要的消息有128个比特位,用SHA-1算法摘要的消息最终有160比特位的输出,只要输入的消息不同,对其进行摘要以后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出,消息摘要是单向、不可逆的
百度搜索
tomcat
,进入官网下载 ,会经常发现有
sha1
,
sha512
, 这些都是数字摘要
常见算法 :
- MD5算法 : 摘要结果16个字节, 转16进制后32个字节
- SHA1算法 : 摘要结果20个字节, 转16进制后40个字节
- SHA256算法 : 摘要结果32个字节, 转16进制后64个字节
- SHA512算法 : 摘要结果64个字节, 转16进制后128个字节
/**
* @author :zsy
* @date :Created 2021/6/17 15:59
* @description:消息摘要
*/
public class DigestDemo1 {
public static void main(String[] args) throws NoSuchAlgorithmException {
String input = "aa";
//算法
String algorithm = "MD5";
String encryptMD5 = getDigest(input, algorithm);
System.out.println(encryptMD5);
String encryptSHA1 = getDigest(input, "SHA-1");
System.out.println(encryptSHA1);
String encryptSHA256 = getDigest(input, "SHA-256");
System.out.println(encryptSHA256);
String encryptSHA512 = getDigest(input, "SHA-512");
System.out.println(encryptSHA512);
}
public static String getDigest(String input, String algorithm) throws NoSuchAlgorithmException {
//创建消息摘要对象
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
//执行消息摘要算法
byte[] digest = messageDigest.digest(input.getBytes());
System.out.println(algorithm + "加密密文长度:" + digest.length);
return toHex(digest);
}
private static String toHex(byte[] digest) {
StringBuilder builder = new StringBuilder();
for (byte b : digest) {
String string = Integer.toHexString(b & 0xff);
string = string.length() > 1 ? string : "0" + string;
builder.append(string);
}
return builder.toString();
}
}
数字签名
数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。它是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名是非对称密钥加密技术与数字摘要技术的应用。
含义是:在网络中传输数据时候,给数据添加一个数字签名,表示是谁发的数据,而且还能证明数据没有被篡改。
数字签名的主要作用就是保证了数据的有效性(验证是谁发的)和完整性(证明信息没有被篡改)。下面我们就来好好地看一下他的底层实现原理是什么样子的。
基本原理
为了理解得清楚,我们通过案例一步一步来讲解。话说张三有俩好哥们A、B。由于工作原因,张三和AB写邮件的时候为了安全都需要加密。于是张三想到了数字签名:
整个思路是这个样子的:
第一步:加密采用非对称加密,张三有三把钥匙,两把公钥,送给朋友。一把私钥留给自己。
第二步:A或者B写邮件给张三:A先用公钥对邮件加密,然后张三收到邮件之后使用私钥解密。
第三步:张三写邮件给A或者B:
(1)张三写完邮件,先用hash函数生成邮件的摘要,附着在文章上面,这就完成了数字签名,然后张三再使用私钥加密。就可以把邮件发出去了。
(2)A或者是B收到邮件之后,先把数字签名取下来,然后使用自己的公钥解密即可。这时候取下来的数字签名中的摘要若和张三的一致,那就认为是张三发来的,再对信件本身使用Hash函数,将得到的结果,与上一步得到的摘要进行对比。如果两者一致,就证明这封信未被修改过。
上面的流程我们使用一张图来演示一下:
首先把公钥送给朋友A和B:
还有就是最后一个比较麻烦的,张三给A或者B发邮件:
数字证书
上面提到我们对签名进行验证时,需要用到公钥。如果公钥是伪造的,那我们无法验证数字签名了,也就根本不可能从数字签名确定对方的合法性了。这时候证书就闪亮登场了。我们可能都有考各种证书的经历,比如说普通话证书,四六级证书等等,但是归根结底,到任何场合我们都能拿出我们的证书来证明自己确实已经考过了普通话,考过了四六级。这里的证书也是同样的道理。
如果不理解证书的作用,我们可以举一个例子,比如说我们的毕业证书,任何公司都会承认。为什么会承认?因为那是国家发得,大家都信任国家。也就是说只要是国家的认证机构,我们都信任它是合法的。
那么这个证书是如何生成的呢?我们再来看一张图:
此时即使张三的朋友A把公钥弄错了,张三也可以通过这个证书验证。
网页加密
我们看一个应用“数字证书”的实例:https协议。这个协议主要用于网页加密
首先,客户端向服务器发出加密请求。
如何设置密码才安全
密码不要太常见,不要使用类似于123456式的常用密码。
各应用软件密码建议不同,撞库:使用一个密码去试其他的平台的密码,避免出现一个应用数据库被脱库,全部应用密码崩塌,
可在设置密码时增加注册时间、注册地点、应用特性等方法。例如tianjin123456,表示在天津注册的该应用。
参考资料:
密码学
网络安全原理及加密算法
MD5加密算法
几种常见的加密算法
在线获取消息摘要