无需公钥的无损加密解密
Java 实现方案:通过自定义编码(Base64)+ 时间戳动态密钥 + AES 对称加密,实现无需公钥的无损加密解密。核心逻辑是利用时间戳生成动态密钥,加密和解密时通过相同的时间戳验证和密钥生成规则确保数据一致性
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;public class TimeBasedCrypto {// 固定密钥(实际使用中需安全存储,如配置中心)private static final String FIXED_KEY = "your-secure-fixed-key-123";// 时间戳容忍范围(毫秒),防止时钟偏差导致解密失败private static final long TIMESTAMP_TOLERANCE = 5 * 60 * 1000; // 5分钟/*** 加密流程:参数编码 -> 生成时间戳密钥 -> AES加密* @param originalParam 原始参数(明文)* @return 加密后的数据(格式:Base64(加密内容):时间戳)*/public static String encrypt(String originalParam) throws Exception {// 1. 对原始参数进行Base64编码(确保二进制安全传输)String encodedParam = Base64.getEncoder().encodeToString(originalParam.getBytes(StandardCharsets.UTF_8));// 2. 生成当前时间戳(毫秒)long timestamp = System.currentTimeMillis();// 3. 基于时间戳和固定密钥生成AES密钥(AES密钥长度需为16/24/32字节)SecretKeySpec secretKey = generateKey(timestamp);// 4. AES加密编码后的参数Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");cipher.init(Cipher.ENCRYPT_MODE, secretKey);byte[] encryptedBytes = cipher.doFinal(encodedParam.getBytes(StandardCharsets.UTF_8));// 5. 加密内容Base64编码,与时间戳拼接返回(格式:加密内容:时间戳)String encryptedContent = Base64.getEncoder().encodeToString(encryptedBytes);return encryptedContent + ":" + timestamp;}/*** 解密流程:解析时间戳 -> 验证有效性 -> 生成密钥 -> AES解密 -> 解码参数* @param encryptedData 加密后的数据(格式:Base64(加密内容):时间戳)* @return 原始参数(明文)*/public static String decrypt(String encryptedData) throws Exception {// 1. 拆分加密内容和时间戳String[] parts = encryptedData.split(":", 2);if (parts.length != 2) {throw new IllegalArgumentException("无效的加密数据格式");}String encryptedContent = parts[0];long timestamp = Long.parseLong(parts[1]);// 2. 验证时间戳有效性(是否在容忍范围内)long currentTime = System.currentTimeMillis();if (Math.abs(currentTime - timestamp) > TIMESTAMP_TOLERANCE) {throw new SecurityException("时间戳过期或无效(超出" + TIMESTAMP_TOLERANCE + "毫秒)");}// 3. 基于相同时间戳生成解密密钥SecretKeySpec secretKey = generateKey(timestamp);// 4. AES解密Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");cipher.init(Cipher.DECRYPT_MODE, secretKey);byte[] decodedBytes = Base64.getDecoder().decode(encryptedContent);byte[] decryptedBytes = cipher.doFinal(decodedBytes);String encodedParam = new String(decryptedBytes, StandardCharsets.UTF_8);// 5. Base64解码,恢复原始参数return new String(Base64.getDecoder().decode(encodedParam), StandardCharsets.UTF_8);}/*** 生成AES密钥:时间戳+固定密钥哈希后取前16字节(AES-128)*/private static SecretKeySpec generateKey(long timestamp) throws Exception {// 拼接时间戳和固定密钥String keyMaterial = timestamp + ":" + FIXED_KEY;// SHA-256哈希确保密钥随机性,取前16字节作为AES-128密钥MessageDigest digest = MessageDigest.getInstance("SHA-256");byte[] keyBytes = digest.digest(keyMaterial.getBytes(StandardCharsets.UTF_8));byte[] aesKey = new byte[16];System.arraycopy(keyBytes, 0, aesKey, 0, 16); // 截取前16字节return new SecretKeySpec(aesKey, "AES");}// 测试示例public static void main(String[] args) throws Exception {// 原始数据String original = "这是一段需要加密的数据:123456";System.out.println("原始数据:" + original);// 加密String encrypted = encrypt(original);System.out.println("加密后:" + encrypted);// 解密String decrypted = decrypt(encrypted);System.out.println("解密后:" + decrypted);// 验证无损性System.out.println("解密是否无损:" + original.equals(decrypted));}
}
核心逻辑说明
编码层:使用 Base64 编码原始参数,避免二进制数据在传输中被破坏(如 HTTP 请求参数、JSON 字段)。
密钥生成:
- 加密时,将当前时间戳与固定密钥拼接,通过 SHA-256 哈希生成 32 字节摘要,取前 16 字节作为 AES-128 密钥(确保密钥长度符合 AES 要求)。
- 解密时,使用加密数据中携带的时间戳,按相同规则重新生成密钥,无需公钥即可解密。
时间戳验证:解密时检查时间戳是否在容忍范围内(如 5 分钟),防止过期数据被重复使用(类似防重放机制)。
加密算法:采用 AES 对称加密(ECB 模式 + PKCS5 填充),加密解密使用同一密钥,确保无损解密(前提是密钥一致)。
注意事项
安全性:
- 固定密钥(
FIXED_KEY
)需严格保密,建议通过安全配置中心管理,避免硬编码。 - ECB 模式安全性较低(相同明文加密后结果相同),生产环境可改用 CBC/GCM 模式(需额外处理 IV 向量)。
- 时间戳容忍范围需根据业务场景调整(过短可能因时钟偏差解密失败,过长增加重放风险)。
- 固定密钥(
无损性保证:Base64 编码和解码、AES 加密和解密均为可逆操作,只要密钥正确,解密后的数据与原始数据完全一致。
适用场景:适合短期有效的数据传输(如 API 请求签名、临时令牌),不适合长期存储加密(时间戳过期后无法解密)。
通过这种方式,无需公钥体系即可实现数据的安全加密和解密,且时间戳的引入增加了密钥的动态性,提升了安全性。