Java 中的加密与签名


  !版权声明:本博客内容均均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。

目录[-]


环境配置:

  • JDK 版本:1.8

参考地址:

一、加密简介

1、背景

数据加密的基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码为“密文”,使其只能在输入相应的密钥之后才能显示出原容,通过这样的途径来达到保护数据不被非法人窃取、阅读的目的。 该过程的逆过程为解密,即将该编码信息转化为其原来数据的过程。

2、目的

使用密码学可以达到以下目的:

  • 保密性: 防止用户的标识或数据被读取。
  • 数据完整性: 防止数据被更改。
  • 身份验证: 确保数据发自特定的一方。

3、加密类型

加密技术通常分为两大类“对称加密”和“非对称加密”。

  • 对称式加密: 对称式加密就是加密和解密使用同一个密钥。
  • 非对称式加密: 非对称式加密就是加密和解密所使用的不是同一个密钥,通常有两个密钥,称为“公钥”和“私钥”,私钥自己拥有,不能给别人,公钥公开。根据应用的不同,我们可以选择使用不同的密钥加密。

二、Java 中的加密方法示例

1、Maven 引入 Hutool 工具依赖

这里主要是使用 Hutool 工具,该工具封装了 Java 大多数加密方式,使用起来非常方便。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.aspire</groupId>
    <artifactId>java-encrypt</artifactId>
    <version>0.0.1</version>
    <name>java-encrypt</name>
    <description>java encrypt demo</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.0</version>
        </dependency>
    </dependencies>

</project>

2、对称加密示例

(1)、简介

对称加密(也叫私钥加密)指加密和解密使用相同密钥的加密算法。有时又叫传统密码算法,就是加密密钥能够从解密密钥中推算出来,同时解密密钥也可以从加密密钥中推算出来。而在大多数的对称算法中,加密密钥和解密密钥是相同的,所以也称这种加密算法为秘密密钥算法或单密钥算法。它要求发送方和接收方在安全通信之前,商定一个密钥。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人都可以对他们发送或接收的消息解密,所以密钥的保密性对通信的安全性至关重要。

(2)、示例

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import java.io.UnsupportedEncodingException;

/**
 * 对称加密
 */
public class SymmetricalEncryptionExample {

    /**
     * AES 加密、解密
     *
     * @param data 加密数据
     */
    public static void aesEncrypt(String data, String secret) {
        // 随机生成密钥
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue(), secret.getBytes()).getEncoded();
        // 创建 AES 对象
        SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
        // 加密
        String encrypt = aes.encryptHex(data);
        // 解密
        String decrypt = aes.decryptStr(encrypt);
        System.out.println("AES 加密后的串:" + encrypt + "AES 解密的数据:" + decrypt);
    }

    /**
     * DES 加密、解密
     *
     * @param data 加密数据
     */
    public static void desEncrypt(String data, String secret) {
        // 随机生成密钥
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(), secret.getBytes()).getEncoded();
        // 创建 DES 对象
        SymmetricCrypto des = new SymmetricCrypto(SymmetricAlgorithm.DES, key);
        // 加密
        String encrypt = des.encryptHex(data);
        // 解密
        String decrypt = des.decryptStr(encrypt);
        System.out.println("DES 加密后的串:" + encrypt + "DES 解密的数据:" + decrypt);
    }

    /**
     * DESede 加密、解密
     *
     * @param data 加密数据
     */
    public static void desedeEncrypt(String data, String secret) {
        // 随机生成密钥
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DESede.getValue(), secret.getBytes()).getEncoded();
        // 创建 DESede 对象
        SymmetricCrypto desede = new SymmetricCrypto(SymmetricAlgorithm.DESede, key);
        // 加密
        String encrypt = desede.encryptHex(data);
        // 解密
        String decrypt = desede.decryptStr(encrypt);
        System.out.println("DESede 加密后的串:" + encrypt + "DESede 解密的数据:" + decrypt);
    }

    /**
     * Blowfish 加密、解密
     *
     * @param data 加密数据
     */
    public static void blowfishEncrypt(String data, String secret) {
        // 随机生成密钥
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.Blowfish.getValue(), secret.getBytes()).getEncoded();
        // 创建 Blowfish 对象
        SymmetricCrypto blowfish = new SymmetricCrypto(SymmetricAlgorithm.Blowfish, key);
        // 加密
        String encrypt = blowfish.encryptHex(data);
        // 解密
        String decrypt = blowfish.decryptStr(encrypt);
        System.out.println("Blowfish 加密后的串:" + encrypt + "Blowfish 解密的数据:" + decrypt);
    }

    /**
     * RC2 加密、解密
     *
     * @param data 加密数据
     */
    public static void rc2Encrypt(String data, String secret) {
        // 随机生成密钥
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.RC2.getValue(), secret.getBytes()).getEncoded();
        // 创建 RC2 对象
        SymmetricCrypto rc2 = new SymmetricCrypto(SymmetricAlgorithm.RC2, key);
        // 加密
        String encrypt = rc2.encryptHex(data);
        // 解密
        String decrypt = rc2.decryptStr(encrypt);
        System.out.println("RC2 加密后的串:" + encrypt + "RC2 解密的数据:" + decrypt);
    }

    /**
     * ARCFOUR 加密、解密
     *
     * @param data 加密数据
     */
    public static void arcfourEncrypt(String data, String secret) {
        // 随机生成密钥
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.ARCFOUR.getValue(), secret.getBytes()).getEncoded();
        // 创建 ARCFOUR 对象
        SymmetricCrypto arcfour = new SymmetricCrypto(SymmetricAlgorithm.ARCFOUR, key);
        // 加密
        String encrypt = arcfour.encryptHex(data);
        // 解密
        String decrypt = arcfour.decryptStr(encrypt);
        System.out.println("ARCFOUR 加密后的串:" + encrypt + "ARCFOUR 解密的数据:" + decrypt);
    }

    public static void main(String[] args) throws UnsupportedEncodingException {
        /* 设置要加密的内容 */
        String data = "测试数据";
        String secret1 = "abcdefghijklmnop";
        String secret2 = "abcdefghijklmnopqrstuvwx";
        /* AES加密 */
        aesEncrypt(data, secret1);
        /* DES加密 */
        desEncrypt(data, secret1);
        /* RC2加密 */
        rc2Encrypt(data, secret1);
        /* Blowfish加密 */
        blowfishEncrypt(data, secret1);
        /* ARCFOUR加密 */
        arcfourEncrypt(data, secret1);
        /* DESede加密 */
        desedeEncrypt(data, secret2);
    }

}

3、非对称加密示例

(1)、简介

非对称式加密就是加密和解密所使用的不是同一个密钥,通常有两个密钥,称为“公钥”和“私钥”,它们两个必需配对使用,否则不能打开加密文件。这里的“公钥”是指可以对外公布的,“私钥”则不能,只能由持有人一个人知道。它的优越性就在这里,因为对称式的加密方法如果是在网络上传输加密文件就很难不把密钥告诉对方,不管用什么方法都有可能被别窃听到。而非对称式的加密方法有两个密钥,且其中的“公钥”是可以公开的,也就不怕别人知道,收件人解密时只要用自己的私钥即可以,这样就很好地避免了密钥的传输安全性问题。

(2)、示例

import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import java.io.UnsupportedEncodingException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * 非对称加密
 */
public class AsymmetricEncryptionExample {

    /**
     * RSA 公钥加密,私钥解密
     *
     * @param data             加密数据
     * @param privateKeyString 私钥字符串
     * @param publicKeyString  公钥字符串
     * @throws UnsupportedEncodingException
     */
    public static void rsaEncrypt1(String data, String privateKeyString, String publicKeyString) throws UnsupportedEncodingException {
        // 将公、私钥字符串转换成公、私钥对象
        PrivateKey privateKey = SecureUtil.generatePrivateKey("RSA", Base64.decode(privateKeyString));
        PublicKey publicKey = SecureUtil.generatePublicKey("RSA", Base64.decode(publicKeyString));
        // 获取字符串byte数组
        byte[] bytes = data.getBytes("UTF-8");
        // 创建 RSA 对象
        RSA rsa = new RSA();
        // 设置公钥,然后执行公钥加密
        rsa.setPublicKey(publicKey);
        byte[] encrypt = rsa.encrypt(bytes, KeyType.PublicKey);
        // 设置私钥,然后执行私钥解密
        rsa.setPrivateKey(privateKey);
        byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);
        // 输出解密的内容
        System.out.println("公钥加密,私钥解密,解密内容:" + new String(decrypt));
    }

    /**
     * RSA 私钥加密,公钥解密
     *
     * @param data             加密数据
     * @param privateKeyString 私钥字符串
     * @param publicKeyString  公钥字符串
     * @throws UnsupportedEncodingException
     */
    public static void rsaEncrypt2(String data, String privateKeyString, String publicKeyString) throws UnsupportedEncodingException {
        // 将公、私钥字符串转换成公、私钥对象
        PrivateKey privateKey = SecureUtil.generatePrivateKey("RSA", Base64.decode(privateKeyString));
        PublicKey publicKey = SecureUtil.generatePublicKey("RSA", Base64.decode(publicKeyString));
        // 获取字符串byte数组
        byte[] bytes = data.getBytes("UTF-8");
        // 创建 RSA 对象
        RSA rsa = new RSA();
        // 设置私钥,然后执行私钥加密
        rsa.setPrivateKey(privateKey);
        byte[] encrypt = rsa.encrypt(bytes, KeyType.PrivateKey);
        // 设置公钥,然后执行公钥解密
        rsa.setPublicKey(publicKey);
        byte[] decrypt = rsa.decrypt(encrypt, KeyType.PublicKey);
        // 输出解密的内容
        System.out.println("私钥加密,公钥解密,解密内容:" + new String(decrypt));
    }

    public static void main(String[] args) throws UnsupportedEncodingException {
        /* 生成钥匙对 */
        KeyPair pair = SecureUtil.generateKeyPair("RSA");
        PrivateKey privateKey = pair.getPrivate();
        PublicKey publicKey = pair.getPublic();
        // 将公钥私钥转换成 Base64 字符串
        String privateKeyString = Base64.encode(privateKey.getEncoded());
        String publicKeyString = Base64.encode(publicKey.getEncoded());
        /* 设置要加密的内容 */
        String data = "测试数据";
        /* RSA 公钥加密,私钥解密 */
        rsaEncrypt1(data, privateKeyString, publicKeyString);
        /* RSA 私钥加密,公钥解密 */
        rsaEncrypt2(data, privateKeyString, publicKeyString);
    }

}

4、签名示例

(1)、简介

在两个人进行通信,需要确保收到的内容不被劫取篡改,这里就用到签名来确保发消息的人确实是指定的人。

比如说,A 准备给 B 发送一段信息,A 先将消息进行散列运算,得到消息摘要 MD5,然后用自己的私匙对摘要进行加密,然后发送给 B,B 得到消息内容和加密的信息摘要,再用 A 的公匙和发送的内容来验证这段加密摘要(如果验证成功,则证明是 A 发送的消息),如果验证成则说明数据没有被篡改过。(信息摘要是根据对消息内容进行散列运算得到一个唯一的标识码,除非数据相同,否则几乎不可能由其他数据运算得到此标识码,所以它是唯一的,只要信息内容有一点改变,再次进行散列运算时得到标识码就会改变,所以可以对解密后的信息进行散列运算得到摘要然后和发送过来的信息摘要进行对比判断数据是否被修改)。

(2)、示例

import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * 签名
 */
public class SignExample {

    /**
     * RSA 私钥签名
     *
     * @param data             签名数据
     * @param privateKeyString 私钥字符串
     */
    public static String rsaSign(String data, String privateKeyString) {
        // 将私钥字符串转换成私钥对象
        PrivateKey privateKey = SecureUtil.generatePrivateKey("RSA", Base64.decode(privateKeyString));
        // 设置签名对象以及加密算法
        Sign sign = SecureUtil.sign(SignAlgorithm.MD5withRSA);
        // 设置私钥,然后执行签名
        sign.setPrivateKey(privateKey);
        byte[] bytes = sign.sign(data.getBytes());
        // 将签名转换为 Base64 字符串,然后返回
        return Base64.encode(bytes);
    }

    /**
     * RSA 公钥验签
     *
     * @param data            签名数据
     * @param publicKeyString 公钥
     * @param signString      签名
     * @return 是否验证成功
     */
    public static boolean rsaSignVerify(String data, String publicKeyString, String signString) {
        // 将公钥字符串转换成公钥对象
        PublicKey publicKey = SecureUtil.generatePublicKey("RSA", Base64.decode(publicKeyString));
        // 设置签名对象以及加密算法
        Sign sign = SecureUtil.sign(SignAlgorithm.MD5withRSA);
        // 将签名字符串转换成 byte 数组
        byte[] bytes = Base64.decode(signString);
        // 设置公钥,然后执行验签
        sign.setPublicKey(publicKey);
        return sign.verify(data.getBytes(), bytes);
    }

    public static void main(String[] args) {
        /* 生成钥匙对 */
        KeyPair pair = SecureUtil.generateKeyPair("RSA");
        PrivateKey privateKey = pair.getPrivate();
        PublicKey publicKey = pair.getPublic();
        // 将公钥私钥转换成 Base64 字符串
        String privateKeyString = Base64.encode(privateKey.getEncoded());
        String publicKeyString = Base64.encode(publicKey.getEncoded());
        /* 设置要加密的内容 */
        String data = "测试数据";
        /* RSA 私钥签名,公钥验签 */
        String sign = rsaSign(data, privateKeyString);
        boolean verify = rsaSignVerify(data, publicKeyString, sign);
        System.out.println("签名验证结果:" + verify);
    }

}

5、摘要加密示例

(1)、简介

摘要算法是一种能产生特殊输出格式的算法,这种算法的特点是无论用户输入什么长度的原始数据,经过计算后输出的密文都是固定长度的,这种算法的原理是根据一定的运算规则对原数据进行某种形式的提取,这种提取就是摘要,被摘要的数据内容与原数据有密切联系,只要原数据稍有改变,输出的“摘要”便完全不同,因此,基于这种原理的算法便能对数据完整性提供较为健全的保障。

但是,由于输出的密文是提取原数据经过处理的定长值,所以它已经不能还原为原数据,即消息摘要算法是不可逆的,理论上无法通过反向运算取得原数据内容,因此它通常只能被用来做数据完整性验证。

(2)、示例

import cn.hutool.crypto.digest.DigestUtil;

/**
 * 摘要加密
 */
public class DigesterEncryption {

    /**
     * MD2 加密
     * @param data 加密内容
     */
    public static void md2Encrypt(String data) {
        String md2String = DigestUtil.md5Hex(data);
        System.out.println("MD2加密后的字符串:" + md2String);
    }

    /**
     * MD5 加密
     * @param data 加密内容
     */
    public static void md5Encrypt(String data) {
        String md5String = DigestUtil.md5Hex(data);
        System.out.println("MD5加密后的字符串:" + md5String);
    }

    /**
     * SHA-1 加密
     * @param data 加密内容
     */
    public static void sha1Encrypt(String data) {
        String sha1String = DigestUtil.sha1Hex(data);
        System.out.println("SHA1加密后的字符串:" + sha1String);
    }

    /**
     * SHA-256 加密
     * @param data 加密内容
     */
    public static void sha256Encrypt(String data) {
        String sha256String = DigestUtil.sha256Hex(data);
        System.out.println("SHA256加密后的字符串:" + sha256String);
    }

    public static void main(String[] args) {
        /* 设置要加密的内容 */
        String data = "测试数据";
        /* MD2加密 */
        md2Encrypt(data);
        /* MD5加密 */
        md5Encrypt(data);
        /* SHA1加密 */
        sha1Encrypt(data);
        /* SHA256加密 */
        sha256Encrypt(data);
    }

}

—END—