# 对称及非对称算法

对称及非对称加密算法

# 1. 对称加密

对称加密(也叫共享密钥加密算法)指加密和解密使用相同密钥的加密算法

它要求发送方和接收方在安全通信之前,商定一个密钥,对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人都可以对他们发送或接收的消息解密,所以密钥的保密性对通信的安全性至关重要

在对称加密算法中,数据发送方将明文(原始数据)和加密密钥一起经过特殊加密处理,生成复杂的加密密文进行发送

数据接收方收到密文后,若想读取原数据,则需要使用加密使用的密钥及相同算法的逆算法对加密的密文进行解密,才能使其恢复成可读明文

  • AES (默认AES/ECB/PKCS5Padding)
  • ARCFOUR
  • Blowfish
  • DES (默认DES/ECB/PKCS5Padding)
  • DESede
  • RC2
  • PBEWithMD5AndDES
  • PBEWithSHA1AndDESede
  • PBEWithSHA1AndRC2_40

AES 工具类

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;

import javax.crypto.SecretKey;
import java.security.SecureRandom;

/**
 * AESUtil
 *
 * @author wliduo[i@dolyw.com]
 * @date 2022/7/12 13:54
 */
public class AESUtil {

    /**
     * 数据类型-十六进制
     */
    private static final String DATA_TYPE_HEX = "Hex";

    /**
     * 数据类型-Base64
     */
    private static final String DATA_TYPE_BASE64 = "Base64";

    /**
     * 私钥类型-十六进制
     */
    public static final String KEY_TYPE_GENERATE_HEX = "GenerateKeyHex";

    /**
     * 私钥类型-Base64
     */
    public static final String KEY_TYPE_GENERATE_BASE64 = "GenerateKeyBase64";

    /**
     * 私钥类型-SHA1PRNGRandom
     */
    public static final String KEY_TYPE_SHA1PRNG_RANDOM = "SHA1PRNGRandom";

    /**
     * 获取私钥
     *
     * @return
     */
    public static String getKey(String keyType) {
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();
        // 根据私钥类型生成对应的私钥字符
        if (KEY_TYPE_GENERATE_BASE64.equals(keyType)) {
            return Base64.encode(key);
        }
        if (KEY_TYPE_GENERATE_HEX.equals(keyType)) {
            return HexUtil.encodeHexStr(key);
        }
        // 默认返回KEY_TYPE_SHA1PRNG_RANDOM类型随机字符
        return RandomUtil.randomString("abcdefghijklmnopqrstuvwxyz" +
                "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789",30);
    }

    /**
     * 初始化私钥
     *
     * @param keyType
     * @return
     */
    public static SecretKey initKey(String privateKeyStr, String keyType) {
        // 根据私钥字符和私钥类型初始化SecretKey
        SecretKey secretKey = null;
        if (KEY_TYPE_GENERATE_BASE64.equals(keyType)) {
            secretKey = KeyUtil.generateKey(SymmetricAlgorithm.AES.getValue(), Base64.decode(privateKeyStr));
        }
        if (KEY_TYPE_GENERATE_HEX.equals(keyType)) {
            secretKey = KeyUtil.generateKey(SymmetricAlgorithm.AES.getValue(), HexUtil.decodeHex(privateKeyStr));
        }
        if (KEY_TYPE_SHA1PRNG_RANDOM.equals(keyType)) {
            SecureRandom secureRandom = RandomUtil.getSHA1PRNGRandom(privateKeyStr.getBytes());
            secretKey = KeyUtil.generateKey(SymmetricAlgorithm.AES.getValue(), 128, secureRandom);
        }
        return secretKey;
    }

    /**
     * 私钥加密
     *
     * @param content
     * @return
     */
    public static String encrypt(String content, String privateKeyStr, String keyType, String encryptDataType) {
        AES aes = new AES(initKey(privateKeyStr, keyType));
        byte[] contentByte = aes.encrypt(content);
        if (DATA_TYPE_BASE64.equals(encryptDataType)) {
            // 将字节数组转为Base64字符返回
            return Base64.encode(contentByte);
        }
        if (DATA_TYPE_HEX.equals(encryptDataType)) {
            // 将字节数组转为十六进制字符返回
            return HexUtil.encodeHexStr(contentByte);
        }
        return StrUtil.str(contentByte, CharsetUtil.CHARSET_UTF_8);
    }

    /**
     * 私钥解密
     *
     * @param content
     * @return
     */
    public static String decrypt(String content, String privateKeyStr, String keyType, String encryptDataType) {
        AES aes = new AES(initKey(privateKeyStr, keyType));
        byte[] contentByte = null;
        if (DATA_TYPE_BASE64.equals(encryptDataType)) {
            // 将解密内容按Base64解析为字节数组
            contentByte = Base64.decode(content);
        }
        if (DATA_TYPE_HEX.equals(encryptDataType)) {
            // 将解密内容按十六进制解析为字节数组
            contentByte = HexUtil.decodeHex(content);
        }
        contentByte = aes.decrypt(contentByte);
        // 解密内容字节数组转换为字符返回
        return StrUtil.str(contentByte, CharsetUtil.CHARSET_UTF_8);
    }

    /**
     * 测试
     *
     * @param args
     */
    public static void main(String[] args) throws Exception {
        // String keyType = KEY_TYPE_SHA1PRNG_RANDOM;
        // String keyType = KEY_TYPE_GENERATE_BASE64;
        String keyType = KEY_TYPE_GENERATE_HEX;
        String key = getKey(keyType);
        System.out.println(key);

        String content = "{AESUtil}测试一下‘’";
        String encryptDataType = DATA_TYPE_BASE64;
        // String encryptDataType = DATA_TYPE_HEX;

        // 私钥加密
        String contentEncrypt = encrypt(content, key, keyType, encryptDataType);
        // 私钥解密
        String contentDecrypt = decrypt(contentEncrypt, key, keyType, encryptDataType);
        System.out.println(contentEncrypt);
        System.out.println(contentDecrypt);
    }
}

# 2. 非对称加密

非对称加密算法(也叫公开密钥加密算法)。它需要两个密钥,一个称为公开密钥(public key),即公钥,另一个称为私有密钥(private key),即私钥,因为加密和解密使用的是两个不同的密钥,所以这种算法称为非对称加密算法

私钥自己拥有,不能给别人,公钥公开。根据应用的不同,我们可以选择使用不同的密钥加密

如果使用公钥对数据进行加密,只有用对应的私钥才能进行解密;如果使用私钥对数据进行加密,只有用对应的公钥才能进行解密

签名:使用私钥加密,公钥解密,用于让公钥所有者验证私钥所有者的身份并且用来防止私钥所有者发布的内容被篡改,但是不用来保证内容不被他人获得

加密:用公钥加密,私钥解密,用于向公钥所有者发布信息,这个信息可能被他人篡改,但是无法被他人获得

  • RSA
  • RSA_ECB_PKCS1(RSA/ECB/PKCS1Padding)
  • RSA_None(RSA/None/NoPadding)
  • ECIES(需要Bouncy Castle库)

RSA 工具类

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;

/**
 * RSAUtil
 *
 * @author wliduo[i@dolyw.com]
 * @date 2022/7/12 13:54
 */
public class RSAUtil {

    /**
     * 数据类型-十六进制
     */
    private static final String DATA_TYPE_HEX = "Hex";

    /**
     * 数据类型-Base64
     */
    private static final String DATA_TYPE_BASE64 = "Base64";

    /**
     * 获取公私钥,使用-----分隔,公钥-----私钥
     *
     * @return
     */
    public static String getKey() {
        RSA rsa = new RSA(AsymmetricAlgorithm.RSA.getValue());
        return rsa.getPublicKeyBase64() + "-----" + rsa.getPrivateKeyBase64();
    }

    /**
     * 公钥加密
     *
     * @param content
     * @return
     */
    public static String encrypt(String content, String publicKeyStr, String encryptDataType) {
        RSA rsa = new RSA(AsymmetricAlgorithm.RSA.getValue(), null, publicKeyStr);
        // 加密内容转换为字节数组操作
        byte[] contentByte = StrUtil.bytes(content, CharsetUtil.CHARSET_UTF_8);
        contentByte = rsa.encrypt(contentByte, KeyType.PublicKey);
        if (DATA_TYPE_BASE64.equals(encryptDataType)) {
            // 将字节数组转为Base64字符返回
            return Base64.encode(contentByte);
        }
        if (DATA_TYPE_HEX.equals(encryptDataType)) {
            // 将字节数组转为十六进制字符返回
            return HexUtil.encodeHexStr(contentByte);
        }
        return StrUtil.str(contentByte, CharsetUtil.CHARSET_UTF_8);
    }

    /**
     * 私钥解密
     *
     * @param content
     * @return
     */
    public static String decrypt(String content, String privateKeyStr, String encryptDataType) {
        RSA rsa = new RSA(AsymmetricAlgorithm.RSA.getValue(), privateKeyStr, null);
        byte[] contentByte = null;
        if (DATA_TYPE_BASE64.equals(encryptDataType)) {
            // 将解密内容按Base64解析为字节数组
            contentByte = Base64.decode(content);
        }
        if (DATA_TYPE_HEX.equals(encryptDataType)) {
            // 将解密内容按十六进制解析为字节数组
            contentByte = HexUtil.decodeHex(content);
        }
        contentByte = rsa.decrypt(contentByte, KeyType.PrivateKey);
        // 解密内容字节数组转换为字符返回
        return StrUtil.str(contentByte, CharsetUtil.CHARSET_UTF_8);
    }

    /**
     * 测试
     *
     * @param args
     */
    public static void main(String[] args) {
        String[] keys = getKey().split("-----");
        for (String key : keys) {
            System.out.println(key);
        }

        String content = "{RSAUtil}测试一下‘’";
        String encryptDataType = DATA_TYPE_BASE64;
        // String encryptDataType = DATA_TYPE_HEX;

        // 公钥加密
        String contentEncrypt = encrypt(content, keys[0], encryptDataType);
        // 私钥解密
        String contentDecrypt = decrypt(contentEncrypt, keys[1], encryptDataType);
        System.out.println(contentEncrypt);
        System.out.println(contentDecrypt);
    }
}

参考

上次更新时间: 2022-11-17 07:58:19