security · 2020-03-13 0

Java消息摘要、对称加密、非对称加密和数字签名

一、消息摘要

消息摘要是一种与消息认证结合使用以确保消息完整性的技术,消息摘要采用单向Hash函数将加密的明文“摘要”成一串密文,则这串密文亦称为数字指纹(Finger Print),则接受者通过对接受到信息的新产生的摘要与原摘要比较,就知道消息是否被修改了

特点:

  1. 唯一性
  2. 不可逆
  3. 不需要秘钥
  4. 长度固定,无论输入的明文有多长,计算出的消息摘要的长度总数固定的

原理:消息摘要就是将需要摘要的数据作为参数,经过Hash函数计算,得到的散列值

常用消息摘要算法:MD(Message Digest,消息摘要算法)、SHA(Secure Hash Algorithm, 安全散列算法)、MAC(Message AuthenticationCode,消息认证码算法),目前广泛使用算法MD5、SHA1

    @Test
    public void testMD() throws Exception {
        String plainText = "USE MD";
        System.out.println("明文:" + plainText);

        // 使用getInstance("算法")来获得消息摘要
        MessageDigest messageDigest = MessageDigest.getInstance("MD5");
        // 开始使用算法
        messageDigest.update(plainText.getBytes());
        // 得到摘要
        byte[] digestByte = messageDigest.digest();
        // 摘要长度始终为16个字节,128位
        System.out.println("摘要长度:" + digestByte.length);

        // 输出算法运算结果,防止乱码,使用Base64编码
        String encode = Base64.getEncoder().encodeToString(digestByte);
        System.out.println("摘要:" + encode);
    }

结果:

摘要始终不变

明文:USE MD
摘要长度:16
摘要:fa3mkoztZ4XtArxxV4vPGg==

二、对称加密

消息发送方和消息接收方必须使用相同的密钥

对称加密常用算法:DES(Data Encryption Standard, 数据加密标准)、AES(Advanced Encryption Standard, 高级加密标准)

    @Test
    public void testAES() throws Exception {
        String plainText = "USE AES";
        System.out.println("明文:" + plainText);

        // 得到一个使用AES算法的KeyGenerator的实例
        KeyGenerator kenGen = KeyGenerator.getInstance("AES");
        // 定义秘钥长度128位
        kenGen.init(128);
        // 通过KeyGenerator产生一个key(秘钥算法刚才已定义,为AES)
        Key key = kenGen.generateKey();

        // 1.获取加密工具类,定义Cipher的基本信息,ECB是加密方法,PKCS5Padding是填充方法
        // 算法/工作模式/填充模式(algorithm/mode/padding
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");

        // 2.加密,对工具类对象进行初始化
        cipher.init(Cipher.ENCRYPT_MODE, key);
        // 3.用加密工具类对象对明文进行加密
        byte[] encipherByte = cipher.doFinal(plainText.getBytes());
        String encode = Base64.getEncoder().encodeToString(encipherByte);
        System.out.println("加密:" + encode);

        // 2.解密
        cipher.init(Cipher.DECRYPT_MODE, key);
        // 3.用加密工具类对象对密文进行解密
        byte[] decode = Base64.getDecoder().decode(encode);
        byte[] decipherByte = cipher.doFinal(decode);
        String decipherText = new String(decipherByte);
        System.out.println("解密:" + decipherText);
    }

结果:

每次执行后,密文不同的

明文:USE AES
加密:LcZyK4nD7BdSe2OY+4yYIw==
解密:USE AES

三、非对称加密

非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)

非对称加密常用算法:RSA

    @Test
    public void testRSA() throws Exception {
        String plainText = "USE RSA";
        System.out.println("明文:" + plainText);

        // 产生一个RSA秘钥生成器KeyPairGenerator(一对钥匙生成器)
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        // 定义秘钥长度512位
        keyGen.initialize(512);
        // 通过KeyPairGenerator产生秘钥,注意这里的key是一对钥匙
        KeyPair key = keyGen.generateKeyPair();

        // 1.获取加密工具类
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

        // 2.使用公钥加密,对工具类对象进行初始化
        cipher.init(Cipher.ENCRYPT_MODE, key.getPublic());
        // 3.用加密工具类对象对明文进行加密
        byte[] encipherByte = cipher.doFinal(plainText.getBytes());
        String encode = Base64.getEncoder().encodeToString(encipherByte);
        System.out.println("加密:" + encode);

        // 2.使用私钥解密,对工具类对象进行初始化
        // PrivateKey privateKey = KeyFactory.getInstance("RSA")
        // .generatePrivate(new PKCS8EncodedKeySpec(key.getPrivate().getEncoded()));
        // cipher.init(Cipher.DECRYPT_MODE, privateKey);
        cipher.init(Cipher.DECRYPT_MODE, key.getPrivate());
        // 3.用加密工具类对象对密文进行解密
        byte[] decode = Base64.getDecoder().decode(encode);
        byte[] decipherByte = cipher.doFinal(decode);
        String decipherText = new String(decipherByte);
        System.out.println("解密:" + decipherText);
    }

结果:

每次执行后,密文不同的

明文:USE RSA
加密:P4YisfnFrY4fERKWIJEpp2QdNO8UMJBmgeZt5Q6JmNSOhqbW87k7+74xA8L6z7IkjSZITJk3c1ULDB80jAizIg==
解密:USE RSA

四、数字签名

数字签名的主要作用就是保证了数据的有效性(验证是谁发的)和完整性(证明信息没有被篡改)

摘要经过加密,就得到数字签名

签名算法有:MD5withRSA、SHA1withRSA、SHA256withRSA

    @Test
    public void testSign() throws Exception {
        String plainText = "USE RSA TO SIGNATURE";
        System.out.println("正文:" + plainText);

        // 形成RSA公钥对
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(512);
        KeyPair key = keyGen.generateKeyPair();

        // 1.获取签名工具类
        Signature sig = Signature.getInstance("MD5withRSA");
        // 2.初始化签名,使用私钥签名
        sig.initSign(key.getPrivate());
        sig.update(plainText.getBytes());
        // 3.得到签名
        byte[] signByte = sig.sign();
        String encode = Base64.getEncoder().encodeToString(signByte);
        System.out.println("签名:" + encode);

        // 2.初始化验证,使用公钥验证
        sig.initVerify(key.getPublic());
        sig.update(plainText.getBytes());
        // 3.进行验证
        byte[] decode = Base64.getDecoder().decode(encode);
        if (sig.verify(decode)) {
            System.out.println("签名验证正确!");
        } else {
            System.out.println("签名验证失败!");
        }
    }

结果:

每次执行后,签名不同的

正文:USE RSA TO SIGNATURE
签名:U8p07Agh4/BcxmZ2oh6IaTtFaNBi4X2PfruTTIJlISp2bUEInwx5W47XMsn6oBbRN+SuOCnz8zi1Xpmv7yqCIw==
签名验证正确!