Java中如何实现AES静态加密:相同明文生成固定密文?
问题描述
需要将文本加密后存储在Excel表格中,Java代码读取加密文本解密为明文,再用明文请求服务器。使用AES加解密逻辑时,相同明文每次加密得到的密文不同(虽可正常解密),但需求是相同静态明文对应固定密文,以便预存后能正常解密。
示例:
Enter
This is a question
Encrypted Data : mFsue8JGwLcJQTiBzM0HLVvdDXKPNGsG/O7N60joH+Ozgg==
Decrypted Data : This is a questionEnter
This is a question
Encrypted Data : FdBz3cGS4NphK14Fw8Me4daM4lVzdrK47WUMSRiUVe+juQ==
Decrypted Data : This is a question
以下是使用的代码(逻辑一致,可能包含未使用变量和方法):
Method.java
import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; public class Method { private static SecretKey key; private final int KEY_SIZE = 128; private final int DATA_LENGTH = 128; private Cipher encryptionCipher; /* * public void init() throws Exception { KeyGenerator keyGenerator = * KeyGenerator.getInstance("AES"); keyGenerator.init(KEY_SIZE); key = * keyGenerator.generateKey(); * * * } */ // String to Key public static void getKeyFromPassword(String toEnc, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException { SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); KeySpec spec = new PBEKeySpec(toEnc.toCharArray(), salt.getBytes(), 65536, 128); SecretKey originalKey = new SecretKeySpec(factory.generateSecret(spec).getEncoded(),"AES"); key= originalKey; } /* * public static void convertStringToSecretKeyto(String string) { * * * byte[] bytesEncoded = Base64.getEncoder().encode(string.getBytes()); byte[] * decodedKey = Base64.getDecoder().decode(string); key = new * SecretKeySpec(decodedKey, 0, decodedKey.length, "AES"); * System.out.println(bytesEncoded); System.out.println(decodedKey); * * } */ public String encrypt(String data) throws Exception { byte[] dataInBytes = data.getBytes(); encryptionCipher = Cipher.getInstance("AES/GCM/NoPadding"); encryptionCipher.init(Cipher.ENCRYPT_MODE, key); byte[] encryptedBytes = encryptionCipher.doFinal(dataInBytes); return encode(encryptedBytes); } public String decrypt(String encryptedData) throws Exception { byte[] dataInBytes = decode(encryptedData); Cipher decryptionCipher = Cipher.getInstance("AES/GCM/NoPadding"); GCMParameterSpec spec = new GCMParameterSpec(DATA_LENGTH, encryptionCipher.getIV()); decryptionCipher.init(Cipher.DECRYPT_MODE, key, spec); byte[] decryptedBytes = decryptionCipher.doFinal(dataInBytes); return new String(decryptedBytes); } private String encode(byte[] data) { return Base64.getEncoder().encodeToString(data); } private byte[] decode(String data) { return Base64.getDecoder().decode(data); } }
Main.java
import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; import java.util.Base64; import java.util.Scanner; public class Cypher { public static void main(String[] args) { // TODO Auto-generated method stub try { Method aes_encryption = new Method(); // aes_encryption.init(); Method.getKeyFromPassword("Texty text","Salty salt"); System.out.println("Enter"); Scanner sc= new Scanner(System.in); String s= sc.nextLine(); String encryptedData = aes_encryption.encrypt(s); String decryptedData = aes_encryption.decrypt(encryptedData); System.out.println("Encrypted Data : " + encryptedData); System.out.println("Decrypted Data : " + decryptedData); } catch (Exception ignored) { } } }
解决方案
密文不同的核心原因是AES/GCM模式默认会生成随机初始化向量(IV),每次加密的IV不同,导致相同明文和密钥生成的密文也不同。要实现固定密文,需指定固定的IV:
修改思路
- 定义一个固定的IV(GCM模式推荐IV长度为12字节,可自行生成固定字节数组)
- 加密时手动传入该固定IV初始化Cipher
- 解密时使用相同的固定IV进行初始化
修改后的核心代码示例
// 在Method类中添加固定IV(示例值,可自行替换为12字节的固定数组) private final byte[] FIXED_IV = "abcdefghijkl".getBytes(); public String encrypt(String data) throws Exception { byte[] dataInBytes = data.getBytes(); encryptionCipher = Cipher.getInstance("AES/GCM/NoPadding"); // 使用固定IV初始化加密Cipher GCMParameterSpec spec = new GCMParameterSpec(DATA_LENGTH, FIXED_IV); encryptionCipher.init(Cipher.ENCRYPT_MODE, key, spec); byte[] encryptedBytes = encryptionCipher.doFinal(dataInBytes); return encode(encryptedBytes); } public String decrypt(String encryptedData) throws Exception { byte[] dataInBytes = decode(encryptedData); Cipher decryptionCipher = Cipher.getInstance("AES/GCM/NoPadding"); // 解密时使用相同的固定IV GCMParameterSpec spec = new GCMParameterSpec(DATA_LENGTH, FIXED_IV); decryptionCipher.init(Cipher.DECRYPT_MODE, key, spec); byte[] decryptedBytes = decryptionCipher.doFinal(dataInBytes); return new String(decryptedBytes); }
注意事项
- 固定IV会让相同明文+密钥的密文固定,但会失去语义安全性,攻击者可通过密文重复判断明文重复,仅适用于预存固定明文的场景,请勿用于动态加密场景
- 确保密钥和IV的安全性,避免硬编码在代码中,建议通过配置文件等方式管理
- GCM模式的IV推荐长度为12字节,避免使用过短或过长的IV
内容的提问来源于stack exchange,提问作者Vaibhav
相关产品推荐
相关产品推荐

