10、加密算法

加密方式

对称加密

对称加密是一种相对来说比较常用的加密方式,它的工作原理很简单:先用一个秘钥将明文加密成密文,再用相同的秘钥将密文解密成明文。其中最常见的对称加密算法有DES、3DES、AES。

对称加密的优点是加密解密速度快,因为它使用的是同一个秘钥进行加密和解密。但是它也有缺点,由于是同一个秘钥,一旦这个秘钥泄漏,整个加密系统就会失效。一旦数据被攻击者窃取,攻击者就可以使用这个秘钥进行解密。

package top.ygang.huijifindresource.util;

import java.nio.charset.Charset;
import java.security.Key;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;

/**
 * AES对称加密工具类
 */
public class AESUtil {

    /**
     * 加密
     * @param plaintext
     * @param key
     * @return
     * @throws Exception
     */
    public static String encrypt(String plaintext, String key) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] bytes = cipher.doFinal(plaintext.getBytes());
        String s = Base64.getEncoder().encodeToString(bytes);
        return s;
    }

    /**
     * 解密
     * @param ciphertext
     * @param key
     * @return
     * @throws Exception
     */
    public static String decrypt(String ciphertext, String key) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        byte[] decrypted = Base64.getDecoder().decode(ciphertext);
        byte[] decryptedBytes = cipher.doFinal(decrypted);
        return new String(decryptedBytes);
    }

    public static void main(String[] args) throws Exception {
        String msg = "你好";
        String key = "ThisIsASecretKey";

        String encrypt = encrypt(msg, key);
        System.out.println(encrypt);
        String decrypt = decrypt(encrypt, key);
        System.out.println(decrypt);
    }
}

非对称加密RSA

RSA加密是一种非对称加密。可以在不直接传递密钥的情况下,完成解密。这能够确保信息的安全性,避免了直接传递密钥所造成的被破解的风险。是由一对密钥来进行加解密的过程,分别称为公钥和私钥。两者之间有数学相关,该加密算法的原理就是对于极大整数做因数分解的困难性来保证安全性。通常个人保存私钥,公钥是公开的(可能同时多人持有)。

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @描述 Rsa加密工具类
 * @创建人 yhgh
 * @创建时间 2022/01/22 11:49
 */
public class RsaUtil {


    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;
    /**
     * 加密方式名称
     */
    private static final String ALGORITHM_NAME = "RSA";

    private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();

    private static final  Base64.Decoder BASE64_DECODER = Base64.getDecoder();


    /**
     * 获取密钥对
     */
    private static KeyPair getKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM_NAME);
        generator.initialize(1024);
        return generator.generateKeyPair();
    }

    /**
     * 获取base64加密后密钥对
     */
    public static Map<String, String> getKeyPairMap() throws Exception {
        KeyPair keyPair = getKeyPair();
        String privateKey = new String(BASE64_ENCODER.encode(keyPair.getPrivate().getEncoded()));
        String publicKey = new String(BASE64_ENCODER.encode(keyPair.getPublic().getEncoded()));
        Map<String, String> keyMap = new HashMap<>();
        keyMap.put("privateKey", privateKey);
        keyMap.put("publicKey", publicKey);
        return keyMap;
    }

    /**
     * 获取公钥
     *
     * @param publicKey base64加密的公钥字符串
     */
    private static PublicKey getPublicKey(String publicKey) throws Exception {
        byte[] decodedKey = BASE64_DECODER.decode(publicKey.getBytes());
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);
        return keyFactory.generatePublic(keySpec);
    }



    /**
     * 获取私钥
     *
     * @param privateKey base64加密的私钥字符串
     */
    private static PrivateKey getPrivateKey(String privateKey) throws Exception {
        byte[] decodedKey = BASE64_DECODER.decode(privateKey.getBytes());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * RSA加密
     *
     * @param data      待加密数据
     * @param publicKeyStr 公钥
     */
    public static String encrypt(String data, String publicKeyStr) throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
        cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKeyStr));
        int inputLen = data.getBytes().length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        // 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串
        // 加密后的字符串
        return new String(BASE64_ENCODER.encode(encryptedData));
    }

    /**
     * RSA解密
     *
     * @param data       待解密数据
     * @param privateKeyStr 私钥
     */
    public static String decrypt(String data, String privateKeyStr) throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
        cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKeyStr));
        byte[] dataBytes = BASE64_DECODER.decode(data);
        int inputLen = dataBytes.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        // 解密后的内容
        return new String(decryptedData, StandardCharsets.UTF_8);
    }

    // 测试
    public static void main(String[] args) throws Exception{
        // 获取公钥、私钥
        Map<String, String> keyPairMap = getKeyPairMap();
        String privateKey = keyPairMap.get("privateKey");
        String publicKey = keyPairMap.get("publicKey");
        System.out.println("privateKey = " + privateKey);
        System.out.println("publicKey = " + publicKey);
        // 要加密的字符串
        String data = "admin.123";
        // 使用公钥加密
        String encrypt = encrypt(data, publicKey);
        System.out.println("encrypt = " + encrypt);
        // 使用私钥解密
        String decrypt = decrypt(encrypt, privateKey);
        System.out.println("decrypt = " + decrypt);
    }
}

前端加密

npm install jsencrypt
import JSEncrypt from 'jsencrypt'
// 创建加密对象实例
const encryptor = new JSEncrypt()
const pubKey = ''
// 设置公钥
encryptor.setPublicKey(pubKey)
// 对内容进行加密
const rsaPassWord = encryptor.encrypt('要加密的内容')

哈希算法

由于哈希算法加密的结果不可逆,常见的使用场景如下:

MD5

MD 算法已经不被推荐使用,建议使用更安全的哈希算法比如 SHA-2、Bcrypt。

String originalString = "哈希算法";
// 创建MD5摘要对象
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(originalString.getBytes(StandardCharsets.UTF_8));
// 计算哈希值
byte[] result = messageDigest.digest();
// 将哈希值转换为十六进制字符串
String hexString = new HexBinaryAdapter().marshal(result);

System.out.println("Original String: " + originalString); // Original String: 哈希算法
System.out.println("MD5 Hash: " + hexString.toLowerCase()); // MD5 Hash: 16600f689cdf9ad8305749bd64d3ca32

Bcrypt

基于 Blowfish 加密算法的密码哈希算法,专门为密码加密而设计,安全性高。

在Spring Security中有集成,如果不使用Spring Security,也可以单独引入Bcrypt

<dependency>
    <groupId>org.mindrot</groupId>
    <artifactId>jbcrypt</artifactId>
    <version>0.4</version>
</dependency>
package top.ygang.huijifindresource.utils.captcha;

import org.mindrot.jbcrypt.BCrypt;

/**
 * @Description: Bcrypt加密工具类
 * @Author: yanggang
 * @Date: 
 */
public class BcryptUtil {

    /**
     * 加密密码,自动包含盐值
     * @param plainTextPassword
     * @return
     */
    public static String encryptPassword(String plainTextPassword) {
        return BCrypt.hashpw(plainTextPassword, BCrypt.gensalt());
    }

    /**
     * 验证密码
     * @param plainTextPassword
     * @param storedHash
     * @return
     */
    public static boolean verifyPassword(String plainTextPassword, String storedHash) {
        return BCrypt.checkpw(plainTextPassword, storedHash);
    }

    public static void main(String[] args) {
        String passwd = "abcd.123";
        String encryptPassword = encryptPassword(passwd);
        System.out.println(encryptPassword);
        boolean b = verifyPassword(passwd, encryptPassword);
        System.out.println(b);
    }
}