一、常见加密方式

1、对称加密

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

(1)示例

  • 我们现在有一个原文3要发送给B
  • 设置密钥为108, 3 * 108 = 324, 将324作为密文发送给B
  • B拿到密文324后, 使用324/108 = 3 得到原文

(2)常见加密算法

  DES

  Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。

  AES

  Advanced Encryption Standard, 高级加密标准 .在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。

(3)特点

  • 加密速度快, 可以加密大文件
  • 密文可逆, 一旦密钥文件泄漏, 就会导致数据暴露
  • 加密后编码表找不到对应字符, 出现乱码
  • 一般结合Base64使用

二、DES 加解密

示例代码 des加密算法

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

使用 DES 进行加密:

public class DESDemo {
    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 包

public class DESDemo {
    public static void main(String[] args) throws Exception {
        // 原文
        String input = "硅谷";
        // 定义密钥,des加密必须是8位
        String key = "12345678";
        // 定义加密算法
        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));
        
        //对数据进行 Base64 编码
        String encode = Base64.encode(bytes);
        System.out.println("Base64 编码为:" + encode);
    }
}

再次运行:

使用 DES 解密

public class DESDemo {
    // DES加密算法,key的大小必须是8个字节
    public static void main(String[] args) throws Exception {
        // 原文
        String input = "硅谷";
        // 定义密钥,des加密必须是8位
        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);
    }

    /**
     *
     * 使用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, BadPaddingException {
        // 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));

        //对数据进行 Base64 编码
        String encode = Base64.encode(bytes);

        return encode;
    }


    /**
     * 使用DES解密
     *
     * @param input             密文
     * @param key               密钥
     * @param transformation    获取Cipher对象的算法
     * @param algorithm         获取密钥的算法
     * @return                  原文
     * @throws Exception
     */

    private static String decryptDES(String input, String key, String transformation, String algorithm) throws Exception {
        //1.获取 Cipher 对象
        Cipher cipher = Cipher.getInstance(transformation);
        //2.指定密钥规则
        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

1、Base64 算法简介

  • Base64是网络上最常见的用于传输8Bit字节码的可读性编码算法之一
  • 可读性编码算法不是为了保护数据的安全性,而是为了可读性
  • 可读性编码不改变信息内容,只改变信息内容的表现形式
  • 所谓Base64,即是说在编码过程中使用了64种字符:大写A到Z、小写a到z、数字0到9、“+”和“/”
  • Base58是Bitcoin(比特币)中使用的一种编码方式,主要用于产生Bitcoin的钱包地址 相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"i",以及"+"和"/"符号

2、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 = 64

3、Base64 构成原则

① 小写 a - z = 26个字母

② 大写 A - Z = 26个字母

③ 数字 0 - 9 = 10 个数字

④ + / = 2个符号

大家可能发现一个问题,咱们的base64有个 = 号,但是在映射表里面没有发现 = 号 , 这个地方需要注意,等号非常特殊,因为base64是三个字节一组 ,如果当我们的位数不够的时候,会使用等号来补齐

4、Base64补等号测试

Demo:

public class Base64Demo {

    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” 说明:

字符:1

对应的ASCII:49

对应的二进制:00110001

转换前:00110001 00000000 00000000(不够3个字节的补0,补了几个字节最后的编码就有几个=号)

转换后:00001100 00010000 00000000 00000000(每一个前面加两个0)

换算成对应的10进制:12 16 0 0

在参考base64的码表得到最终的值为:MQ==

四、AES加解密

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

public class AESDemo {
    // AES加密算法,key的大小必须是8个字节
    public static void main(String[] args) throws Exception {
        // 原文
        String input = "硅谷";
        // 定义密钥,AES加密算法,比较高级,所以key的大小必须是16个字节
        String key = "12345678";

        //定义加密类型
        String transformation = "AES";

        // 指定获取密钥的算法
        String algorithm = "AES";


        String encryptDES = encryptAES(input, key, transformation, algorithm);
        System.out.println("加密:" + encryptDES);

        String decryptDES = decryptAES(encryptDES, key, transformation, algorithm);
        System.out.println("解密: " + decryptDES);
    }

    /**
     *
     * 使用AES加密数据
     *
     * @param input             原文
     * @param key               密钥(AES,密钥的长度必须是8个字节)
     * @param transformation    获取Cipher对象的算法
     * @param algorithm         获取密钥的算法
     * @return                  密文
     * @throws Exception
     */

    private static String encryptAES(String input, String key, String transformation, String algorithm) throws Exception, BadPaddingException {
        // 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));

        //对数据进行 Base64 编码
        String encode = Base64.encode(bytes);

        return encode;
    }


    /**
     * 使用AES解密
     *
     * @param input             密文
     * @param key               密钥
     * @param transformation    获取Cipher对象的算法
     * @param algorithm         获取密钥的算法
     * @return                  原文
     * @throws Exception
     */

    private static String decryptAES(String input, String key, String transformation, String algorithm) throws Exception {
        //1.获取 Cipher 对象
        Cipher cipher = Cipher.getInstance(transformation);
        //2.指定密钥规则
        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);
    }
}

运行程序:AES 加密的密钥key , 需要传入16个字节

再运行程序:

五、toString()与new String ()用法区别

举例:

public class StringDemo {
    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);
    }
}

运行结果:

哪一个是正确的?为什么?

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

六、加密模式

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

1、ECB

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

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

2、CBC

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

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

七、填充模式

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

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)

加密模式和填充模式例子

(1)使用 DES 方式,使用 ECB 加密方式

默认情况下ECB/PKCS5Padding就是默认值
public class DESPaddingDemo {

    // 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"; 密文:qANksk5lvqM=
        //String transformation = "DES/ECB/PKCS5Padding";   //qANksk5lvqM=

        String transformation = "DES/ECB/PKCS5Padding";


        // 指定获取密钥的算法
        String algorithm = "DES";

        String encryptDES = encryptDES(input, key, transformation, algorithm);
        System.out.println("DES加密:" + encryptDES);

        String decryptDES = decryptDES(encryptDES, key, transformation, algorithm);
        System.out.println("DES解密:" + decryptDES);

    }


    /**
     * 使用DES解密
     *
     * @param encryptDES        密文
     * @param key               密钥
     * @param transformation    获取Cipher对象的算法
     * @param algorithm         获取密钥的算法
     * @return                  原文
     */

    private static String decryptDES(String encryptDES, 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(encryptDES));

        return new String(bytes);
    }

    /**
     * 使用DES加密数据
     * @param input             原文
     * @param key               密钥(DES,密钥的长度必须是8个字节)
     * @param transformation    获取Cipher对象的算法
     * @param algorithm         获取密钥的算法
     * @return                  密文
     */

    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;
    }
}
使用ECB/NoPadding 的加密模式
public class DESPaddingDemo {

    // 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"; 密文:qANksk5lvqM=
        //String transformation = "DES/ECB/PKCS5Padding";   //qANksk5lvqM=

        String transformation = "DES/ECB/NoPadding";


        // 指定获取密钥的算法
        String algorithm = "DES";

        String encryptDES = encryptDES(input, key, transformation, algorithm);
        System.out.println("DES加密:" + encryptDES);

        String decryptDES = decryptDES(encryptDES, key, transformation, algorithm);
        System.out.println("DES解密:" + decryptDES);

    }


    /**
     * 使用DES解密
     *
     * @param encryptDES        密文
     * @param key               密钥
     * @param transformation    获取Cipher对象的算法
     * @param algorithm         获取密钥的算法
     * @return                  原文
     */

    private static String decryptDES(String encryptDES, 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(encryptDES));

        return new String(bytes);
    }

    /**
     * 使用DES加密数据
     * @param input             原文
     * @param key               密钥(DES,密钥的长度必须是8个字节)
     * @param transformation    获取Cipher对象的算法
     * @param algorithm         获取密钥的算法
     * @return                  密文
     */

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

使用 Nopadding 的情况,就需要原文本身是8个字节的倍数

修改一下原文为8个字节长度:

(2)使用 DES 方式,使用 CBC 加密方式

使用 CBC/PKCS5Padding 的加密方式
public class DESPaddingDemo {

    // DES加密算法,key的大小必须是8个字节
    public static void main(String[] args) throws Exception{
        String input = "硅谷12";

        //DES加密算法,key的大小必须是8个字节
        String key = "12345678";

        //指定获取Cipher的算法,如果没有指定加密模式和填充模式,ECB/PKCS5Padding就是默认值
        //String transformation = "DES"; 密文:qANksk5lvqM=
        //String transformation = "DES/ECB/PKCS5Padding";   //qANksk5lvqM=

        // NoPadding模式,原文的长度必须是8个字节的整倍数 ,所以必须把 硅谷改成硅谷12
        //String transformation = "DES/ECB/NoPadding";      //hPIdFgHHP3Y=

        // CBC模式,必须指定初始向量,初始向量中密钥的长度必须是8个字节
        //String transformation = "DES/CBC/PKCS5Padding";  //Y6htKI/ceJgxuMpKeTKQpA==

        String transformation = "DES/CBC/PKCS5Padding";


        // 指定获取密钥的算法
        String algorithm = "DES";

        String encryptDES = encryptDES(input, key, transformation, algorithm);
        System.out.println("DES加密:" + encryptDES);

        String decryptDES = decryptDES(encryptDES, key, transformation, algorithm);
        System.out.println("DES解密:" + decryptDES);

    }


    /**
     * 使用DES解密
     *
     * @param encryptDES        密文
     * @param key               密钥
     * @param transformation    获取Cipher对象的算法
     * @param algorithm         获取密钥的算法
     * @return                  原文
     */

    private static String decryptDES(String encryptDES, 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, iv);

        // 3. 解密
        byte[] bytes = cipher.doFinal(Base64.decode(encryptDES));

        return new String(bytes);
    }

    /**
     * 使用DES加密数据
     * @param input             原文
     * @param key               密钥(DES,密钥的长度必须是8个字节)
     * @param transformation    获取Cipher对象的算法
     * @param algorithm         获取密钥的算法
     * @return                  密文
     */

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

        // 初始向量,参数表示跟谁进行异或,初始向量的长度必须是8位
        IvParameterSpec iv = new IvParameterSpec(key.getBytes());

        // ENCRYPT_MODE:加密模式
        // DECRYPT_MODE: 解密模式
        // 初始化加密模式和算法
        cipher.init(Cipher.ENCRYPT_MODE,sks, iv);

        // 加密
        byte[] bytes = cipher.doFinal(input.getBytes());

        // 输出加密后的数据
        String encode = Base64.encode(bytes);

        return encode;
    }
}

CBC模式,必须指定初始向量,初始向量中密钥的长度必须是8个字节,否则将出以下错误

使用 CBC/NoPadding 的加密方式
public class DESPaddingDemo {

    // DES加密算法,key的大小必须是8个字节
    public static void main(String[] args) throws Exception{
        String input = "硅谷12";

        //DES加密算法,key的大小必须是8个字节
        String key = "12345678";

        //指定获取Cipher的算法,如果没有指定加密模式和填充模式,ECB/PKCS5Padding就是默认值
        //String transformation = "DES"; 密文:qANksk5lvqM=
        //String transformation = "DES/ECB/PKCS5Padding";   //qANksk5lvqM=

        // NoPadding模式,原文的长度必须是8个字节的整倍数 ,所以必须把 硅谷改成硅谷12
        //String transformation = "DES/ECB/NoPadding";      //hPIdFgHHP3Y=

        // CBC模式,必须指定初始向量,初始向量中密钥的长度必须是8个字节
        //String transformation = "DES/CBC/PKCS5Padding";  //Y6htKI/ceJgxuMpKeTKQpA==
        //String transformation = "DES/CBC/NoPadding";    //Y6htKI/ceJg=

        String transformation = "DES/CBC/NoPadding";


        // 指定获取密钥的算法
        String algorithm = "DES";

        String encryptDES = encryptDES(input, key, transformation, algorithm);
        System.out.println("DES加密:" + encryptDES);

        String decryptDES = decryptDES(encryptDES, key, transformation, algorithm);
        System.out.println("DES解密:" + decryptDES);

    }


    /**
     * 使用DES解密
     *
     * @param encryptDES        密文
     * @param key               密钥
     * @param transformation    获取Cipher对象的算法
     * @param algorithm         获取密钥的算法
     * @return                  原文
     */

    private static String decryptDES(String encryptDES, 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, iv);

        // 3. 解密
        byte[] bytes = cipher.doFinal(Base64.decode(encryptDES));

        return new String(bytes);
    }

    /**
     * 使用DES加密数据
     * @param input             原文
     * @param key               密钥(DES,密钥的长度必须是8个字节)
     * @param transformation    获取Cipher对象的算法
     * @param algorithm         获取密钥的算法
     * @return                  密文
     */

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

        // 初始向量,参数表示跟谁进行异或,初始向量的长度必须是8位
        IvParameterSpec iv = new IvParameterSpec(key.getBytes());

        // ENCRYPT_MODE:加密模式
        // DECRYPT_MODE: 解密模式
        // 初始化加密模式和算法
        cipher.init(Cipher.ENCRYPT_MODE,sks, iv);

        // 加密
        byte[] bytes = cipher.doFinal(input.getBytes());

        // 输出加密后的数据
        String encode = Base64.encode(bytes);

        return encode;
    }
}

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

八、非对称加密

1、简介

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

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

③ 与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(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

2、生成公钥和私钥

public class RSADemo1 {
    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();
        System.out.println("privateKey = " + privateKey);
        System.out.println("publicKey = " + publicKey);

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

        // 对公私钥进行base64编码
        String privateKeyString = Base64.encode(privateKeyEncoded);
        String publicKeyString = Base64.encode(publicKeyEncoded);

        // 打印私钥
        System.out.println("私钥为:");
        System.out.println(privateKeyString);
        // 打印公钥
        System.out.println("公钥为:");
        System.out.println(publicKeyString);
    }
}

运行程序:

3、RSA 的加解密

(1)私钥加密

public class RSADemo2 {
    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);

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

运行程序:

(2)私钥加密私钥解密

public class RSADemo2 {
    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);

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

        // 私钥进行解密
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        // 对密文进行解密,不需要使用base64,因为原文不会乱码
        byte[] decryptBytes = cipher.doFinal(bytes);
        System.out.println("原文为:" + new String(decryptBytes));
    }
}

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

(3)私钥加密公钥解密

public class RSADemo2 {
    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);

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

        // 公钥进行解密
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        // 对密文进行解密,不需要使用base64,因为原文不会乱码
        byte[] decryptBytes = cipher.doFinal(bytes);
        System.out.println("原文为:" + new String(decryptBytes));
    }
}

运行程序:

(4)公钥加密和公钥解密

一样会报错,使用公钥加密必须使用私钥进行解密。

4、保存公钥和私钥

前面代码每次都会生成 加密和解密 ,咱们需要把加密和解密的方法全部到本地的根目录下面。

public class RSADemo3 {
    public static void main(String[] args) throws Exception {
        String input = "硅谷";
        // 加密算法
        String algorithm = "RSA";

        //生成密钥对并保存在本地文件中
        generateKeyToFile(algorithm, "a.pub""a.pri");
    }

    /**
     * 生成密钥对并保存在本地文件中
     *
     * @param algorithm : 算法
     * @param pubPath   : 公钥保存路径
     * @param priPath   : 私钥保存路径
     * @throws Exception
     */

    private 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数组
        byte[] publicKeyEncoded = publicKey.getEncoded();
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 进行Base64编码
        String publicKeyString = Base64.encode(publicKeyEncoded);
        String privateKeyString = Base64.encode(privateKeyEncoded);
        // 保存文件
        FileUtils.writeStringToFile(new File(pubPath), publicKeyString, StandardCharsets.UTF_8);
        FileUtils.writeStringToFile(new File(priPath), privateKeyString, StandardCharsets.UTF_8);
    }
}

运行在项目根目录生成私钥:

运行在项目根目录生成公钥:

5、读取私钥和公钥并加解密

public class RSADemo3 {
    public static void main(String[] args) throws Exception {
        String input = "硅谷";
        // 加密算法
        String algorithm = "RSA";

        //生成密钥对并保存在本地文件中
        //generateKeyToFile(algorithm, "a.pub", "a.pri");

        PrivateKey privateKey = getPrivateKey("a.pri", algorithm);
        System.out.println("privateKey = " + privateKey);

        PublicKey publicKey = getPublicKey("a.pub", algorithm);
        System.out.println("publicKey = " + publicKey);

        String encryptStr = encryptRSA(algorithm, privateKey, input);
        System.out.println("encryptStr密钥:" + encryptStr);
        String decryptStr = decryptRSA(algorithm, publicKey, encryptStr);
        System.out.println("decryptStr原文: " + decryptStr);

    }

    //从文件读取私钥,并转换为私钥对象
    public static PrivateKey getPrivateKey(String priPath, String algorithm) throws Exception {
        // 将文件内容转为字符串
        String privateKeyString = FileUtils.readFileToString(new File(priPath), Charset.defaultCharset());
        // 获取密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        // 构建密钥规范 进行Base64解码
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyString));
        // 生成私钥
        return keyFactory.generatePrivate(spec);
    }

    //从文件读取公钥,并转换为公钥对象
    public static PublicKey getPublicKey(String pubPath, String algorithm) throws Exception {
        // 将文件内容转为字符串
        String publicKeyString = FileUtils.readFileToString(new File(pubPath), Charset.defaultCharset());
        // 获取密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        // 构建密钥规范 进行Base64解码
        X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decode(publicKeyString));
        // 生成公钥
        return keyFactory.generatePublic(spec);
    }


    /**
     * 使用密钥加密数据
     *
     * @param algorithm 算法
     * @param key       密钥
     * @param input     原文
     * @return 密文
     */

    public static String encryptRSA(String algorithm, Key key, String input) throws Exception {
        //创建加密对象
        //参数表示加密算法
        Cipher cipher = Cipher.getInstance(algorithm);

        //初始化加密
        //第一个参数:加密的模式
        //第二个参数:使用私钥进行加密
        cipher.init(Cipher.ENCRYPT_MODE, key);

        //私钥加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        //对密文进行 Base64 编码
        return Base64.encode(bytes);
    }


    /**
     * 使用密钥解密数据
     *
     * @param algorithm     算法
     * @param key           密钥
     * @param encrypted     密文
     * @return 原文
     * @throws Exception
     */

    public static String decryptRSA(String algorithm, Key key, String encrypted) throws Exception {
        // 创建解密对象
        // 参数表示解密算法
        Cipher cipher = Cipher.getInstance(algorithm);

        //使用解密模式
        cipher.init(Cipher.DECRYPT_MODE, key);

        //由于密文进行了Base64编码,在这里需要进行解码
        byte[] decode = Base64.decode(encrypted);

        //对密文进行解密,不需要使用Base64,因为原文不会乱码
        byte[] bytes = cipher.doFinal(decode);
        return new String(bytes);
    }


    /**
     * 生成密钥对并保存在本地文件中
     *
     * @param algorithm : 算法
     * @param pubPath   : 公钥保存路径
     * @param priPath   : 私钥保存路径
     * @throws Exception
     */

    private 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数组
        byte[] publicKeyEncoded = publicKey.getEncoded();
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 进行Base64编码
        String publicKeyString = Base64.encode(publicKeyEncoded);
        String privateKeyString = Base64.encode(privateKeyEncoded);
        // 保存文件
        FileUtils.writeStringToFile(new File(pubPath), publicKeyString, StandardCharsets.UTF_8);
        FileUtils.writeStringToFile(new File(priPath), privateKeyString, StandardCharsets.UTF_8);
    }


}

运行程序:

posted on 2022-04-30 13:07  格物致知_Tony  阅读(142)  评论(0编辑  收藏  举报