当我运行scrypt时,我的第一个解密结果是正确的:
兄弟群众意味着古老的需求社会在完美眩光之前 愤怒的确定
当我保存encryptedText,salt(例如数据库)并想再次解密时,我得到了这个。
Ŝ ,&6 ; m an在完美眩光愤怒之前的古代需求社会
*更新 我认为问题是我处理数据的方式(盐和静脉注射)。现在我能够加密并保存-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();
}
}
* Edited.Run Test
public static void main(String[] args) throws Exception {
//Passphrase and masterPassword
String passPhrase = "stackoverflow is great";
String masterPassword = "password";
//-Aes
Aes crypt = new Aes();
// Aes generate random salt
String genSalt = crypt.generateSalt();
String tmpSalt = genSalt;
// Aes generate random Iv
String genIV = crypt.generateIV("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 16);
String tmpIV = genIV;
// Aes encrypt phrase
String cipherPassPhrase = crypt.encrypt(passPhrase, masterPassword, tmpSalt, tmpIV);
System.out.println(cipherPassPhrase);
// save cipherPassPhrase, tmpSalt, tmpIV to database ....decrypt with not stored masterPassword
}
答案 0 :(得分:0)
您的代码存在一些不同的问题。首先是你如何产生盐......
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);
String s = new String(bytes);
return s;
您正在使用随机数据并尝试将其转换为字符串,但字符串构造函数不期望任何随机数据,它需要一些编码的文本字符串(UTF-8,UTF-16,或者其他。)如果对二进制数据进行编码,则只能将其转换为字符串,我只需返回bytes
数组。
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都非常重要,因为它helps to avoid known text attacks。
答案 1 :(得分:-1)
您的代码可以加密A并解密A,但不加密A,加密B并解密A. 因为加密A时加密A的salt和初始化向量被覆盖。 我不知道你要归档什么。我留下了一个可用的代码示例。
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