1. 项目概述:为什么我们需要一个加密工具类?

在Java后端开发里,数据安全是个绕不开的话题。无论是用户密码的存储、接口参数的防篡改,还是敏感数据的传输,你总得和加密算法打交道。我见过不少项目,加密代码散落在各个角落:用户服务里用MD5哈希密码,支付模块里用RSA签名,配置文件里又用AES解密数据库连接串。每次要用,要么去网上搜一段代码复制粘贴,要么翻找以前的项目“考古”。这不仅效率低下,更危险的是,由于实现方式不统一、参数配置随意,很容易埋下安全漏洞的种子。

这个“Java加密算法工具类”项目,就是为了解决这个痛点。它不是一个简单的API调用示例,而是一个旨在将RSA和MD5这两种最常用、也最具代表性的加密算法,封装成健壮、易用、符合生产环境要求的工具类。RSA代表了非对称加密的典型,用于密钥交换、数字签名;MD5则是消息摘要算法的代表(尽管已不推荐用于密码存储,但在文件校验、数据指纹等场景仍有其价值)。把它们俩摸透,你就能掌握加密领域一大半的核心思想。

对于开发者而言,这个工具类能带来几个实实在在的好处:一是 开箱即用 ,无需再为密钥对生成、填充模式、字符编码这些细节头疼;二是 统一规范 ,确保整个项目使用同一套安全标准和异常处理逻辑;三是 便于维护 ,算法升级或漏洞修复只需改动工具类一处。接下来,我会从设计思路开始,带你一步步拆解并实现这个工具类,并分享我在实际项目中积累的诸多“踩坑”经验。

2. 核心设计思路与架构选型

在动手写代码之前,好的设计能避免后期大量的重构。一个合格的加密工具类,不能只是 Cipher.getInstance("RSA") 的简单包装。

2.1 设计目标与原则

首先明确我们的工具类要达成什么目标:

  1. 安全性优先 :这是加密组件的生命线。必须使用经过验证的、强度足够的算法和参数(如RSA密钥长度至少2048位,不使用ECB模式等)。
  2. 接口友好 :对外暴露的方法应该简单直观。例如, encrypt(String data, String publicKey) decrypt(String encryptedData, String privateKey) 。内部复杂的密钥处理、异常转换应由工具类消化。
  3. 灵活性 :虽然封装,但不死板。应支持自定义密钥长度、字符集、填充模式等(通过重载方法或配置项),以适应不同场景。
  4. 健壮性 :充分考虑异常情况。输入为空怎么办?密钥格式错误怎么办?密文被篡改怎么办?必须有清晰的异常提示和日志记录。
  5. 性能考量 :非对称加密(RSA)非常耗时,不适合加密大数据。工具类应在设计上就提醒或限制这一点,或者提供“分段加密”的参考方案。

基于这些目标,我决定采用“静态工具类”的形式,因为加密操作通常是无状态的。核心功能划分为两个部分: RSAUtil MD5Util ,它们可以独立使用,也可以通过一个统一的 CryptoUtils 门面类来调用,这取决于项目的复杂程度。

2.2 技术栈与依赖选择

这是一个纯工具类项目,原则上应 零依赖 ,只使用JDK标准库。这能保证其最大的可移植性和兼容性。

  • 核心包 java.security (提供 KeyPairGenerator , Cipher , MessageDigest ), java.util.Base64 (用于密钥和密文的编码解码,替代旧的 sun.misc.BASE64Encoder )。
  • 密钥存储 :RSA的公私钥通常以PEM格式( -----BEGIN PUBLIC KEY----- )或Base64字符串形式存储和传输。我们会实现密钥与字符串的相互转换。
  • 关于第三方库 :有同学可能会想到Bouncy Castle(BC)。BC确实提供了更多算法和更灵活的选项。但在本工具类中,我们优先使用JCE(Java Cryptography Extension)的标准实现,以保持轻量。我们会在代码中预留扩展点,注明“如需使用BC,可在此处替换 Cipher.getInstance("RSA/ECB/PKCS1Padding") Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC") ”。

注意 :在JDK 8及更高版本中, java.util.Base64 是首选。绝对不要使用 sun.misc.BASE64Encoder ,它是Sun的私有API,不同JDK版本可能行为不一致,且未来可能被移除。

3. RSA非对称加密工具类实现详解

RSA是三位科学家姓氏的缩写,它的安全性基于大数分解的难题。简单说,就是一对密钥:公钥加密,私钥解密;或者私钥签名,公钥验签。

3.1 密钥对的生成与管理

密钥对是RSA的起点。生成密钥对时,密钥长度是关键参数。

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

public class RSAKeyGenerator {
    /**
     * 生成RSA密钥对
     * @param keySize 密钥长度,推荐2048或以上
     * @return 包含公钥和私钥的KeyPair对象
     */
    public static KeyPair genKeyPair(int keySize) {
        try {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
            // 初始化密钥生成器,指定长度和随机源
            keyPairGen.initialize(keySize, new SecureRandom());
            return keyPairGen.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前JRE环境不支持RSA算法", e);
        }
    }
}

关键点解析

  1. 算法名称 "RSA" 是标准名称。
  2. 密钥长度 keySize 。1024位已被认为不安全, 生产环境务必使用2048位或4096位 。长度越长越安全,但生成和使用速度越慢。
  3. 随机源 SecureRandom 用于提供密码学强度的随机数,这很重要。使用默认的 new SecureRandom() 即可。

生成密钥对后,我们通常需要将它们转换成字符串(如Base64)以便存储或在网络上传输。

import java.util.Base64;

public class RSAUtil {
    /**
     * 将公钥对象转换为Base64字符串
     */
    public static String getPublicKeyStr(RSAPublicKey publicKey) {
        return Base64.getEncoder().encodeToString(publicKey.getEncoded());
    }

    /**
     * 将私钥对象转换为Base64字符串
     */
    public static String getPrivateKeyStr(RSAPrivateKey privateKey) {
        return Base64.getEncoder().encodeToString(privateKey.getEncoded());
    }

    /**
     * 从Base64字符串还原公钥对象(此处省略具体代码,涉及KeyFactory和X509EncodedKeySpec)
     */
    public static RSAPublicKey getPublicKey(String publicKeyStr) throws Exception { ... }

    /**
     * 从Base64字符串还原私钥对象(涉及PKCS8EncodedKeySpec)
     */
    public static RSAPrivateKey getPrivateKey(String privateKeyStr) throws Exception { ... }
}

实操心得 :密钥的格式( X509EncodedKeySpec 对应公钥, PKCS8EncodedKeySpec 对应私钥)很容易搞混。一个记忆方法是:公钥通常遵循X.509标准,而私钥常用PKCS#8标准封装。转换时用错了 KeySpec ,就会抛出 InvalidKeySpecException

3.2 加密与解密的核心流程

有了密钥,就可以进行加密解密了。RSA加密有长度限制,加密的数据长度不能超过密钥长度(例如2048位是256字节),还要减去填充占用的字节数(如PKCS1Padding占用11字节)。所以 RSA不适合直接加密大文件 ,通常用来加密一个对称加密算法(如AES)的密钥。

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;

public class RSAUtil {
    private static final String TRANSFORMATION = "RSA/ECB/PKCS1Padding";

    /**
     * 公钥加密
     * @param data 明文
     * @param publicKeyStr Base64编码的公钥字符串
     * @return Base64编码的密文
     */
    public static String encrypt(String data, String publicKeyStr) throws Exception {
        // 1. 参数校验
        if (data == null || publicKeyStr == null) {
            throw new IllegalArgumentException("加密数据和公钥不可为空");
        }
        // 2. 还原公钥对象
        RSAPublicKey publicKey = getPublicKey(publicKeyStr);
        // 3. 获取Cipher实例并初始化
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        // 4. 执行加密(注意编码)
        byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
        byte[] encryptedBytes = cipher.doFinal(dataBytes);
        // 5. 返回Base64密文
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    /**
     * 私钥解密
     * @param encryptedDataStr Base64编码的密文
     * @param privateKeyStr Base64编码的私钥字符串
     * @return 明文
     */
    public static String decrypt(String encryptedDataStr, String privateKeyStr) throws Exception {
        // 1. 参数校验
        if (encryptedDataStr == null || privateKeyStr == null) {
            throw new IllegalArgumentException("密文和私钥不可为空");
        }
        // 2. 还原私钥对象
        RSAPrivateKey privateKey = getPrivateKey(privateKeyStr);
        // 3. 获取Cipher实例并初始化
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        // 4. 解码Base64并执行解密
        byte[] encryptedBytes = Base64.getDecoder().decode(encryptedDataStr);
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
        // 5. 返回明文
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }
}

关键点解析

  1. TRANSFORMATION "RSA/ECB/PKCS1Padding" 。这是最常用的组合。
    • RSA :算法。
    • ECB :加密模式。对于非对称加密,由于每次加密的数据块很小,ECB模式是可以接受的,且是标准写法。
    • PKCS1Padding :填充方案。这是RSA的标准填充,能增加安全性。 绝对不要使用 NoPadding ,那是不安全的。
  2. 字符编码 :在 getBytes() new String() 时, 必须明确指定字符集 ,如 StandardCharsets.UTF_8 。使用平台默认编码(如 getBytes() )是导致跨系统乱码的常见原因。
  3. 异常处理 Cipher.doFinal() 可能抛出 BadPaddingException IllegalBlockSizeException 等。在工具类中,我们选择抛出 Exception ,让调用方根据业务决定如何处理(如记录日志、返回错误信息)。在生产代码中,最好定义自定义的加密异常类进行包装。

3.3 分段加密解密的实现

当明文超过限制时,必须分段处理。这是一个进阶但非常重要的功能。

public class RSAUtil {
    private static final int MAX_ENCRYPT_BLOCK = 245; // 2048位密钥,PKCS1Padding下,加密块最大245字节
    private static final int MAX_DECRYPT_BLOCK = 256; // 解密块固定为密钥长度/8

    public static String encryptLongText(String data, String publicKeyStr) throws Exception {
        RSAPublicKey publicKey = getPublicKey(publicKeyStr);
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);

        byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
        int inputLen = dataBytes.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(dataBytes, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(dataBytes, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return Base64.getEncoder().encodeToString(encryptedData);
    }
    // 分段解密方法decryptLongText实现逻辑类似,略
}

计算过程 :为什么 MAX_ENCRYPT_BLOCK 是245?对于2048位(256字节)的RSA密钥,使用PKCS1Padding填充时,填充部分至少占用11字节(这是规范定义的),所以留给数据的空间是 256 - 11 = 245 字节。解密时,密文块长度就是256字节。

注意事项 :即使实现了分段加密,RSA加密大量数据的性能依然很差。正确的做法是: 用RSA加密一个随机生成的AES密钥(会话密钥),然后用这个AES密钥去加密实际的大数据 。这就是典型的“混合加密”系统,兼具非对称加密的安全性和对称加密的效率。

4. MD5消息摘要工具类实现详解

MD5是一种广泛使用的哈希函数,它能将任意长度的数据映射为一个固定长度(128位,16字节)的“指纹”或“摘要”。它是 单向的 ,即无法从摘要反推原始数据。注意,MD5因其碰撞漏洞(两个不同的数据产生相同的摘要)已不推荐用于密码存储等安全场景,但在文件完整性校验、生成唯一标识等非抗碰撞场景仍有价值。

4.1 基础MD5哈希实现

JDK中实现MD5非常简单。

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Util {
    private static final String ALGORITHM = "MD5";

    /**
     * 生成字符串的MD5摘要(32位小写十六进制形式)
     * @param input 输入字符串
     * @return 32位小写MD5字符串
     */
    public static String md5(String input) {
        if (input == null) {
            return null;
        }
        try {
            MessageDigest md = MessageDigest.getInstance(ALGORITHM);
            byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            // 将byte数组转换为16进制字符串
            return bytesToHex(digest);
        } catch (NoSuchAlgorithmException e) {
            // 理论上MD5是JRE标准算法,不会抛出此异常
            throw new RuntimeException(e);
        }
    }

    /**
     * 将byte数组转换为16进制字符串(小写)
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
}

关键点解析

  1. MessageDigest.getInstance("MD5") :获取MD5算法实例。
  2. md.digest(bytes) :计算摘要,返回16字节的 byte[]
  3. bytesToHex :一个非常实用的工具方法,将字节数组转换成我们常见的32位十六进制字符串。 0xff & b 是为了将byte转换为无符号整数。

4.2 加盐(Salt)与迭代哈希

直接对密码进行MD5哈希是极不安全的,因为彩虹表可以快速破解。 加盐 是必须的。盐是一个随机生成的、足够长的字符串,与密码拼接后再哈希。即使两个用户密码相同,由于盐不同,哈希值也不同。

public class MD5Util {
    /**
     * 生成随机的盐值(Base64编码)
     * @param length 盐的字节长度,推荐至少16字节
     */
    public static String generateSalt(int length) {
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[length];
        random.nextBytes(salt);
        return Base64.getEncoder().encodeToString(salt);
    }

    /**
     * 使用盐对密码进行MD5哈希
     * @param password 原始密码
     * @param salt Base64编码的盐值
     * @return 加盐后的MD5哈希值
     */
    public static String md5WithSalt(String password, String salt) {
        String combined = password + salt; // 或使用更安全的拼接方式,如 password + ":" + salt
        return md5(combined);
    }

    /**
     * 迭代哈希(Key Stretching),增加暴力破解成本
     * @param password 密码
     * @param salt 盐
     * @param iterations 迭代次数,推荐1000次以上
     * @return 最终哈希值
     */
    public static String md5Iterative(String password, String salt, int iterations) {
        String hash = password + salt;
        for (int i = 0; i < iterations; i++) {
            hash = md5(hash); // 每次对上一次的结果进行哈希
        }
        return hash;
    }
}

安全建议

  1. 盐的长度 :至少16字节(128位)。
  2. 盐的存储 :盐必须和哈希值一起存储在数据库中。每个用户应有独立的盐。
  3. 迭代次数 :通过多次哈希(如1000次、10000次)可以显著增加攻击者的计算成本。这被称为“密钥延伸”(Key Stretching)。
  4. 更好的选择 :对于现代密码存储, 强烈推荐使用BCrypt、SCrypt或Argon2 等专门的密码哈希函数。它们内置了盐和成本因子(迭代次数的概念),能更好地抵御硬件(GPU、ASIC)暴力破解。MD5加盐迭代只是一种原理演示和遗留系统兼容方案。

4.3 文件MD5校验

MD5另一个经典用途是校验文件完整性,比如下载文件后计算其MD5与官方提供的值比对。

public class MD5Util {
    /**
     * 计算文件的MD5值
     * @param file 目标文件
     * @return 文件的MD5摘要字符串
     * @throws IOException
     */
    public static String md5File(File file) throws IOException {
        try (FileInputStream fis = new FileInputStream(file);
             DigestInputStream dis = new DigestInputStream(fis, MessageDigest.getInstance(ALGORITHM))) {
            // 读取文件流,DigestInputStream会自动更新摘要
            byte[] buffer = new byte[8192]; // 8KB缓冲区
            while (dis.read(buffer) != -1) {
                // 只需读取,摘要计算由dis自动完成
            }
            MessageDigest md = dis.getMessageDigest();
            byte[] digest = md.digest();
            return bytesToHex(digest);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}

技巧 :使用 DigestInputStream 可以非常优雅地在读取文件的同时计算摘要,无需手动分块更新 MessageDigest 。缓冲区大小(如8192字节)可以根据实际情况调整。

5. 工具类的整合与高级功能

将RSA和MD5的功能整合到一个协调的工具类中,并提供一些生产环境中常用的高级功能。

5.1 统一门面与配置化

我们可以创建一个 CryptoUtils 作为门面,内部委托给 RSAUtil MD5Util 。同时,通过一个 CryptoConfig 类来集中管理配置,如默认密钥长度、字符集、是否启用分段加密等。

public class CryptoConfig {
    public static final String DEFAULT_CHARSET = "UTF-8";
    public static final int DEFAULT_RSA_KEY_SIZE = 2048;
    public static final boolean ENABLE_RSA_SEGMENT = true;
    // 可以读取properties文件来覆盖这些默认值
}

public final class CryptoUtils {
    // 私有构造器,防止实例化
    private CryptoUtils() {}

    // RSA 快捷方法
    public static String rsaEncrypt(String data, String publicKey) throws Exception {
        return RSAUtil.encrypt(data, publicKey);
    }
    public static String rsaDecrypt(String data, String privateKey) throws Exception {
        return RSAUtil.decrypt(data, privateKey);
    }
    public static KeyPair rsaGenKeyPair() {
        return RSAKeyGenerator.genKeyPair(CryptoConfig.DEFAULT_RSA_KEY_SIZE);
    }

    // MD5 快捷方法
    public static String md5(String data) {
        return MD5Util.md5(data);
    }
    public static String md5WithSalt(String data, String salt) {
        return MD5Util.md5WithSalt(data, salt);
    }
    public static String md5File(String filePath) throws IOException {
        return MD5Util.md5File(new File(filePath));
    }
}

5.2 RSA签名与验签

除了加密,RSA另一个核心用途是 数字签名 ,用于验证数据的完整性和来源真实性。发送方用私钥签名,接收方用公钥验签。

public class RSAUtil {
    private static final String SIGN_ALGORITHM = "SHA256withRSA";

    /**
     * 用私钥对数据进行签名
     * @param data 原始数据
     * @param privateKeyStr 私钥字符串
     * @return Base64编码的签名
     */
    public static String sign(String data, String privateKeyStr) throws Exception {
        RSAPrivateKey privateKey = getPrivateKey(privateKeyStr);
        Signature signature = Signature.getInstance(SIGN_ALGORITHM);
        signature.initSign(privateKey);
        signature.update(data.getBytes(StandardCharsets.UTF_8));
        byte[] signBytes = signature.sign();
        return Base64.getEncoder().encodeToString(signBytes);
    }

    /**
     * 用公钥验证签名
     * @param data 原始数据
     * @param signStr 签名字符串
     * @param publicKeyStr 公钥字符串
     * @return 验签是否通过
     */
    public static boolean verify(String data, String signStr, String publicKeyStr) throws Exception {
        RSAPublicKey publicKey = getPublicKey(publicKeyStr);
        Signature signature = Signature.getInstance(SIGN_ALGORITHM);
        signature.initVerify(publicKey);
        signature.update(data.getBytes(StandardCharsets.UTF_8));
        byte[] signBytes = Base64.getDecoder().decode(signStr);
        return signature.verify(signBytes);
    }
}

关键点解析

  1. 签名算法 "SHA256withRSA" 。这表示先对数据用SHA-256计算摘要,再用RSA私钥对摘要进行加密(即签名)。 SHA1withRSA 已逐渐被淘汰,推荐使用 SHA256withRSA SHA384withRSA
  2. 过程 :签名是 私钥 加密摘要;验签是 公钥 解密签名得到摘要,再与计算出的数据摘要比对。
  3. 应用场景 :API接口防篡改。客户端用私钥对请求参数签名,服务器用公钥验签,确保参数在传输过程中未被修改。

6. 常见问题、性能调优与安全实践

工具类写好了,但在实际使用中会遇到各种问题。下面是我总结的一些典型场景和解决方案。

6.1 常见异常与排查表

异常信息 可能原因 解决方案
javax.crypto.BadPaddingException: Decryption error 1. 用错了密钥(如用公钥解密)。
2. 密文在传输过程中被损坏或篡改。
3. 加密和解密使用的填充模式不一致。
1. 确认加密用公钥,解密用私钥。
2. 检查Base64编解码过程,确保密文完整。
3. 确保两端 TRANSFORMATION 字符串完全一致。
java.security.InvalidKeyException 1. 密钥字符串格式错误,不是有效的Base64或PEM。
2. 密钥类型不匹配(如将DSA密钥用于RSA)。
3. 密钥长度不符合算法要求。
1. 检查密钥来源,确保正确解码。
2. 确认密钥对是由 KeyPairGenerator.getInstance("RSA") 生成的。
3. 使用 key.getAlgorithm() 检查密钥算法。
IllegalBlockSizeException 1. 加密的数据长度超过了 MAX_ENCRYPT_BLOCK
2. 解密的数据长度不是密钥长度的整数倍。
1. 启用分段加密功能 encryptLongText
2. 检查密文是否完整,Base64解码是否正确。
NoSuchAlgorithmException 当前JRE环境没有提供指定的算法实现。 1. 检查算法名拼写(如“RSA”全大写)。
2. 对于某些算法(如某些JCE未提供的),可能需要安装Bouncy Castle等Provider。
MD5结果与其他工具不一致 1. 字符编码不一致(如UTF-8 vs GBK)。
2. 输入字符串包含不可见字符(如换行符、空格)。
3. 其他工具可能输出了大写十六进制或Base64格式。
1. 统一使用UTF-8编码。
2. 打印或调试输入字符串的字节数组进行比对。
3. 确认输出格式,我们的工具输出的是 32位小写 十六进制。

6.2 性能优化建议

  1. RSA密钥复用 :RSA密钥对的生成非常耗时。 绝对不要在每次加密/解密时都生成新密钥对 。应在系统初始化时生成并妥善保存(如放到配置文件、数据库或密钥管理服务中),后续直接加载使用。
  2. Cipher实例复用 Cipher.getInstance() 也有一定开销。在高并发场景下,可以考虑使用ThreadLocal或对象池来缓存已初始化的Cipher实例。但要注意线程安全,每个Cipher实例通常不是线程安全的。
  3. 混合加密体系 :重申一遍,处理大量数据时,采用“RSA加密AES密钥,AES加密数据”的混合模式。RSA只承担密钥交换的职责。
  4. 签名验证优化 :对于验签操作,如果公钥是固定的,可以将 Signature 实例初始化( initVerify )的部分缓存起来。

6.3 安全增强实践

  1. 密钥管理 :私钥的安全是重中之重。 永远不要将私钥硬编码在代码或提交到版本库 。应使用环境变量、配置中心(如Apollo、Nacos)、或专业的密钥管理服务(KMS)来存储。公钥可以相对公开。
  2. 算法升级 :关注安全动态。如果未来RSA 2048位被证明不安全,需要平滑升级到4096位。我们的工具类应通过配置来指定密钥长度,便于全局升级。
  3. 废弃MD5用于密码 :在新系统中,密码存储请使用BCrypt、SCrypt或Argon2。如果必须处理遗留的MD5密码,应在用户下次登录时,将其迁移到新的哈希算法。
  4. 使用HTTPS :网络传输层的加密应由TLS/SSL(即HTTPS)保证。应用层的RSA加密主要用于额外的签名验证或加密特别敏感的参数,而不是替代HTTPS。

我个人在多个微服务项目中部署过类似的加密工具包。最大的体会是, 约定大于配置 。通过团队规范,明确哪些场景用RSA签名,哪些用AES加密,密码用什么算法哈希,并统一使用这个工具类,能极大减少沟通成本和安全隐患。最后一个小技巧:为你的工具类编写详尽的单元测试,覆盖正常流程、异常边界、性能基准,这是保证其长期稳定运行的最有效手段。

更多推荐