ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

第二章:常见加密与解密

2022-04-30 13:35:11  阅读:268  来源: 互联网

标签:加密 String algorithm 解密 Cipher 密钥 key 第二章


一、常见加密方式

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


}

运行程序:

标签:加密,String,algorithm,解密,Cipher,密钥,key,第二章
来源: https://www.cnblogs.com/niujifei/p/16209947.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有