• 第二章:常见加密与解密


    一、常见加密方式

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


    }

    运行程序:

  • 相关阅读:
    编码和字符集
    【机器学习】模型泛化
    asp.net GridView控件的列属性
    asp.net截取指定长度的字符串内容
    asp.net 对数据库表增加,删除,编辑更新修改
    asp.net 链接数据库ADO.NET
    常用正则表达式 验证电子邮件网址邮政编码等
    ASP.NET获取文件的相关知识
    C#获取picturebox图片路径
    C# dataGridView根据数据调整列宽
  • 原文地址:https://www.cnblogs.com/niujifei/p/16209947.html
Copyright © 2020-2023  润新知