资讯详情

JAVA密码学教程

第一章 密码学

1.1 密码学的基本概念

密码在我们的生活中起着重要的作用,那么密码来自哪里,为什么会产生呢?

密码学是网络安全、信息安全、区块链等产品的基础,常见的非对称加密、对称加密、散列函数等,都属于密码学范畴。

密码学有几千年的历史。从最初的替换法到今天的不对称加密算法,它经历了三个阶段:古典密码学、现代密码学和现代密码学。密码学不仅是数学家的智慧,也是当今网络空间安全的重要基础。

1.1.1 古典密码学

在古代战争中,使用隐藏信息来保护重要的通信信息是很常见的。例如,首先用化学药水在纸上写下需要保护的信息。药水干燥后,纸上看不到任何信息。在阅读纸上的信息之前,需要使用其他化学药水。

https://www.iqiyi.com/v_19rt6ab1hg.html 1分05秒

这些方法是为了保护重要的信息不被他人获取,但隐藏信息的方法更容易被他人识别。例如,如果哨兵的调查力度增加,就会发现其中的技巧,从而开发出难以破解的古典密码学。

① 替换法

替换方法很容易理解,即用不能直接阅读的密文信息用固定信息替换原文。 b 替换成 we 替换成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

同样,移位法也可以采用多表移位法。典型的多表案例是维尼吉亚密码(也翻译了维热纳尔密码),属于多表密码的一种形式。

在这里插入图片描述

③ 古典密码破解方式

虽然古典密码很简单,但它是密码史上使用时间最长的加密方法。直到发现概率论的数学方法,古典密码才被破解。

英语单词中字母的频率不同,e以12.702%的百分比最高,z 只占到0.074%,感兴趣的可以去百科查字母频率的详细统计数据。如果密文数量足够大,单表的替换法或移位法可以通过频率分析来破解。

虽然多表的替换或移位更困难,但如果数据足够大,它也可以被破解。以维尼吉亚密码算法为例,破解方法是找出密文中完全相同的字母串,猜测密钥长度,得到密钥长度,然后将同一组的密文放在一起,通过频率分析解决。

1.1.2 近代密码学

古典密码的安全性受到威胁,使用方便性低。在工业化时代,现代密码被广泛使用。

恩尼格玛机

恩尼格玛机是二战期间纳粹德国使用的加密机,后来被英国破译。参与破译的人有图灵,图灵被称为计算机科学之父和人工智能之父。

恩尼格玛机

恩尼格玛机使用的加密方法本质上是移位和替代的,但由于密码表种类繁多,难以破解,加密解密机械化,使用方便,可用于二战。

1.1.3 现代密码学

① 散列函数

散列函数,也可以看到杂凑函数、摘要函数或哈希函数,可以计算任何长度的信息,变成固定长度值,常见MD5SHA-1SHA256,多应用于文件验证和数字签名。

MD5 128位(16字节)的哈希值可以生成任何长度的原文

SHA-1.任何长度的原文都可以生成160个(20字节)的哈希值

② 对称密码

对称密码使用相同的加密密钥和解密钥。对称密码分为:序列密码(流量密码)和分组密码(块密码)。流量密码是对信息流中的每个元素(一个字母或一个比特)作为基本处理单元进行加密。块密码首先分为信息流块,然后分别加密每个块。

例如,原文1234567890,流加密先加密1,再加密2,再加密3……最后拼接成密文;块加密先分为1234块,5678块,90块XX(XX对于补位数字),将不同的块分别加密,最后拼接成密文。上述古典密码学加密方法均为流加密。

③ 非对称密码

对称密码的密钥安全非常重要。加密器和解密器需要提前协商密钥,并确保密钥的安全。一旦密钥泄露,即使算法是安全的,也不能保证原始信息的隐私。

在实际使用中,远程提前协商密钥不容易实现。即使协商良好,也很容易在远程传输过程中被他人获得。因此,此时非对称密钥突出了优势。

非对称密码有两个密钥,公钥(publickey)和私钥(privatekey),使用的密钥不同于加密和解密操作。用公钥加密原文后,需要用私钥解密;用私钥加密原文后(此时一般称为签名),需要用公钥解密(此时一般称为验签)。公钥可以公开,我们使用公钥加密信息,然后发送给私钥持有人,私钥持有人使用私钥解密信息,获取原始信息。因为私钥只有一个人持有,不用担心被别人解密获取原始信息。

1.1.4 如何安全地设置密码

  • 密码不要太常见,不要使用类似123456的常用密码。
  • 不同的应用软件密码建议避免应用数据库脱库,所有应用密码都崩溃,
  • 在设置密码时,可以增加注册时间、注册地点和应用特性。tianjin123456表示在天津注册的应用。

1.2 ASCII编码

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是一个基于拉丁字母的计算机编码系统,主要用于显示现代英语和其他西欧语言。它是当今最常单字节编码系统,相当于国际标准ISO/IEC 646。

示例代码

创建maven项目 encrypt-decrypt

添加pom文件

<dependencies>         <dependency>             <groupId>commons-io</groupId>             <artifactId>commons-io</artifactId>             <version>2.6</version>         </dependency>     </dependencies> 

创建类 com.atguigu.ascii.AsciiDemo

字符转换成ascii码

package com.atguigu.ascii;  /**  * AsciiDemo  *  * @Author: 尚硅谷  * @CreateTime: 2020-03-17  * @Description:  */ public class AsciiDemo {      public static void main(String[] args) {         char a = 'A';         int b = a;         // 打印ascii码         System.out.println(b);     } } 

运行程序

字符串转换成ascii码

package com.atguigu.ascii;  /**  * AsciiDemo  *  * @Author: 尚硅谷  * @CreateTime: 2020-03-17  * @Description:  */ public class AsciiDemo {      public static void main(String[] args) { //        char a = 'A'; //        int b = a; //        Systm.out.println(b);
        String a = "AaZ";
        // 获取ascii码,需要把字符串转成字符
        char[] chars = a.toCharArray();
        for (char c : chars) {

            int asciiCode = c;
            System.out.println(asciiCode);
        }
    }
}

运行程序

1.3 恺撒加密

1.3.1 中国古代加密

看一个小故事 , 看看古人如何加密和解密:

公元683年,唐中宗即位。随后,武则天废唐中宗,立第四子李旦为皇帝,但朝政大事均由她自己专断。

裴炎、徐敬业和骆宾王等人对此非常不满。徐敬业聚兵十万,在江苏扬州起兵。裴炎做内应,欲以拆字手段为其传递秘密信息。后因有人告密,裴炎被捕,未发出的密信落到武则天手中。这封密信上只有“青鹅”二字,群臣对此大惑不解。

武则天破解了“青鹅”的秘密:“青”字拆开来就是“十二月”,而“鹅”字拆开来就是“我自与”。密信的意思是让徐敬业、骆宾王等率兵于十二月进发,裴炎在内部接应。“青鹅”破译后,裴炎被杀。接着,武则天派兵击败了徐敬业和骆宾王。

1.3.2 外国加密

在密码学中,恺撒密码是一种最简单且最广为人知的加密技术。

凯撒密码最早由古罗马军事统帅盖乌斯·尤利乌斯·凯撒在军队中用来传递加密信息,故称凯撒密码。这是一种位移加密方式,只对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,以此类推。

这个加密方法是以恺撒的名字命名的,当年恺撒曾用此方法与其将军们进行联系。

恺撒密码通常被作为其他更复杂的加密方法中的一个步骤。

1.3.3 凯撒位移加密

创建类 KaiserDemo,把 hello world 往右边移动3位

package com.atguigu.kaiser;

/**
 * KaiserDemo
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-03-17
 * @Description:
 */
public class KaiserDemo {
    public static void main(String[] args) {
        String input = "Hello world";
        // 往右边移动3位
        int key = 3;
        // 用来拼接
        StringBuilder sb = new StringBuilder();
        // 字符串转换成字节数组
        char[] chars = input.toCharArray();
        for (char c : chars) {
            int asciiCode = c;
            // 移动3位
            asciiCode = asciiCode + key;
            char newChar = (char) asciiCode;
            sb.append(newChar);
        }

        System.out.println(sb.toString());
    }
}

运行

1.3.4 凯撒加密和解密

package com.atguigu.kaiser;

/**
 * KaiserDemo
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-03-16
 * @Description:
 */
public class KaiserDemo {
    public static void main(String[] args) {
        String orignal = "Hello world";
        // 往右边偏移三位
        int key = 3;
        // 选中我即将抽取的代码,按快捷键Ctrl + Alt + M 
        String encryptKaiser =  encryptKaiser(orignal,key);
        System.out.println("加密:" + encryptKaiser);
        String decryptKaiser =  decryptKaiser(encryptKaiser,key);
        System.out.println("解密:" + decryptKaiser);
    }
    /**
     * 使用凯撒加密方式解密数据
     *
     * @param encryptedData :密文
     * @param key           :密钥
     * @return : 源数据
     */
    public static String decryptKaiser(String encryptedData, int key) {
        // 将字符串转为字符数组
        char[] chars = encryptedData.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char aChar : chars) {
            // 获取字符的ASCII编码
            int asciiCode = aChar;
            // 偏移数据
            asciiCode -= key;
            // 将偏移后的数据转为字符
            char result = (char) asciiCode;
            // 拼接数据
            sb.append(result);
        }
        return sb.toString();
    }
    /**
     * 使用凯撒加密方式加密数据
     *
     * @param orignal :原文
     * @param key     :密钥
     * @return :加密后的数据
     */
    public static String encryptKaiser(String orignal, int key) {
        // 将字符串转为字符数组
        char[] chars = orignal.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char aChar : chars) {
            // 获取字符的ascii编码
            int asciiCode = aChar;
            // 偏移数据
            asciiCode += key;
            // 将偏移后的数据转为字符
            char result = (char) asciiCode;
            // 拼接数据
            sb.append(result);
        }
        return sb.toString();
    }
}

1.4 频度分析法破解恺撒加密

公元前5世纪的时候,斯巴达人利用一根木棒,缠绕上皮革或者羊皮纸,在上面横向写下信息,解下这条皮带。展开来看,这长串字母没有任何意义。

比如这样:

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

比如这样:

然后收件人将这条皮带缠绕在相同的木棒上,就能恢复信息了。

前404年,一位遍体鳞伤的信差来到斯巴达将领利桑德面前,这趟波斯之旅只有他和四位同伴幸存,利桑德接下腰带,缠绕到他的密码棒上,得知波斯的发那巴祖斯准备侵袭他,多亏密码棒利桑德才能够预先防范,击退敌军。

密码棒是不是太简单了些?

加密者选择将组成信息的字母替代成别的字母,比如说将a写成1,这样就不能被解密者直接拿到信息了。

这难不倒解密者,以英文字母为例,为了确定每个英文字母的出现频率,分析一篇或者数篇普通的英文文章,英文字母出现频率最高的是e,接下来是t,然后是a……,然后检查要破解的密文,也将每个字母出现的频率整理出来,假设密文中出现频率最高的字母是j,那么就可能是e的替身,如果密码文中出现频率次高的但是P,那么可能是t的替身,以此类推便就能解开加密信息的内容。这就是频率分析法。

  • 将明文字母的出现频率与密文字母的频率相比较的过程
  • 通过分析每个符号出现的频率而轻易地破译代换式密码
  • 在每种语言中,冗长的文章中的字母表现出一种可对之进行分辨的频率。
  • e是英语中最常用的字母,其出现频率为八分之一

拷贝资料里面的 Util.javaFrequencyAnalysis.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 ,所以我们在去看第二个解密文件,发现我们可以读懂,解密成功

1.5 Byte和bit

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

bit : 比特, 又叫位. 一个位要么是0要么是1. 数据传输的单位 , 比如家里的宽带100MB,下载速度并没有达到100MB,一般都是12-13MB,那么是因为需要使用 100 / 8

关系: 1Byte = 8bit

1.5.1 获取字符串byte

package com.atguigu.bytebit;

/**
 * ByteBit
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-03-17
 * @Description:
 */
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);
        }
    }
}

运行程序

1.5.2 byte对应bit

package com.atguigu.bytebit;

/**
 * ByteBit
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-03-17
 * @Description:
 */
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获取对应的bit
            String s = Integer.toBinaryString(c);
            System.out.println(s);
        }
    }
}

运行程序

打印出来应该是8个bit,但前面是0,没有打印 ,从打印结果可以看出来,一个英文字符 ,占一个字节

1.5.3 中文对应的字节

// 中文在GBK编码下, 占据2个字节
// 中文在UTF-8编码下, 占据3个字节
package com.atguigu;

/**
 * ByteBitDemo
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-03-16
 * @Description:
 */
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);
        }
    }

运行程序

1.5.4 英文对应的字节

我们在看看英文,在不同的编码格式占用多少字节

package com.atguigu.bytebit;

/**
 * ByteBit
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-04-12
 * @Description:
 */
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);
        }
    }
}

运行程序

1.6 常见加密方式

1.6.1 对称加密

  • 采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
  • 示例
    • 我们现在有一个原文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使用

1.6.2 DES加密

示例代码 des加密算法

Cipher :文档 https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html#getInstance-java.lang.String-

package com.atguigu.desaes;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

/**
 * DesAesDemo
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-03-17
 * @Description:
 */
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

运行程序

1.6.4 DES解密

使用 ctrl + alt + m 快捷键抽取代码

package com.atguigu.desaes;

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是三个字节一组 ,如果当我们的位数不够的时候,会使用等号来补齐

1.6.4 base64补等号测试

package com.atguigu;

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()));
    }
}

运行:

1.6.5 AES加密解密

AES 加密解密和 DES 加密解密代码一样,只需要修改加密算法就行,拷贝 ESC 代码

package com.atguigu.desaes;
import com.sun.org.apache.xml.internal.security.utils.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

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个字节

在运行程序

1.6.6 toString()与new String ()用法区别

举例子

package com.atguigu;


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()对象打印的时候使用

1.7 加密模式

加密模式:https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html

ECB

ECB : Electronic codebook, 电子密码本. 需要加密的消息按照块密码的块大小被分为数个块,并对每个块进行独立加密

  • 优点 : 可以并行处理数据
  • 缺点 : 同样的原文生成同样的密文, 不能很好的保护数据
  • 同时加密,原文是一样的,加密出来的密文也是一样的

CBC

CBC : Cipher-block chaining, 密码块链接. 每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块

  • 优点 : 同样的原文生成的密文不一样
  • 缺点 : 串行处理数据.

1.8 填充模式

  • 当需要按块处理的数据, 数据长度不符合块处理需求时, 按照一定的方法填充满块长的规则

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)

加密模式和填充模式例子

package com.atguigu.desaes;

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";
        // 指定获取Cipher的算法,如果没有指定加密模式和填充模式,ECB/PKCS5Padding就是默认值
        //     String transformation = "DES"; // 9PQXVUIhaaQ=
        //String transformation = "DES/ECB/PKCS5Padding"; // 9PQXVUIhaaQ=
        // CBC模式,必须指定初始向量,初始向量中密钥的长度必须是8个字节
        //String transformation = "DES/CBC/PKCS5Padding"; // 9PQXVUIhaaQ=
        // NoPadding模式,原文的长度必须是8个字节的整倍数 ,所以必须把 硅谷改成硅谷12
        String transformation = "DES/CBC/NoPadding"; // 9PQXVUIhaaQ=
        // 指定获取密钥的算法
        String algorithm = "DES";
        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: 解密模式
         // 初始向量,参数表示跟谁进行异或,初始向量的长度必须是8位
//        IvParameterSpec iv = new IvParameterSpec(key.getBytes());
         // 初始化加密模式和算法
        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);
//        IvParameterSpec iv = new IvParameterSpec(key.getBytes());
        cipher.init(Cipher.DECRYPT_MODE, sks);
        // 3. 解密
        byte[] bytes = cipher.doFinal(Base64.decode(input));

        return new String(bytes);
    }
}

运行程序:

修改成 CBC 加密 模式

 String transformation = "DES/CBC/PKCS5Padding";

运行 ,报错,需要添加一个参数

修改加密代码:

运行程序

修改填充模式

String transformation = "DES/CBC/NoPadding";

运行报错 NoPadding 这种填充模式 原文必须是8个字节的整倍数

修改运行

在测试 AES 的时候需要注意,key需要16个字节,加密向量也需要16个字节 ,其他方式跟 DES 一样

1.9 消息摘要

  • 消息摘要(Message Digest)又称为数字摘要(Digital Digest)
  • 它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生
  • 使用数字摘要生成的值是不可以篡改的,为了保证文件或者值的安全

1.9.1 特点

无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。例如应用MD5算法摘要的消息有128个比特位,用SHA-1算法摘要的消息最终有160比特位的输出

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

消息摘要是单向、不可逆的

常见算法 :

- MD5
- SHA1
- SHA256
- SHA512

在线获取消息摘要

百度搜索 tomcat ,进入官网下载 ,会经常发现有 sha1sha512 , 这些都是数字摘要

数字摘要

1.9.2 获取字符串消息摘要

package com.atguigu.digest;

import javax.sound.midi.Soundbank;
import java.security.MessageDigest;

/**
 * DigestDemo1
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-03-17
 * @Description:
 */
public class DigestDemo1 {

    public static void main(String[] args) throws Exception{
        // 原文
        String input = "aa";
        // 算法
        String algorithm = "MD5";
        // 获取数字摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 获取消息数字摘要的字节数组
        byte[] digest = messageDigest.digest(input.getBytes());
        System.out.println(new String(digest));
    }
}

运行

1.9.3 base64 编码

package com.atguigu.digest;

import com.sun.org.apache.xml.internal.security.utils.Base64;

import javax.sound.midi.Soundbank;
import java.security.MessageDigest;

/**
 * DigestDemo1
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-03-17
 * @Description:
 */
public class DigestDemo1 {

    public static void main(String[] args) throws Exception{
        // 原文
        String input = "aa";
        // 算法
        String algorithm = "MD5";
        // 获取数字摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 消息数字摘要
        byte[] digest = messageDigest.digest(input.getBytes());
//        System.out.println(new String(digest));
        // base64编码
        System.out.println(Base64.encode(digest));
    }
}

运行

使用在线 md5 加密 ,发现我们生成的值和代码生成的值不一样,那是因为消息摘要不是使用base64进行编码的,所以我们需要把值转成16进制

数字摘要转换成 16 进制

// 4124bc0a9335c27f086f24ba207a4912     md5 在线校验
// QSS8CpM1wn8IbyS6IHpJEg==             消息摘要使用的是16进制

代码转成16进制

package com.atguigu.digest;

import com.sun.org.apache.xml.internal.security.utils.Base64;

import javax.sound.midi.Soundbank;
import java.security.MessageDigest;

/**
 * DigestDemo1
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-03-17
 * @Description:
 */
public class DigestDemo1 {

    public static void main(String[] args) throws Exception{
        // 4124bc0a9335c27f086f24ba207a4912     md5 在线校验
        // QSS8CpM1wn8IbyS6IHpJEg==             消息摘要使用的是16进制
        // 原文
        String input = "aa";
        // 算法
        String algorithm = "MD5";
        // 获取数字摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 消息数字摘要
        byte[] digest = messageDigest.digest(input.getBytes());
//        System.out.println(new String(digest));
        // base64编码
//        System.out.println(Base64.encode(digest));
        // 创建对象用来拼接
        StringBuilder sb = new StringBuilder();

        for (byte b : digest) {
            // 转成 16进制
            String s = Integer.toHexString(b & 0xff);
            //System.out.println(s);
            if (s.length() == 1){
                // 如果生成的字符只有一个,前面补0
                s = "0"+s;
            }
            sb.append(s);
        }
        System.out.println(sb.toString());
        
    }
}

运行

1.9.4 其他数字摘要算法

package com.atguigu.digest;

import com.sun.org.apache.xml.internal.security.utils.Base64;

import javax.sound.midi.Soundbank;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * DigestDemo1
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-03-17
 * @Description:
 */
public class DigestDemo1 {

    public static void main(String[] args) throws Exception{
        // 4124bc0a9335c27f086f24ba207a4912     md5 在线校验
        // QSS8CpM1wn8IbyS6IHpJEg==             消息摘要使用的是16进制
        // 原文
        String input = "aa";
        // 算法
        String algorithm = "MD5";
        // 获取数字摘要对象
        String md5 = getDigest(input, "MD5");
        System.out.println(md5);

        String sha1 = getDigest(input, "SHA-1");
        System.out.println(sha1);

        String sha256 = getDigest(input, "SHA-256");
        System.out.println(sha256);

        String sha512 = getDigest(input, "SHA-512");
        System.out.println(sha512);


    }

    private static String toHex(byte[] digest) throws Exception {

//        System.out.println(new String(digest));
        // base64编码
//        System.out.println(Base64.encode(digest));
        // 创建对象用来拼接
        StringBuilder sb = new StringBuilder();

        for (byte b : digest) {
            // 转成 16进制
            String s = Integer.toHexString(b & 0xff);
            if (s.length() == 1){
                // 如果生成的字符只有一个,前面补0
                s = "0"+s;
            }
            sb.append(s);
        }
        System.out.println("16进制数据的长度:" + sb.toString().getBytes().length);
        return sb.toString();
    }

    private static String getDigest(String input, String algorithm) throws Exception {
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 消息数字摘要
        byte[] digest = messageDigest.digest(input.getBytes());
        System.out.println("密文的字节长度:" + digest.length);

        return toHex(digest);
    }
}

运行

1.9.5 获取文件消息摘要

package com.atguigu.digest;

import com.sun.org.apache.xml.internal.security.utils.Base64;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import sun.misc.BASE64Decoder;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.security.MessageDigest;

/**
 * DigestDemo
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-03-16
 * @Description:
 */
public class DigestDemo {

    public static void main(String[] args) throws Exception{
        String input = "aa";
        String algorithm = "MD5";

        // sha1 可以实现秒传功能

        String sha1 = getDigestFile("apache-tomcat-9.0.10-windows-x64.zip", "SHA-1");
        System.out.println(sha1);

        String sha512 = getDigestFile("apache-tomcat-9.0.10-windows-x64.zip", "SHA-512");
        System.out.println(sha512);

        String md5 = getDigest("aa", "MD5");
        System.out.println(md5);

        String md51 = getDigest("aa ", "MD5");
        System.out.println(md51);
    }

    private static String getDigestFile(String filePath, String algorithm) throws Exception{
        FileInputStream fis = new FileInputStream(filePath);
        int len;
        byte[] buffer = new byte[1024];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while ( (len =  fis.read(buffer))!=-1){
            baos.write(buffer,0,len);
        }
        // 获取消息摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 获取消息摘要
        byte[] digest = messageDigest.digest(baos.toByteArray());
        System.out.println("密文的字节长度:"+digest.length);
        return toHex(digest);
    }

    private static String getDigest(String input, String algorithm) throws Exception{
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        byte[] digest = messageDigest.digest(input.getBytes());
        System.out.println("密文的字节长度:"+digest.length);
        return toHex(digest);
    }

    private static String toHex(byte[] digest) {
        //        System.out.println(new String(digest));
        // 消息摘要进行表示的时候,是用16进制进行表示
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) {
            // 转成16进制

            String s = Integer.toHexString(b & 0xff);
            // 保持数据的完整性,前面不够的用0补齐
            if (s.length()==1){
                s="0"+s;
            }
            sb.append(s);
        }
        System.out.println("16进制数据的长度:"+ sb.toString().getBytes().length);
        return sb.toString();
    }
}

运行程序 ,获取 sha-1sha-512 的值

查看 tomcat 官网上面 sha-1sha-512 的值

使用 sha-1 算法,可以实现秒传功能,不管咱们如何修改文件的名字,最后得到的值是一样的

运行程序 ,获取 sha-1sha-512 的值

如果原文修改了,那么sha-1值 就会不一样

运行结果:

总结

  • MD5算法 : 摘要结果16个字节, 转16进制后32个字节
  • SHA1算法 : 摘要结果20个字节, 转16进制后40个字节
  • SHA256算法 : 摘要结果32个字节, 转16进制后64个字节
  • SHA512算法 : 摘要结果64个字节, 转16进制后128个字节

1.10 非对称加密

① 非对称加密算法又称现代加密算法

② 非对称加密是计算机通信安全的基石,保证了加密数据不会被破解

③ 与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)私有密(privatekey)

④ 公开密钥和私有密钥是一对

⑤ 如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密

⑥ 如果用私有密钥对数据进行加密,只有用对应的公开密钥才能解密

⑦ 因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法

  • 示例
    • 首先生成密钥对, 公钥为(5,14), 私钥为(11,14)
    • 现在A希望将原文2发送给B
    • A使用公钥加密数据. 2的5次方mod 14 = 4 , 将密文4发送给B
    • B使用私钥解密数据. 4的11次方mod14 = 2, 得到原文2
  • 特点
    • 加密和解密使用不同的密钥
    • 如果使用私钥加密, 只能使用公钥解密
    • 如果使用公钥加密, 只能使用私钥解密
    • 处理数据的速度较慢, 因为安全级别高
  • 常见算法
    • RSA
    • ECC

1.10.1 生成公钥和私钥

package com.atguigu.rsa;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.io.FileUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
/**
 * RSAdemo
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-04-12
 * @Description:
 */
public class RSAdemo {
    public static void main(String[] args) throws Exception {
      
        // 加密算法
        String algorithm = "RSA";
        //  创建密钥对生成器对象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 生成私钥
        PrivateKey privateKey = keyPair.getPrivate();
        // 生成公钥
        PublicKey publicKey = keyPair.getPublic();
        // 获取私钥字节数组
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 获取公钥字节数组
        byte[] publicKeyEncoded = publicKey.getEncoded();
        // 对公私钥进行base64编码
        String privateKeyString = Base64.encode(privateKeyEncoded);
        String publicKeyString = Base64.encode(publicKeyEncoded);
        // 打印私钥
        System.out.println(privateKeyString);
        // 打印公钥
        System.out.println(publicKeyString);
    }
}

运行程序:先打印的是私钥 , 后面打印的是公钥

1.10.2 私钥加密

package com.atguigu.rsa;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.io.FileUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
/**
 * RSAdemo
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-04-12
 * @Description:
 */
public class RSAdemo {
    public static void main(String[] args) throws Exception {
        String input = "硅谷";
        // 加密算法
        String algorithm = "RSA";
        //  创建密钥对生成器对象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 生成私钥
        PrivateKey privateKey = keyPair.getPrivate();
        // 生成公钥
        PublicKey publicKey = keyPair.getPublic();
        // 获取私钥字节数组
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 获取公钥字节数组
        byte[] publicKeyEncoded = publicKey.getEncoded();
        // 对公私钥进行base64编码
        String privateKeyString = Base64.encode(privateKeyEncoded);
        String publicKeyString = Base64.encode(publicKeyEncoded);


        // 创建加密对象
        // 参数表示加密算法
        Cipher cipher = Cipher.getInstance(algorithm);
        // 初始化加密
        // 第一个参数:加密的模式
        // 第二个参数:使用私钥进行加密
        cipher.init(Cipher.ENCRYPT_MODE,privateKey);
        // 私钥加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        System.out.println(Base64.encode(bytes));
       
    }
}

运行程序

1.10.3 私钥加密私钥解密

package com.atguigu.rsa;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.io.FileUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
/**
 * RSAdemo
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-04-12
 * @Description:
 */
public class RSAdemo {
    public static void main(String[] args) throws Exception {
        String input = "硅谷";
        // 加密算法
        String algorithm = "RSA";
        //  创建密钥对生成器对象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 生成私钥
        PrivateKey privateKey = keyPair.getPrivate();
        // 生成公钥
        PublicKey publicKey = keyPair.getPublic();
        // 获取私钥字节数组
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 获取公钥字节数组
        byte[] publicKeyEncoded = publicKey.getEncoded();
        // 对公私钥进行base64编码
        String privateKeyString = Base64.encode(privateKeyEncoded);
        String publicKeyString = Base64.encode(publicKeyEncoded);


        // 创建加密对象
        // 参数表示加密算法
        Cipher cipher = Cipher.getInstance(algorithm);
        // 初始化加密
        // 第一个参数:加密的模式
        // 第二个参数:使用私钥进行加密
        cipher.init(Cipher.ENCRYPT_MODE,privateKey);
        // 私钥加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        System.out.println(Base64.encode(bytes));
        // 私钥进行解密
        cipher.init(Cipher.DECRYPT_MODE,publicKey);
        // 对密文进行解密,不需要使用base64,因为原文不会乱码
        byte[] bytes1 = cipher.doFinal(bytes);
        System.out.println(new String(bytes1));

    }
}

运行程序 ,因为私钥加密,只能公钥解密

1.10.4 私钥加密公钥解密

package com.atguigu.rsa;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.io.FileUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
/**
 * RSAdemo
 *
 * @Author: 尚硅谷
 * @CreateTime: 2020-04-12
 * @Description:
 */
public class RSAdemo {
    public static void main(String[] args) throws Exception {
        String input = "硅谷";
        // 加密算法
        String algorithm = "RSA";
        //  创建密钥对生成器对象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 生成私钥
        PrivateKey privateKey = keyPair.getPrivate();
        // 生成公钥
        PublicKey publicKey = keyPair.getPublic();
        // 获取私钥字节数组
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 获取公钥字节数组
        byte[] publicKeyEncoded = publicKey.getEncoded();
        // 对公私钥进行base64编码
        String privateKeyString = Base64.encode(privateKeyEncoded);
        String publicKeyString = Base64.encode(publicKeyEncoded);


        // 创建加密对象
        // 参数表示加密算法
        Cipher cipher = Cipher.getInstance(algorithm);
        // 初始化加密
        // 第一个参数:加密的模式
        // 第二个参数:使用私钥进行加密
        cipher.init(Cipher.ENCRYPT_MODE,privateKey);
        // 私钥加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        System.out.println(Base64.encode(bytes));
      

标签: 接近开关移位传感器vlg10

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

 锐单商城 - 一站式电子元器件采购平台  

 深圳锐单电子有限公司