Java加密解密学习笔记

文章目录

  • 1 密码学简介
  • 1.1 密码学基本概念
  • 1.2 密码学的历史
  • 1.2.1 古典密码学
  • 1.2.1.1 替换法
  • 1.2.1.2 移位法
  • 1.2.1.3 古典密码学破解方式
  • 1.2.2 近代密码学
  • 1.2.3 现代密码学
  • 1.2.3.1 哈希函数
  • 1.2.3.2 对称解密
  • 1.2.3.3 非对称加密
  • 1.3 如何设置密码才安全
  • 2 Byte和bit
  • 2.1 英文字母
  • 2.2 中文
  • 3 Base64
  • 3.1 Base64
  • 3.1.1 为什么乱码
  • 3.1.2 解决方案
  • 3.1.4 Base64编码原理
  • 3.1.4.1 当字符串字符个数为3的整数倍时
  • 3.1.4.2 当字符串个数除以3余数为2时
  • 3.1.4.3 当余数为1时
  • 3.2 toString和new String区别
  • 4 凯撒加密
  • 4.1 凯撒加密
  • 4.2 凯撒解密
  • 4.3 频率分析法
  • 5 DES加密解密
  • 5.1 对称加密
  • 5.2 DES加密
  • 5.3 DES解密
  • 5.4 DES缺点
  • 6 AES加密解密
  • 6.1 AES加密
  • 6.2 AES解密
  • 7 数字摘要
  • 7.1 特点
  • 7.2 消息摘要算法应用
  • 7.3 MD5
  • 7.4 SHA-1
  • 7.5 SHA-256
  • 7.6 SHA-512
  • 8 RSA加密解密
  • 8.1 非对称加密
  • 8.2 RSA加密解密
  • 9 数字签名
  • 9.1 数字签名介绍
  • 9.2 数字签名原理
  • 9.3 获取数字签名
  • 9.4 校验数字签名
  • 9.5 数字信封
  • 9.6 数字证书
  • 9.6.1 数字证书简介
  • 9.6.2 CA认证中心
  • 9.6.3 代码实现
  • 9.6.4 数字证书案例


1 密码学简介

密码学:主要是研究编制密码和破译密码的学科

密码学的主要目的:用一句大白话:研究如何隐藏信息且吧信息传递出去的 一个学科

古典密码学--------->古代史

近代密码学---------->近代史

现代密码学----------->现代史

1.1 密码学基本概念

密码学:网络安全,信息安全,区块链这些学科的基础

密码学存在好几千年

1.2 密码学的历史

1.2.1 古典密码学

古典密码学----->古代史

古代就已经开始使用密码,目的:就是希望保护信息

1.2.1.1 替换法

就是使用固定的信息,将原文替换成密文

例如: bee 将b替换成w 单词就变成wpp

将e替换成p

替换法的加密方式:

第1种:单表替换 ----原文和密文使用的是同一张表

例如:abcde -----> swtrp

第2种:多表替换 —有多张表,原文和密文进行对比

表单1:abcde - swtrp 表单2: abcde - chfhk 表单3:abcde - jftou

原文:bee

密钥:312

密文:fpk

1.2.1.2 移位法

移位法:是按照字母,在字母表上面的位置,进行移动

凯撒加密

例如:abcde -----往后移动2位-----cdefg

java 加密 解密 java密码加密和解密_java

1.2.1.3 古典密码学破解方式

频率分析法:概率论

自从概率论出现以后,就把古典密码破解完了。

1.2.2 近代密码学

近代密码学 -----> 近代史 二战:机器加密

核心原理和古典密码学是一模一样的。也是 替换法和移位法,但到了二战之后,有了加密,用的表更多,这样就增加了难度。

近代密码学被图灵破解了,

图灵:人工智能之父

1.2.3 现代密码学

现代密码学 ----- > 现代史

1.2.3.1 哈希函数

哈希函数,也叫散列函数,摘要函数,可将任意长度的消息经过运算,变成固定长度数值,

常见的有:MD5,SHA-1,SHA-256,SHA-512,多应用在文件校验,数字签名中。

MD5:可以将任意长度的原文生成一个128位(16字节)的哈希值

SHA-1:可以将任意长度的原文生成一个160位(20字节)的哈希值

1.2.3.2 对称解密

对称加密,使用的加密方式和解密方式,使用的是同一把密钥

常见的加密方式:DES加密解密,AES加密解密

流加密,块加密

toString()和new String() 核心原理和区别

加密模式:ECB CBC

填充模式:NoPadding 和 PKCS5Padding

1.2.3.3 非对称加密

非对称加密:2把密钥,使用公钥加密,必须使用私钥解密,

或者私钥加密,必须使用公钥解密

RSA算法和EGC算法

数字摘要:base64核心加密原则,和base64原理

数字签名和数字证书

keytool工具的妙用

非对称加密,现在用的最多最多

1.3 如何设置密码才安全

1.密码不要太常见,不要使用123456作为密码

2.各个应用软件里面的密码不要设置一样, 撞库

小网站,密码措施不好,可能明文存储,密码拿到后,就可以去试你的支付宝,京东,微信

只要知道你一个软件的密码,就相当于知道了你所有软件的密码

不知名网站,倒推出知名网站的密码

3.在设置密码的时候,可以加一些特殊的标记

注册京东:jd 支付宝:zfb

推荐:软件首字母+密码

2 Byte和bit

2.1 英文字母

Byte: 字节,数据存储的基本单位,比如移动硬盘1T,单位是byte.

bit: 比特,又叫位,一个位要么是0要么是1,数据传输的单位,

比如家里的宽度100MB,下载速度并没有达到100MB,一般都是12-13MB,那么是因为需要使用100/8.

byte[] bytes = a.getBytes(); 这行代码就是获取字符串a的bit位。

package com.tangguanlin.encrypt_decrypt;
/**
 * 说明:字节和位
 * 作者:汤观林
 * 日期:2022年01月03日 15时
 */
public class BytebitTest {
    public static void main(String[] args) {
        String a  = "cdb";
        byte[] bytes = a.getBytes(); //一个英文对应一个字节
        for(byte ebyte:bytes){
            int temp = ebyte;
            System.out.println(temp);
            String bit = Integer.toBinaryString(temp); //获取字节的位
            System.out.println(bit);
        }
    }
}

运行结果:

99
1100011
100
1100100
98
1100010

2.2 中文

根据编码格式不一样,对应的字节也不一样

如果是utf-8,一个中文对应的是三个字节

如果是GBK,一个中文对应的是两个字节

英文,什么编码格式都是对应一个字节

package com.tangguanlin.encrypt_decrypt;
import java.io.UnsupportedEncodingException;
/**
 * 说明:中文
 * 作者:汤观林
 * 日期:2022年01月03日 15时
 */
public class BytebitTest2 {
    /**
     *   根据编码格式不一样,对应的字节也不一样
     *   如果是utf-8,一个中文对应的是三个字节
     *   如果是GBK,一个中文对应的是两个字节
     *             英文,什么编码格式都是对应一个字节
     * @param args
     */
    public static void main(String[] args) throws UnsupportedEncodingException {
        String a  = "尚";
        byte[] bytes = a.getBytes(); 
        for(byte ebyte:bytes){
            System.out.println(ebyte);
            int temp = ebyte;
            String bit = Integer.toBinaryString(temp); //获取字节的位
            System.out.println(bit);
        }
    }
}

运行结果:

-27
11111111111111111111111111100101
-80
11111111111111111111111110110000
-102
11111111111111111111111110011010

3 Base64

3.1 Base64

base64不是加密算法,是可读性算法

base64的目的不是保护我们的数据,目的是为了可读性

把不在ASCII里面的编码出现的乱码用其他不会乱码的编码进行

3.1.1 为什么乱码

java 加密 解密 java密码加密和解密_System_02

3.1.2 解决方案

加密后的结果是字节数组,这些被加密后的字节在码表(例如:GBK,UTF-8码表)上找不到对应字符,会出现乱码,

当乱码字符串再次转为字节数组时,长度会变化,导致解密失败,所以转换后的数据是不安全的。

使用Base64对字节数组进行编码,任何字节都能映射成对应的Base64字符,之后能恢复到字节数组,

利于加密后数据的保持传输,所以转换时安全的。

结论:通过Base64编码,所有的字节都不会丢失。

### 3.1.3 Base64码表

java 加密 解密 java密码加密和解密_System_03

base64是由64个字符组成,大写A-Z,小写a-z,数字0-9,两个符号:+和/

base58一般用来比特币里面的一种编码方式

base58里面,没有数字0,也没有字幕o,没有大写字母I,没有小写字母i,没有+,也没有 / 共58个字符。

3.1.4 Base64编码原理

base64,是三个字节为一组,一个字节是8位,一共就是24位,base64把一个字节,转换成4组,每组6位,

一个字节,应该是8位,缺少2位,就在高位进行补齐,在高位进行补0,这样做的好处,base64取后面的6位,

前面的2位,会把它去掉,可以把base64控制在0-63之间。

111111 = 32+16+8+4+2+1 =63

在base64里面,需要设置一共是3个字节,为一组,如果在输出的时候,不够3个字节,就需要使用=进行补齐。

3.1.4.1 当字符串字符个数为3的整数倍时

比如字符串“ABC”,其在计算机内存中的十六进制表示为41,42,43,十进制表示为65,66,67。

二进制表示为:

01000001 01000010 01000011 将这三个二进制依次取6bit

010000 010100 001001 000011 ,将这四个二进制数转换成十六进制数为:

十进制数为 16,20,9,3. 对照上面的码表,分别查找出对应的字符为Q,U,J ,D。

也就是说,字符串“ABC”经过Base64编码后得出“QUJD”.这个最简单的情况,即ASCII码字符数刚好可以被3整除。

接着继续讨论余数为2,为1的情况。

3.1.4.2 当字符串个数除以3余数为2时

比如字符串“ce”,其在内存中十六进制表示为63,65;十进制表示为99,101,

二进制表示为:

01100011 01100101 依次取6bit

011000 110110 010100 这时,第3个字符不足6位,在后面不0。

这3个数10进制为24,54,20,对照码表得出结果Y2U,

编码后的字符个数不足4位,用“=”填充,最后编码得出“Y2U=====”。

3.1.4.3 当余数为1时

比如字符串{,其在内存中的十六进制表示为$7B,十进制为123,

二进制表示为:

01111011 依次取6bit

011110 110000

十进制表示为:30,48.对照码表得出结果为“ew”,补上“==”,

最后编码为“ew======”.

解码也很简单,是编码的逆过程,即将每个字符对照码表换算成6bit的二进制数,

然后重组起来,按8位进行截取,得出原码。

3.2 toString和new String区别

toString():[B@617c74e5
new String():硅谷

注意:如果在使用编码,进行加密和解密的时候,需要使用new String()这种方式。

区别:

toString()方法,这个方法调用的实际是Object里面的toString方法,

一般在object的toString方法,返回的实际上是哈希值,是一个地址。

new String()方法:是根据参数,参数是一个字节数组,使用Java虚拟机默认编码格式,会把这个字节数组进行decode,找到对应的字符,如果虚拟机的编码格式是ISO-8859-1,会去找ASCII里面的编码进行参照,找对应的字符。

什么时候使用new String,什么时候 使用 toString()

new String(): 一般在转码的时候,需要使用new String();

toString(): 做对象打印的时候,或者想得到地址的时候,就使用toString。

4 凯撒加密

凯撒加密属于移动法

移位法:是按照字母,在字母表上面的位置,进行移动

是把26个字母,进行位移,往左边或者右边进行位移,在位移的时候,需要注意,最多只能移动25位。

密钥:加密与解密算法的参数,直接影响对明文进行变换的结果。

移位法一般是在英文国家,因为文字是英文字母。

中国使用的是汉子,由偏旁部首组成,所以中国用的是“拆字法”,汉字没办法进行移位

java 加密 解密 java密码加密和解密_码表_04

4.1 凯撒加密

package com.tangguanlin.encrypt_decrypt;
/**
 * 说明:凯撒加密
 * 作者:汤观林
 * 日期:2022年01月03日 00时
 */
public class KaiserTest {
    public static void main(String[] args) {

        //原文
        String input =  "Hello world";
        //把原文右边移动三位
        int key = 3;
        //把字符串变成字节数组
        char[] inputChars = input.toCharArray();
        StringBuilder sb = new StringBuilder();
        for(char c :inputChars){
            int b =  c;
            b = b + 3;
            char d  = (char)b;
            sb.append(d);
        }
        System.out.println("凯撒加密:"+sb.toString());

    }
}

运行结果:

凯撒加密:Khoor#zruog

4.2 凯撒解密

package com.tangguanlin.encrypt_decrypt;
/**
 * 说明:凯撒解密
 * 作者:汤观林
 * 日期:2022年01月03日 00时
 */
public class KaiserTest {
    public static void main(String[] args) {

        //Hello world的密文
        String input_miwen =  "Khoor#zruog";  
       char[] miwen_chars = input_miwen.toCharArray();
        StringBuffer sb = new StringBuffer();
        for(char miwenChar:miwen_chars){
            int a  = miwenChar;
            a = a - 3;
            char mingwenChar = (char)a;
            sb.append(mingwenChar);
        }
        System.out.println("凯撒解密:"+sb.toString());

    }
}

运行结果:

凯撒解密:Hello world

4.3 频率分析法

频率分析法:研究字母或者字母组合在文本中出现的频率。按照频率的高低进行排序。也叫暴力破解法:穷举法

目的:在不知道密钥的情况下,也想进行破解密文。

原理:概率论,比如以英文为例:字母e出现的频率是最高的,第二频率最高的是t,然后就是a,

当我们拿到密文的时候,密文里面也会出现一个频率最高的字母,假设密文里面出现频率最高的是j,

可以加上密文里面的j,就是明文里面的e,假设密文里面出现频率第二高的字母,就是明文当中的t。

java 加密 解密 java密码加密和解密_java_05

字母频率顺序:e ----- t--------a

密文频率顺序: #------h--------d

第1备选文件: 密钥是 e—>#

第2备选文件:密钥是 e---->h

第3备选文件:密钥是 3---->d

最后从3份备选文件中 找出能组成一个完整意思的文件作为破解原文。

key: 1-25 只有25种可能性,25种密钥。

代码:

package com.tangguanlin.encrypt_decrypt;
import java.util.*;
/**
 * 说明:在不知道密钥的情况下凯撒解密
 * 作者:汤观林
 * 日期:2022年01月03日 14时
 */
public class KaiserDecrypt {
    public static void main(String[] args) {

        //原文
        String oldStr = "The early Apache server was a big hit, but we all knew that the codebase needed a general overhaul and redesign. During May-June 1995, while Rob Hartill and the rest of the group focused on implementing new features for 0.7.x (like pre-forked child processes) and supporting the rapidly growing Apache user community, Robert Thau designed a new server architecture (code-named Shambhala) which included a modular structure and API for better extensibility, pool-based memory allocation, and an adaptive pre-forking process model. The group switched to this new server base in July and added the features from 0.7.x, resulting in Apache 0.8.8 (and its brethren) in August.";
        System.out.println("原文:"+oldStr);
        String miwenStr = KaiserTest.kaiserEncrypt(oldStr.trim());

        //统计密文中字母出现的次数
        List<Map.Entry<Character,Integer>> list =  miwenStrCount(miwenStr);

        for(int i =list.size()-1;i>list.size()-5;i--){
            //获取密钥
            int number = list.get(i).getKey() - 'e';
            //密文解密
            char[] miwenCharArray = miwenStr.toCharArray();
            StringBuffer sb = new StringBuffer();
            for(char miwenChar:miwenCharArray){
                int temp = miwenChar;
                int temp2 = temp - number;
                char yuanwenChar = (char)temp2;
                sb.append(yuanwenChar);
            }
            String yuanwenStr = sb.toString();
            System.out.println("备选原文是:"+yuanwenStr);
        }
    }

    //统计密文中字母出现的次数
    public static List<Map.Entry<Character,Integer>> miwenStrCount(String miwenStr){
        Map<Character,Integer> map = new HashMap<>();
        char[] chars = miwenStr.toCharArray();
        for(char c:chars){
            if(!map.containsKey(c)){
                map.put(c,1);
            }else{
                Integer count = map.get(c);
                map.put(c,count+1);
            }
        }

        List<Map.Entry<Character,Integer>> list = new ArrayList<>(map.entrySet());
        list.sort(Comparator.comparing(Map.Entry::getValue));
        return  list;
    }
}

运行结果:

原文:The early Apache server was a big hit, but we all knew that the codebase needed a general overhaul and redesign. During May-June 1995, while Rob Hartill and the rest of the group focused on implementing new features for 0.7.x (like pre-forked child processes) and supporting the rapidly growing Apache user community, Robert Thau designed a new server architecture (code-named Shambhala) which included a modular structure and API for better extensibility, pool-based memory allocation, and an adaptive pre-forking process model. The group switched to this new server base in July and added the features from 0.7.x, resulting in Apache 0.8.8 (and its brethren) in August.
凯撒加密:Wkh#hduo|#Dsdfkh#vhuyhu#zdv#d#elj#klw/#exw#zh#doo#nqhz#wkdw#wkh#frghedvh#qhhghg#d#jhqhudo#ryhukdxo#dqg#uhghvljq1#Gxulqj#Pd|0Mxqh#4<<8/#zkloh#Ure#Kduwloo#dqg#wkh#uhvw#ri#wkh#jurxs#irfxvhg#rq#lpsohphqwlqj#qhz#ihdwxuhv#iru#31:1{#+olnh#suh0irunhg#fklog#surfhvvhv,#dqg#vxssruwlqj#wkh#udslgo|#jurzlqj#Dsdfkh#xvhu#frppxqlw|/#Urehuw#Wkdx#ghvljqhg#d#qhz#vhuyhu#dufklwhfwxuh#+frgh0qdphg#Vkdpekdod,#zklfk#lqfoxghg#d#prgxodu#vwuxfwxuh#dqg#DSL#iru#ehwwhu#h{whqvlelolw|/#srro0edvhg#phpru|#doorfdwlrq/#dqg#dq#dgdswlyh#suh0irunlqj#surfhvv#prgho1#Wkh#jurxs#vzlwfkhg#wr#wklv#qhz#vhuyhu#edvh#lq#Mxo|#dqg#dgghg#wkh#ihdwxuhv#iurp#31:1{/#uhvxowlqj#lq#Dsdfkh#31;1;#+dqg#lwv#euhwkuhq,#lq#Dxjxvw1
备选原文是:™­ªeª¦·±¾e†µ¦¨­ªe¸ª·»ª·e¼¦¸e¦e§®¬e­®¹qe§º¹e¼ªe¦±±e°³ª¼e¹­¦¹e¹­ªe¨´©ª§¦¸ªe³ªª©ª©e¦e¬ª³ª·¦±e´»ª·­¦º±e¦³©e·ª©ª¸®¬³se‰º·®³¬e’¦¾rº³ªev~~zqe¼­®±ªe—´§e¦·¹®±±e¦³©e¹­ªe·ª¸¹e´«e¹­ªe¬·´ºµe«´¨º¸ª©e´³e®²µ±ª²ª³¹®³¬e³ª¼e«ª¦¹º·ª¸e«´·eus|s½em±®°ªeµ·ªr«´·°ª©e¨­®±©eµ·´¨ª¸¸ª¸ne¦³©e¸ºµµ´·¹®³¬e¹­ªe·¦µ®©±¾e¬·´¼®³¬e†µ¦¨­ªeº¸ª·e¨´²²º³®¹¾qe—´§ª·¹e™­¦ºe©ª¸®¬³ª©e¦e³ª¼e¸ª·»ª·e¦·¨­®¹ª¨¹º·ªem¨´©ªr³¦²ª©e˜­¦²§­¦±¦ne¼­®¨­e®³¨±º©ª©e¦e²´©º±¦·e¸¹·º¨¹º·ªe¦³©e†•Že«´·e§ª¹¹ª·eª½¹ª³¸®§®±®¹¾qeµ´´±r§¦¸ª©e²ª²´·¾e¦±±´¨¦¹®´³qe¦³©e¦³e¦©¦µ¹®»ªeµ·ªr«´·°®³¬eµ·´¨ª¸¸e²´©ª±se™­ªe¬·´ºµe¸¼®¹¨­ª©e¹´e¹­®¸e³ª¼e¸ª·»ª·e§¦¸ªe®³eº±¾e¦³©e¦©©ª©e¹­ªe«ª¦¹º·ª¸e«·´²eus|s½qe·ª¸º±¹®³¬e®³e†µ¦¨­ªeus}s}em¦³©e®¹¸e§·ª¹­·ª³ne®³e†º¬º¸¹s
备选原文是:The early Apache server was a big hit, but we all knew that the codebase needed a general overhaul and redesign. During May-June 1995, while Rob Hartill and the rest of the group focused on implementing new features for 0.7.x (like pre-forked child processes) and supporting the rapidly growing Apache user community, Robert Thau designed a new server architecture (code-named Shambhala) which included a modular structure and API for better extensibility, pool-based memory allocation, and an adaptive pre-forking process model. The group switched to this new server base in July and added the features from 0.7.x, resulting in Apache 0.8.8 (and its brethren) in August.
备选原文是:G[XXTe_l4cTV[XfXeiXejTfTU\Z[\gUhgjXT__^aXjg[Tgg[XVbWXUTfXaXXWXWTZXaXeT_biXe[Th_TaWeXWXf\Za!7he\aZ@Tl =haX$,,(j[\_XEbU;Teg\__TaWg[XeXfgbYg[XZebhcYbVhfXWba\`c_X`Xag\aZaXjYXTgheXfYbe#!*!k_\^XceX Ybe^XWV[\_WcebVXffXfTaWfhccbeg\aZg[XeTc\W_lZebj\aZ4cTV[XhfXeVb``ha\glEbUXegG[ThWXf\ZaXWTaXjfXeiXeTeV[\gXVgheXVbWX aT`XWF[T`U[T_Tj[\V[\aV_hWXWT`bWh_TefgehVgheXTaW4C<YbeUXggXeXkgXaf\U\_\glcbb_ UTfXW`X`belT__bVTg\baTaWTaTWTcg\iXceX Ybe^\aZcebVXff`bWX_!G[XZebhcfj\gV[XWgbg[\faXjfXeiXeUTfX\a=h_lTaWTWWXWg[XYXTgheXfYeb`#!*!keXfh_g\aZ\a4cTV[X#!+!+TaW\gfUeXg[eXa\a4hZhfg!
备选原文是:Xli$ievp}$Etegli$wivziv${ew$e$fmk$lmx0$fyx${i$epp$ori{$xlex$xli$gshifewi$riihih$e$kirivep$szivleyp$erh$vihiwmkr2$Hyvmrk$Qe}1Nyri$5==90${lmpi$Vsf$Levxmpp$erh$xli$viwx$sj$xli$kvsyt$jsgywih$sr$mqtpiqirxmrk$ri{$jiexyviw$jsv$42;2|$,pmoi$tvi1jsvoih$glmph$tvsgiwwiw-$erh$wyttsvxmrk$xli$vetmhp}$kvs{mrk$Etegli$ywiv$gsqqyrmx}0$Vsfivx$Xley$hiwmkrih$e$ri{$wivziv$evglmxigxyvi$,gshi1reqih$Wleqflepe-${lmgl$mrgpyhih$e$qshypev$wxvygxyvi$erh$ETM$jsv$fixxiv$i|xirwmfmpmx}0$tssp1fewih$qiqsv}$eppsgexmsr0$erh$er$ehetxmzi$tvi1jsvomrk$tvsgiww$qship2$Xli$kvsyt$w{mxglih$xs$xlmw$ri{$wivziv$fewi$mr$Nyp}$erh$ehhih$xli$jiexyviw$jvsq$42;2|0$viwypxmrk$mr$Etegli$42<2<$,erh$mxw$fvixlvir-$mr$Eykywx2

解密成功!

5 DES加密解密

5.1 对称加密

对称加密:加密和解密使用的是同一把钥匙,分成流加密和块加密

java 加密 解密 java密码加密和解密_字符串_06

比如:

流加密:123456789,先加密1,再加密2,再加密3

块加密:123456789,先加密123,再加密456,最后加密789,相当于分组加密

对称加密:采用单密钥密码系统的加密方法,同一个密码可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。

。示例:

。我们现在有一个原文3要发送给B

。设置密钥为108,3*108 = 324,将324作为密文发送给B

。B拿到密文324后,使用 324/108=3得到密文

。常见加密算法

。DES:Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为

联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。

JDK中自带了DES加密解密算法。

。AES:Advanced Encryption standard,高级加密算法,在密码夘中又称为Rijndae加密法,是美国联邦政府采用的一种区块加密标

准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。

DES不行了之后,才使用的AES。

。特点:

。加密速度快,可以加密大文件

。密文可逆,一旦密钥文件泄漏,就会导致数据泄漏

。加密后编码表找不到对应字符,出现乱码

。一般结合base64使用,用base64转码

5.2 DES加密

如果使用DES进行加密,那么密钥必须是8个字节。

DES是由IBM公司研制的算法。

DES是一个分组加密算法,典型的DES以64位为分组对数据加密,加密和解密用的是同一个算法。它的密钥长度是56位(因为每个第8位都用作奇偶校验),密钥可以是任意的56位的数,其保密性依赖于密钥。

package com.tangguanlin.encrypt_decrypt;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
 * 说明:DES加密解密
 * 作者:汤观林
 * 日期:2022年01月03日 16时
 */
public class DesTest {
    public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {

        //原文
        String input = "硅谷";
        //定义key
        //如果使用DES进行加密,那么密钥必须是8个字节
        String key = "12345678";

        //1.创建DES对象
        //参数:接收一种算法
        Cipher cipher = Cipher.getInstance("DES");

        /**
         * 2.创建加密规则
         * 第1个参数:表示key的字节
         * 第2个参数:加密的类型 DES
         */
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "DES");

        /**
         * 3.进行加密初始化
         * 第1个参数是模式,加密模式:Cipher.ENCRYPT_MODE 解密模式:Cipher.DECRYPT_MODE
         * 第2个参数:加密规则
         */
        cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec);

        //4.调用加密方法
        //参数:原文的字节数组
        byte[] bytes = cipher.doFinal(input.getBytes());

        //打印密码,直接打印密文,会乱码
        //乱码 是因为在ASCII中找不到对应字符
        //System.out.println(new String(bytes));

        //创建base64对象
        //使用base64转码
        String encode = Base64.encode(bytes);
        System.out.println(encode);
    }
}

运行结果:

qANksk5lvqM=

5.3 DES解密

package com.tangguanlin.encrypt_decrypt;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
 * 说明:DES加密解密
 * 作者:汤观林
 * 日期:2022年01月03日 16时
 */
public class DesTest {
    public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, Base64DecodingException {

        //定义key
        //如果使用DES进行加密,那么密钥必须是8个字节
        String key = "12345678";
        
 		//1.创建DES对象
        //参数:接收一种算法
        Cipher cipher2 = Cipher.getInstance("DES");
        
        /**
         * 2.创建加密规则
         * 第1个参数:表示key的字节
         * 第2个参数:加密的类型 DES
         */
        SecretKeySpec secretKeySpec2 = new SecretKeySpec(key.getBytes(), "DES");

        /**
         * 3.进行加密初始化
         * 第1个参数是模式,加密模式:Cipher.ENCRYPT_MODE 解密模式:Cipher.DECRYPT_MODE
         * 第2个参数:加密规则
         */
        cipher2.init(Cipher.DECRYPT_MODE,secretKeySpec2);

        //base64解码
        byte[] decode = Base64.decode(encode);
        
        //4.调用加密方法
        //参数:密码的字节数组
        byte[] bytes2 = cipher2.doFinal(decode);

        System.out.println("解密后:"+new String(bytes2));
    }
}

运行结果:

原文:硅谷
加密后:qANksk5lvqM=
解密后:硅谷

DES的安全性首先取决于密钥的长度。密钥越长,破译者利用穷举法搜索密钥的难度就越大,DES用64bit分组长度和56bit密钥长度:

5.4 DES缺点

随着软硬件技术的发展,多核CPU,分布式计算,量子计算等理论的实现,DES在穷举方式的暴力攻击下还是相当的脆弱的,因此很多人想办法用某种算法来替代它,面对这种需求广泛被采用的有两种方案:

1.设计一套全新的算法,例如AES,这个在后面会说到

2.为了保护已有软硬件投资,仍使用DES,但使用多个密钥进行多次加密,这就是多重DES加密。但是这种方式运算很慢。

6 AES加密解密

6.1 AES加密

AES加密和解密和DES代码是一样的,只是更高级而已。

如果使用AES进行加密,那么密钥必须是16个字节,128bit,现在的主流都是用AES进行加密解密了。

package com.tangguanlin.encrypt_decrypt;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
 * 说明:AES加密解密
 * 作者:汤观林
 * 日期:2022年01月03日 16时
 */
public class DesTest {
    public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {

        //原文
        String input = "硅谷";
        //定义key
        //如果使用AES进行加密,那么密钥必须是16个字节
        String key = "1234567812345678";

        //1.创建AES对象
        //参数:接收一种算法
        Cipher cipher = Cipher.getInstance("AES");

        /**
         * 2.创建加密规则
         * 第1个参数:表示key的字节
         * 第2个参数:加密的类型 AES
         */
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");

        /**
         * 3.进行加密初始化
         * 第1个参数是模式,加密模式:Cipher.ENCRYPT_MODE 解密模式:Cipher.DECRYPT_MODE
         * 第2个参数:加密规则
         */
        cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec);

        //4.调用加密方法
        //参数:原文的字节数组
        byte[] bytes = cipher.doFinal(input.getBytes());

        //打印密码,直接打印密文,会乱码
        //乱码 是因为在ASCII中找不到对应字符
        //System.out.println(new String(bytes));

        //创建base64对象
        //使用base64转码
        String encode = Base64.encode(bytes);
        System.out.println(encode);
    }
}

运行结果:

LIdHYmgk7CfTDY/pvc/SlQ==

6.2 AES解密

package com.tangguanlin.encrypt_decrypt;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
 * 说明:AES加密解密
 * 作者:汤观林
 * 日期:2022年01月03日 16时
 */
public class DesTest {
    public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, Base64DecodingException {

 		//1.创建AES对象
        //参数:接收一种算法
        Cipher cipher2 = Cipher.getInstance("AES");
        
        /**
         * 2.创建加密规则
         * 第1个参数:表示key的字节
         * 第2个参数:加密的类型 AES
         */
        SecretKeySpec secretKeySpec2 = new SecretKeySpec(key.getBytes(), "AES");

        /**
         * 3.进行加密初始化
         * 第1个参数是模式,加密模式:Cipher.ENCRYPT_MODE 解密模式:Cipher.DECRYPT_MODE
         * 第2个参数:加密规则
         */
        cipher2.init(Cipher.DECRYPT_MODE,secretKeySpec2);

        //base64解码
        byte[] decode = Base64.decode(encode);
        
        //4.调用加密方法
        //参数:密码的字节数组
        byte[] bytes2 = cipher2.doFinal(decode);

        System.out.println("解密后:"+new String(bytes2));
    }
}

运行结果:

原文:硅谷
加密后:LIdHYmgk7CfTDY/pvc/SlQ==
解密后:硅谷

7 数字摘要

消息摘要也叫哈希函数,又称数字摘要。

消息摘要(Message Diget) 简称MD5、、

。它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数读消息进行作用而产生

。使用数字摘要生成的值是不可以篡改的,为了保证文件或者值的安全。

目的:为了防止文件篡改

MD5是一种散列函数,因此根据定义它是不可逆的。加密的东西,不能解密。

7.1 特点

无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。

例如:应用MD5算法摘要的消息有128个比特位,

用SHA-1算法摘要的消息最终有160比特位的输出

只要输入的消息不同,对其进行摘要以后产生的摘要消息也必不相同,但相同的输入必会产生相同的输出

消息摘要是单向的,不可逆的。

常见算法:

1.MD5 128位

2.SHA-1 160位

3.SHA-256

4.SHA-512


7.2 消息摘要算法应用

java 加密 解密 java密码加密和解密_System_07

7.3 MD5

package com.tangguanlin.encrypt_decrypt;
import org.apache.xmlbeans.impl.util.Base64;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
 * 说明:MD5加密
 * 作者:汤观林
 * 日期:2022年01月03日 22时
 */
public class DigestTest {
    public static void main(String[] args) throws NoSuchAlgorithmException {

        //原文
        String input = "aa";

        //创建消息摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance("MD5");
        //执行消息摘要算法
        byte[] bytes = messageDigest.digest(input.getBytes());

        //使用base64转码
        //byte[] encode = Base64.encode(bytes);
        StringBuffer sb = new StringBuffer();
        for (byte b : bytes) {
            //把密文转换成16进制 显示
            String s = Integer.toHexString(b & 0xff);
            if(s.length()==1){
                s = "0"+s;  //密文长度为1,高位补0
            }
            sb.append(s);
        }
        System.out.println(sb.toString());
    }
}

运行结果:

4124bc0a9335c27f086f24ba207a4912

7.4 SHA-1

package com.tangguanlin.encrypt_decrypt;
import org.apache.xmlbeans.impl.util.Base64;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
 * 说明:SHA-1加密
 * 作者:汤观林
 * 日期:2022年01月03日 22时
 */
public class DigestTest {
    public static void main(String[] args) throws NoSuchAlgorithmException {

        //原文
        String input = "aa";

        //创建消息摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
        //执行消息摘要算法
        byte[] bytes = messageDigest.digest(input.getBytes());

        //使用base64转码
        //byte[] encode = Base64.encode(bytes);
        StringBuffer sb = new StringBuffer();
        for (byte b : bytes) {
            //把密文转换成16进制 显示
            String s = Integer.toHexString(b & 0xff);
            if(s.length()==1){
                s = "0"+s;  //密文长度为1,高位补0
            }
            sb.append(s);
        }
        System.out.println(sb.toString());
    }
}

运行结果:

e0c9035898dd52fc65c41454cec9c4d2611bfb37

7.5 SHA-256

package com.tangguanlin.encrypt_decrypt;
import org.apache.xmlbeans.impl.util.Base64;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
 * 说明:SHA-256加密
 * 作者:汤观林
 * 日期:2022年01月03日 22时
 */
public class DigestTest {
    public static void main(String[] args) throws NoSuchAlgorithmException {

        //原文
        String input = "aa";

        //创建消息摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
        //执行消息摘要算法
        byte[] bytes = messageDigest.digest(input.getBytes());

        //使用base64转码
        //byte[] encode = Base64.encode(bytes);
        StringBuffer sb = new StringBuffer();
        for (byte b : bytes) {
            //把密文转换成16进制 显示
            String s = Integer.toHexString(b & 0xff);
            if(s.length()==1){
                s = "0"+s;  //密文长度为1,高位补0
            }
            sb.append(s);
        }
        System.out.println(sb.toString());
    }
}

运行结果:

961b6dd3ede3cb8ecbaacbd68de040cd78eb2ed5889130cceb4c49268ea4d506

7.6 SHA-512

package com.tangguanlin.encrypt_decrypt;
import org.apache.xmlbeans.impl.util.Base64;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
 * 说明:SHA-512加密
 * 作者:汤观林
 * 日期:2022年01月03日 22时
 */
public class DigestTest {
    public static void main(String[] args) throws NoSuchAlgorithmException {

        //原文
        String input = "aa";

        //创建消息摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-512");
        //执行消息摘要算法
        byte[] bytes = messageDigest.digest(input.getBytes());

        //使用base64转码
        //byte[] encode = Base64.encode(bytes);
        StringBuffer sb = new StringBuffer();
        for (byte b : bytes) {
            //把密文转换成16进制 显示
            String s = Integer.toHexString(b & 0xff);
            if(s.length()==1){
                s = "0"+s;  //密文长度为1,高位补0
            }
            sb.append(s);
        }
        System.out.println(sb.toString());
    }
}

运行结果:

f6c5600ed1dbdcfdf829081f5417dccbbd2b9288e0b427e65c8cf67e274b69009cd142475e15304f599f429f260a661b5df4de26746459a3cef7f32006e5d1c1

8 RSA加密解密

8.1 非对称加密

市面上最流行的加密方式。

1.非对称加密算法,又叫做现代加密算法。

2.非对称加密算法,必须要有两个秘钥,一个公钥,一个私钥。

3.公钥和私钥是一对。公钥可以给任何人,私钥总是留给自己。

4.如果使用公钥加密,必须使用私钥解密

5.如果使用私钥加密,必须使用公钥解密

常见的非对称加密算法:目前市面最流行的2种算法

1.RSA

2.ECC

8.2 RSA加密解密

package com.tangguanlin.encrypt_decrypt;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.io.FileUtils;
import javax.crypto.Cipher;
import java.io.File;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
 * 说明:RSA加密解密算法
 * 作者:汤观林
 * 日期:2022年01月08日 00时
 */
public class RSATest {
    public static void main(String[] args) throws Exception {

        //原文
        String input ="硅谷";
        System.out.println("原文为:"+input);
        String privPath  = "a.priv";
        String pubPath = "a.pub";

        //算法
        String algorithm = "RSA";

        //创建密钥对生成器
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        //生成密钥对
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        //获取公钥
        PublicKey publicKey = keyPair.getPublic();
        //获取私钥
        PrivateKey privateKey = keyPair.getPrivate();

        //1.先将私钥 公钥保存到项目文件中
        saveKeyToFile(algorithm,privateKey,publicKey,privPath,pubPath);

        //2.从文件中读出私钥
        PrivateKey privateKey1 = readPrivateKey(algorithm, privPath);

        //3.从文件中读出公钥
        PublicKey publicKey1 = readPublicKey(algorithm, pubPath);

        //4.用私钥加密
        String encryptRSAStr = encryptRSA(algorithm, input, privateKey1);

        //5.用公钥解密
        String decryptRSA = decryptRSA(algorithm, encryptRSAStr, publicKey1);
        System.out.println("加密后为:"+decryptRSA);
    }


    //1.保存私钥、公钥到文件
    public static  void saveKeyToFile(String algorithm,PrivateKey privateKey,PublicKey publicKey,String privatePath,String publicPath) throws Exception {

        //获取私钥的字节数组
        byte[] privateKeyEncoded = privateKey.getEncoded();
        //获取公钥的字节数组
        byte[] publicKeyEncoded = publicKey.getEncoded();

        //使用base64进行编码
        String privateEncodeStr = Base64.encode(privateKeyEncoded);
        String publicEncodeStr = Base64.encode(publicKeyEncoded);

        FileUtils.writeStringToFile(new File(privatePath),privateEncodeStr, Charset.forName("UTF-8"));
        FileUtils.writeStringToFile(new File(publicPath),publicEncodeStr,Charset.forName("UTF-8"));

        //System.out.println("私钥:"+privateEncodeStr);
       //System.out.println("公钥:"+publicEncodeStr);
    }


    //2.从文件中读取私钥
    public static PrivateKey readPrivateKey(String algorithm,String privatePath) throws Exception {
        String privateKeyStr = FileUtils.readFileToString(new File(privatePath), Charset.defaultCharset());

        //生成私钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyStr));
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

        return privateKey;
    }

    //3.从文件总能读取公钥
    public static PublicKey readPublicKey(String algorithm,String publicPath) throws Exception {
        String publicKeyStr = FileUtils.readFileToString(new File(publicPath), Charset.defaultCharset());

        //生成公钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        //创建公钥规则
        X509EncodedKeySpec keySpec =  new X509EncodedKeySpec(Base64.decode(publicKeyStr));
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }


    //4.用私钥加密
    public static String encryptRSA(String algorithm,String input,PrivateKey privateKey) throws Exception {

        //创建加密对象
        Cipher cipher = Cipher.getInstance(algorithm);

        /**
         * 对加密对象进行初始化
         * 第1个参数:加密模式:加密还是解密
         * 第2个参数:你想使用公钥加密还是私钥加密
         *  我想使用私钥加密
         */
        cipher.init(Cipher.ENCRYPT_MODE,privateKey);

        //使用私钥进行加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        String privateEncryptStr = Base64.encode(bytes);
        return privateEncryptStr;
    }

    //5.用公钥解密
    public static String decryptRSA(String algorithm,String privateEncryptStr,PublicKey publicKey) throws Exception {
        //创建加密对象
        Cipher cipher = Cipher.getInstance(algorithm);

        cipher.init(Cipher.DECRYPT_MODE,publicKey);
        byte[] bytes1 = cipher.doFinal(Base64.decode(privateEncryptStr));
        String input = new String(bytes1);
        return input;
    }
}

运行结果:

a.priv

MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCWma9HWROIcryPv2Z/TFzv8bV7
sSXBMcFONimsFE7kBDZiZsxehTp62UKUKoRpuAVDWss5lWY/E9D2jXXGftJAsxr7Tw1ZjIjb0as1
BGKO6y9dQcs5HyBT0RSjK9aoj1gQYFLaeiExSZ4tHec22yZigYNaJWDwn7vIYtHbLXfAVr4c6neT
EhnAhGqXjQxrYnrlhQLNYgBR6QLOAM9sGnVOWhF95XevxWCTvde19N1waqP/D3KA37rcnBrKVanV
E5dQLsmaBiRt7d8iQo8iTYNuWa+IeN4TF0eq/QGU7jaP7LBYTNuG9RkjD/F5D7i5IzectlZJw6nZ
FmMivE65yDX5AgMBAAECggEBAIfo2g4BjWcuZI01pkJrn9PKxoWCMRhQ1qrGpoTtLuUICCx1qSoE
Aeqr9zHPzP+7WsSWQ6EL+uIh+AsJTqh7+zL4b/ksNlgCQ+BdL60A8BEanfwDTGuYc4wwu/clsOsd
sdPVmfAhF6NrVJxSyD7xN4reZrQL15hQDx+gTtMGDXQ9ZrKx3VaKh8oho/Elig68bPM3JZ/RYxPU
WSk9jq6KUvru+zxxmcx/jYWAH/H4WcAWUMC+OSYBCXXhqsMfeP/lLZX09p9vH8dIWmRx1znS1wNU
h+zjUJi3ccI4HCXSNGFgWBNJT7Zh1xkh7Fk7U7RgHU9+RPD4dRJ+rs/Qb7Br2oECgYEA+4SuKDRV
YcRze+Xhi3Io/8GuUBN4BhyLnTCXzv9uLq7mCv4E8LdZf+nKHZ/UvAbvc44mdI2G7f7ekoNEQ4zY
q+BzIdcSP6vBx6u5mbbU8U5Apomr/sG4r6XujMG3YI+wh165MxVOFv+3jvqQrf8C9ja4HdG+WHsS
JpgfhlC+hn0CgYEAmUio1P7JDaHsyi9KoyHZVuvc9knmFiouxFTEfMnIEzaO+TATIBnL2xoEL5pD
H2bZaBdeREKrLljK3G+bXybSmS7UkTM5bcBNQeU4ZkoDE5re/FDIqJ+yRwTo7WgMuDS1qUDnM2ej
awLt/F1KP5qz1HZkEzaNPRj0D1MbqNxvei0CgYEAyw/ox37Q/Djt/7at7LZDUZFeGTpOqah8M3Fa
7v0cSMZd5bwF4w40TOhEjLxQtWloA0Rd2bW1nQorSeB+yYa6G7H/Ye/QxOT3cyw6kVah/xvtaEc4
PFrMr8romG2Bjo0TuYcJQKvPuxPDb8Tt7/w1uPCenzyKT8c09UHWGcuwtsUCgYEAkNhZOdR9VlWB
9ViU9exhfUKqbU1Nj/pScRINDkJ6vrUai5yDJh1aRnOiKv1hmkXNARC78lLxgX1ETONl9ZcxALGB
CqhWB0DlgHRDRF2B5L+/ILyRsM4fX/7iwJxHELY2R3beQvQXN2LwUj4xkEL21bj+Pi2Zb3eqAOUu
yPVsng0CgYBvmVfkoruPvCMU90M/77thYZ2c+JMTT6IHy9x+ME+wDe59aZFq7vM82red5CN8VfOe
pq+kg4cJNgxusJTJFYli5dOy8zMbXoOnejZhy7aEq6o0GWnfN7TTVswDMU4ZtIeBZCpyjVkHvdqZ
qsfG7IbXldG8oT9sEmwAFv0oXA+pLg==

a.pub

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlpmvR1kTiHK8j79mf0xc7/G1e7ElwTHB
TjYprBRO5AQ2YmbMXoU6etlClCqEabgFQ1rLOZVmPxPQ9o11xn7SQLMa+08NWYyI29GrNQRijusv
XUHLOR8gU9EUoyvWqI9YEGBS2nohMUmeLR3nNtsmYoGDWiVg8J+7yGLR2y13wFa+HOp3kxIZwIRq
l40Ma2J65YUCzWIAUekCzgDPbBp1TloRfeV3r8Vgk73XtfTdcGqj/w9ygN+63JwaylWp1ROXUC7J
mgYkbe3fIkKPIk2DblmviHjeExdHqv0BlO42j+ywWEzbhvUZIw/xeQ+4uSM3nLZWScOp2RZjIrxO
ucg1+QIDAQAB

控制台:

原文为:硅谷
加密后为:硅谷

9 数字签名

9.1 数字签名介绍

数字签名:公钥数字签名,只有信息的发送者,才能产生别人无法伪造的一段数字串

类似于写在纸上面的普通物理签名。文青的离职证明,就是用的数字签名。

发送方A:原始内容input—>通过摘要算法获取原始内容的摘要str1 —>把str1用发送方的私钥加密----->数字签名

这个过程的算法名称叫 “sha256withrsa”。

接收方B:用发送方的公钥验证签名并解密,---->得到原文input ---->对input进行摘要算法str2

—>比较str1==str2(保证未被篡改)

注意:不是用公钥加密hash,如果用公钥,被人没有你的私钥,怎么验证呢?

因为签名是先进行摘要在进行rsa,所以在摘要算法一定的情况下,签名后得到的字符串长度总是一样的。

目的:验证发送方就是发送方,校验完整性

9.2 数字签名原理

为了理解得清楚,我们通过案例一步一步来讲解。

话说张三有两好哥们A、B,由于工作原因,张三和AB写邮件的时候,为了安全需要加密。于是张三想到了数字签名:

整个思路是这个样子的:

第一步:加密采用非对称加密,张三有三把钥匙,两把公钥,一把私钥留给自己。

第二步:A或者B写邮件给张三:A先用公钥对邮件加密,然后张三收到邮件之后使用私钥解密。

第三步:张三写邮件给A或者B:

(1) 张三写完邮件,先用hash函数生成邮件的摘要,附在文章上面,这就完成了数字签名,然后张三再使用私钥加密。就可以把邮件发出去了。

(2)A或者B收到邮件之后,先把数字签名取下来,然后使用自己的公钥解密即可。这时候取下来的数字签名中的摘要如果和张三的一致,那就认为是张三发来的,再对信件本身使用hash函数,将得到的结果,与上一步得到的摘要进行对比。如果两者一致,就证明这封信未被修改过。

上面的流程我们使用一张图来演示一下:

首先把公钥送给朋友A和B:

java 加密 解密 java密码加密和解密_码表_08

张三给A或B发送邮件:

java 加密 解密 java密码加密和解密_字符串_09

9.3 获取数字签名

package com.tangguanlin.encrypt_decrypt;
import org.apache.xmlbeans.impl.util.Base64;
import java.security.*;
/**
 * 说明:获取数字签名
 * 作者:汤观林
 * 日期:2022年01月08日 14时
 */
public class SignatureTest {
    public static void main(String[] args) throws  Exception{

        //原文
        String  input = "123";

        //算法
        String algorithm = "sha256withrsa";

        //获取私钥  加密用私钥
        PrivateKey privateKey = RSATest.readPrivateKey("RSA","a.priv");

        //获取签名对象
        Signature signature = Signature.getInstance(algorithm);

        //初始化签名
        signature.initSign(privateKey);

        //传入原文
        signature.update(input.getBytes());

        //开始签名
        byte[] sign = signature.sign();
        //使用Base64编码
        byte[] signEncode = Base64.encode(sign);

        System.out.println(new String(signEncode));
    }

运行结果:

D3swXYPuOLj2pJ3zdPyIUPPb/nUTdMq72gqGWqULQxeqGzvfZ8MgboXrbpq6iRIjQ4ztSgcolxOAiazT352YNhewDC+Hl18rUu/+bUK6JWiGRYH9dgudpV+2Fhda4yQr4Hv4dQ5HydZbzxg7ZCS6sFthK5OtNHoszFPdvPCAOT+CxtEPW/AUaw7L2esiTsjZ7x732aU/bf30tpr11V5h4LMnkmzFkOn0Vr2+I/uMJrp9Sq8z7axANt6ndWaVIPYvJfndw5TW76EOmxfGcO0o7xfkw8l9MCYLvxl+NQOfo3GB8ly2ooHt46RdJRtsx1U/8+rR+VCBGqgKJ92NIdxAMQ==

9.4 校验数字签名

package com.tangguanlin.encrypt_decrypt;
import org.apache.xmlbeans.impl.util.Base64;
import java.security.*;
/**
 * 说明:校验数字签名
 * 作者:汤观林
 * 日期:2022年01月08日 14时
 */
public class SignatureTest {
    public static void main(String[] args) throws  Exception{

        //原文
        String  input = "123";

        //算法
        String algorithm = "sha256withrsa";
        
        //获取公钥  公钥解密
        PublicKey publicKey = RSATest.readPublicKey("RSA","a.pub");

   //获取数字签名
  String signatureStr = "D3swXYPuOLj2pJ3zdPyIUPPb/nUTdMq72gqGWqULQxeqGzvfZ8MgboXrbpq6iRIjQ4ztSgcolxOAiazT352YNhewDC+Hl18rUu/+bUK6JWiGRYH9dgudpV+2Fhda4yQr4Hv4dQ5HydZbzxg7ZCS6sFthK5OtNHoszFPdvPCAOT+CxtEPW/AUaw7L2esiTsjZ7x732aU/bf30tpr11V5h4LMnkmzFkOn0Vr2+I/uMJrp9Sq8z7axANt6ndWaVIPYvJfndw5TW76EOmxfGcO0o7xfkw8l9MCYLvxl+NQOfo3GB8ly2ooHt46RdJRtsx1U/8+rR+VCBGqgKJ92NIdxAMQ==";

       //获取签名对象
        Signature signature = Signature.getInstance(algorithm);

        //初始化校验
        signature.initVerify(publicKey);

        //传入原文
        signature.update(input.getBytes());

        //校验签名
        boolean verify = signature.verify(Base64.decode(signatureStr.getBytes()));

        return verify;
    }

运行结果:

true

9.5 数字信封

对称加密的密钥分发不安全--------->发送方用接收方的公钥加密之-------->接收方用私钥再解开

注意:为什么会有分发密钥呢?不是约定好了吗?

结合我们前面的讲解,密钥会随着每个零件的数据一起发送给接收方。

9.6 数字证书

9.6.1 数字证书简介

问:有了数字签名就可以证明发送者的身份了,还有什么问题?

答:B这里存储的是A的公钥被换成了C的公钥,A发送的信息也换成了C的私钥生成的签名。

现在需要一种手段,来证明“B这里存储了A的公钥就是A的公钥。”

java 加密 解密 java密码加密和解密_java 加密 解密_10

B去A的数字证书里拿A的公钥,然后再和存储的A的公钥进行对比,这样就证明了 “B这里存储了A的公钥就是A的公钥“。

9.6.2 CA认证中心

CA(Certificate Authority)认证中心,它是采用PKI(Public Key Infrastructure)公开密钥基础架构技术,专门提供网络身份认证服务,

CA可以是民间团体,也可以是政府机构。负责签发和管理数字证书,且具有权威性和公正性的第三方信任机构,它的作用就像我们现实生活中颁发证件的公司,如护照办理机构。国内的CA认证中心主要分为区域性CA认证中心和行业性CA认证中心。

CA负责数字证书的批审、发放、归档、撤销等功能,CA颁发的数字证书拥有CA的数字签名,

所以除了CA自身,其他机构无法不被察觉的改动。

A和B要进行通信,必须相互获取对方的数字证书,A和B的数字证书可以由不同CA颁发。

9.6.3 代码实现

package com.tangguanlin.encrypt_decrypt;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import java.io.FileInputStream;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
/**
 * 说明:数字证书
 * 作者:汤观林
 * 日期:2022年01月09日 00时
 */
public class NumberCertificateTest {
    public static void main(String[] args) throws Exception {

        CertificateFactory cf = CertificateFactory.getInstance("x.509");

        FileInputStream in = new FileInputStream("xinbiyou_tangguanlin.p12");

        //生成一个证书对象并使用从输入流inStream中读取的数据对它进行初始化
        Certificate c = cf.generateCertificate(in);

        //获取公钥
        PublicKey publicKey = c.getPublicKey();
        String key = Base64.encode(publicKey.getEncoded());

        //拿到公钥,和本地存储的A的公钥进行比较
        System.out.println(key);
    }
}

百度数字证书:

java 加密 解密 java密码加密和解密_字符串_11

java 加密 解密 java密码加密和解密_java_12

9.6.4 数字证书案例

网页加密:

我们看一个应用“数字证书”的实例:https协议。这个协议上要用于网页加密。

首先,客户端向服务器发出加密申请.

java 加密 解密 java密码加密和解密_System_13

服务器用自己的私钥加密网页以后,连同本身的数字证书,一起发给客户端。

java 加密 解密 java密码加密和解密_字符串_14

客户端(浏览器)的“证书管理器”,有“受信任的根证书发布机构”列表。客户端会根据这张列表,查看解开数字证书的公钥是否在列表之内。

java 加密 解密 java密码加密和解密_java_15

如果数字证书记载的网址,与你正在浏览的网址不一致,就说明这张证书可能被冒用,浏览器会发出警告。

java 加密 解密 java密码加密和解密_java 加密 解密_16

如果这张数字证书不是由受信任的机构颁发的,浏览器会发出另一种警告。

java 加密 解密 java密码加密和解密_System_17

如果数字证书是可靠的,客户端就可以使用证书中的服务器公钥,对信息进行解密,然后与服务器交换加密信息。

java 加密 解密 java密码加密和解密_java_18