Java AES在保存并再次解密后解密不同的结果
当我运行scrypt时,我的第一个解密结果是正确的: 兄弟人群意味着古家伙在完美面前要求社会 怒不可遏 当我保存encryptedText、salt(例如,保存到数据库)并想再次解密时,我得到了这个结果 �Ŝ�,&6.���;�M�一个古代人要求社会在完美之前怒目而视 *更新 我认为问题在于我处理数据的方式(salt和iv)。现在我可以加密和保存-encryptText,randomIV,randomSalt到数据库,并用masterPassword对其进行解密而不会出现问题 谢谢大家的帮助,你太棒了 *编辑代码后,我得到了这个解决方案Java AES在保存并再次解密后解密不同的结果,java,encryption,aes,Java,Encryption,Aes,当我运行scrypt时,我的第一个解密结果是正确的: 兄弟人群意味着古家伙在完美面前要求社会 怒不可遏 当我保存encryptedText、salt(例如,保存到数据库)并想再次解密时,我得到了这个结果 �Ŝ�,&6.���;�M�一个古代人要求社会在完美之前怒目而视 *更新 我认为问题在于我处理数据的方式(salt和iv)。现在我可以加密和保存-encryptText,randomIV,randomSalt到数据库,并用masterPassword对其进行解密而不会出现问题 谢谢大家的帮助,你
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.util.Random;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class Aes {
private static int pswdIterations = 65536;
private static int keySize = 256;
public static String encrypt(String plainText, String password, String salt, String initializationVector) throws
NoSuchAlgorithmException,
InvalidKeySpecException,
NoSuchPaddingException,
InvalidParameterSpecException,
IllegalBlockSizeException,
BadPaddingException,
UnsupportedEncodingException,
InvalidKeyException,
InvalidAlgorithmParameterException
{
byte[] saltBytes = salt.getBytes("UTF-8");
byte[] ivBytes = initializationVector.getBytes("UTF-8");
// Derive the key, given password and salt.
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
saltBytes,
pswdIterations,
keySize
);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(ivBytes));
byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
return new Base64().encodeAsString(encryptedTextBytes);
}
public static String decrypt(String encryptedText, String password, String salt, String initializationVector ) throws
NoSuchAlgorithmException,
InvalidKeySpecException,
NoSuchPaddingException,
InvalidKeyException,
InvalidAlgorithmParameterException,
UnsupportedEncodingException
{
byte[] saltBytes = salt.getBytes("UTF-8");
byte[] ivBytes = initializationVector.getBytes("UTF-8");
byte[] encryptedTextBytes = new Base64().decodeBase64(encryptedText);
// Derive the key, given password and salt.
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
saltBytes,
pswdIterations,
keySize
);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
// Decrypt the message, given derived key and initialization vector.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = null;
try {
decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return new String(decryptedTextBytes);
}
public String generateSalt() {
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[16];
random.nextBytes(bytes);
String s = new String(bytes);
return s;
}
public String generateIV(String chars, int length) {
Random rand = new Random();
StringBuilder buf = new StringBuilder();
for (int i=0; i<length; i++) {
buf.append(chars.charAt(rand.nextInt(chars.length())));
}
return buf.toString();
}
}
您的代码可以加密A和解密A,但不能加密A、加密B和解密A。 因为加密A时的salt和初始化向量在加密B时被覆盖。 我不知道你想归档什么。我留下了一个可用的代码示例
import java.security.AlgorithmParameters;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class AESDemo {
private static final String password = "test";
private static String salt;
private static int pswdIterations = 65536;
private static int keySize = 256;
//read from DB
private byte[] ivBytes = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
public String encrypt(String plainText) throws Exception {
// get salt
if (salt == null)
salt = generateSalt();
byte[] saltBytes = salt.getBytes("UTF-8");
// Derive the key
SecretKeyFactory factory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes,
pswdIterations, keySize);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
// encrypt the message
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(ivBytes));
byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
return new Base64().encodeAsString(encryptedTextBytes);
}
@SuppressWarnings("static-access")
public String decrypt(String encryptedText) throws Exception {
byte[] saltBytes = salt.getBytes("UTF-8");
byte[] encryptedTextBytes = new Base64().decodeBase64(encryptedText);
// Derive the key
SecretKeyFactory factory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes,
pswdIterations, keySize);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
// Decrypt the message
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret,
new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = null;
try {
decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return new String(decryptedTextBytes);
}
public String generateSalt() {
//get the salt from DB
return "I hate salt";
}
}
public class stackoverflow_test {
public static void main(String[] ag) throws Exception{
AESDemo d = new AESDemo();
System.out.println("Encrypted string:" + d.encrypt("brother crowd mean guy ancient demand society before perfection glare anger certain"));
String encryptedText = d.encrypt("brother crowd mean guy ancient demand society before perfection glare anger certain");
String encryptedText2 = d.encrypt("Hello World");
System.out.println("Decrypted string:" + d.decrypt(encryptedText2));
System.out.println("Decrypted string:" + d.decrypt(encryptedText));
}
}
结果
Encrypted string:c7NXCBiq7tqzon39iPtkQLJ0chuXudS5oVDPdAr5S3q1245d3uJUVcyNUY77rpGeNvOp9hOldhiOM8mp2C/aOqqNyXx82zJt2V2EFtQkauCl/oY2EMENh1jCR6Nqf1lJ
Decrypted string:Hello World
Decrypted string:brother crowd mean guy ancient demand society before perfection glare anger certain
你的代码有一些不同的问题。首先是你如何产生盐
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);
String s = new String(bytes);
return s;
您正在获取随机数据并试图将其转换为字符串,但字符串构造函数并不只需要任何随机数据,它需要某种编码的文本字符串(UTF-8、UTF-16或其他)。如果对二进制数据进行编码,则只能将其转换为字符串,我只需返回字节
数组即可
public byte[] generateSalt(int length) {
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[length];
random.nextBytes(bytes);
return bytes;
}
接下来,很难说为什么您的输出以您所述的特定方式解密(只有第一个块损坏),但我敢打赌,这与将IV保存到数据库时对其进行错误编码有关。更好的解决方案是在加密数据中预先添加salt和IV,然后在需要解密时将其剥离
public String encrypt(String plainText) throws Exception {
//get salt
byte[] saltBytes = generateSalt(saltLength);
// Derive the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, pswdIterations, keySize);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
//encrypt the message
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
//prepend the salt and IV
byte[] buffer = new byte[saltBytes.length + ivBytes.length + encryptedTextBytes.length];
System.arraycopy(saltBytes, 0, buffer, 0, saltBytes.length);
System.arraycopy(ivBytes, 0, buffer, saltBytes.length, ivBytes.length);
System.arraycopy(encryptedTextBytes, 0, buffer, saltBytes.length + ivBytes.length, encryptedTextBytes.length);
return new Base64().encodeToString(buffer);
}
public String decrypt(String encryptedText) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//strip off the salt and IV
ByteBuffer buffer = ByteBuffer.wrap(new Base64().decode(encryptedText));
byte[] saltBytes = new byte[saltLength];
buffer.get(saltBytes, 0, saltBytes.length);
byte[] ivBytes = new byte[cipher.getBlockSize()];
buffer.get(ivBytes, 0, ivBytes.length);
byte[] encryptedTextBytes = new byte[buffer.capacity() - saltBytes.length - ivBytes.length];
buffer.get(encryptedTextBytes);
// Derive the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, pswdIterations, keySize);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = null;
try {
decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return new String(decryptedTextBytes);
}
每次加密一段数据时使用新的IV非常重要,因为它似乎存在编码问题。请尝试UTF-16。如果只使用普通ASCII字符,则不会出现这种情况。数据库使用什么编码?保存或检索数据时似乎存在问题。你能显示相应的代码吗?即使我不把它保存到数据库,它也有这个问题。当我从控制台中取出salt字符串和加密字符串并解密时,它也会显示无法读取的内容^^^但是为什么80%的文本都可以,只是开头不可以?你应该一次生成salt和IV,保密,在做新的加密时,并没有同时生成这两个。是的,我的注意力是生成一个随机的salt和iv。在上面的解决方案中修复了它。谢谢。但有了你的解决方案,我无法做到这一点。System.out.println(“解密字符串:+d.decrypt(06scBq7GNv+jjJPpsJdOm+1mIdaWrk/SH2LO5YIWVAQYIFO6GMQ8ANULOYLWLJQJW4NUUXRVRVLN5YP/OikicB9H/DMU7TGZ5PPV8YPQTOYDK823MEOE5N0T084CWV)”;例如,我想将加密和种子保存在db中,而不是解密。我修改了代码,使IV和salt在代码中是一个常量。这样,您只需要将加密文本保存在db中,当然也可以将IV和salt保存在db中,并在代码启动时读取。@tom87416使用固定IV是一种安全措施,因为它会打开known文本攻击和使用固定的salt(通常)会使您容易受到rainbow表的攻击。
public String encrypt(String plainText) throws Exception {
//get salt
byte[] saltBytes = generateSalt(saltLength);
// Derive the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, pswdIterations, keySize);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
//encrypt the message
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
//prepend the salt and IV
byte[] buffer = new byte[saltBytes.length + ivBytes.length + encryptedTextBytes.length];
System.arraycopy(saltBytes, 0, buffer, 0, saltBytes.length);
System.arraycopy(ivBytes, 0, buffer, saltBytes.length, ivBytes.length);
System.arraycopy(encryptedTextBytes, 0, buffer, saltBytes.length + ivBytes.length, encryptedTextBytes.length);
return new Base64().encodeToString(buffer);
}
public String decrypt(String encryptedText) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//strip off the salt and IV
ByteBuffer buffer = ByteBuffer.wrap(new Base64().decode(encryptedText));
byte[] saltBytes = new byte[saltLength];
buffer.get(saltBytes, 0, saltBytes.length);
byte[] ivBytes = new byte[cipher.getBlockSize()];
buffer.get(ivBytes, 0, ivBytes.length);
byte[] encryptedTextBytes = new byte[buffer.capacity() - saltBytes.length - ivBytes.length];
buffer.get(encryptedTextBytes);
// Derive the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), saltBytes, pswdIterations, keySize);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = null;
try {
decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return new String(decryptedTextBytes);
}