如何解决如何使用从密码派生的密钥正确加密和解密文件
我正在尝试制定使用“ PBEWithHmacSHA256AndAES_256”标准加密和解密文件的正确过程。
从我对Oracle的example code的了解中了解到。
我已经收集到需要盐,以及迭代计数和哈希标准。
- 用户定义的密码
new String(key).tochararray()
作为字节数组(使用此方法进行其他加密) - 安全的随机IV
initVector
作为字节数组 - 纯文本文件
inputFile
作为字符串 - 要创建的密文文件
outputFile
作为字符串的名称
我已经按照代码示例进行了编码,以证明我认为对于加密方法是正确的。然后,我将盐和IV都添加到密文中,以用于解密。
private static void encrypt(byte[] key,byte[] initVector,String inputFile,String outputFile) //exceptions for throws... {
//Initalisation for encryption
Cipher cipher;
byte[] salt = new byte[16];
SecureRandom rand = new SecureRandom();
// Salt randomly generated with base64
rand.nextBytes(salt);
System.out.println("my salt should be" + Base64.getEncoder().encodetoString(salt));
salt = Base64.getEncoder().encode(salt);
// Iteration count
int count = 1000;
IvParameterSpec iv = new IvParameterSpec(initVector);
// Create PBE parameter set
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(Base64.getDecoder().decode(salt),count,iv);
// Convert pass into SecretKey object
PBEKeySpec pbeKeySpec = new PBEKeySpec(new String(key).tochararray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256");
SecretKey pbeKey;
try {
pbeKey = keyFac.generateSecret(pbeKeySpec);
} catch (InvalidKeySpecException e) {
System.out.println("Sorry,the password specified cannot be used as a secret key");
System.out.println("Please check that your password uses valid characters");
return;
}
// Create PBE Cipher
cipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");
// Initialize PBE Cipher with key and parameters
cipher.init(Cipher.ENCRYPT_MODE,pbeKey,pbeParamSpec);
}
//File error checking and file handling (i.e. generating file paths)...
System.out.println("Secret key is " + Base64.getEncoder().encodetoString(key));
System.out.println("IV is " + Base64.getEncoder().encodetoString(initVector));
//Special file reading and writing with 'Cipher Stream'
try (InputStream fin = FileEncryptor.class.getResourceAsstream(loadFile.getName());
OutputStream fout = Files.newOutputStream(saveFile);
CipherOutputStream cipherOut = new CipherOutputStream(fout,cipher) {
}) {
final byte[] bytes = new byte[1024];
for(int length=fin.read(bytes); length!=-1; length = fin.read(bytes)){
fout.write(initVector);
fout.write(salt);
cipherOut.write(bytes,length);
}
} catch (IOException e) {
System.out.println("Something went wrong with reading and writing these files!");
System.out.println("Please check you have the latest version of this program");
System.out.println("Contact your IT admin to make sure you have sufficient privileges");
}
System.out.println("SUCCESS! Encryption finished,saved at specified location");
}
-
inputIV
的字符串,因为未用于PBE,所以已作为null传入。 -
密文文件
inputFile
作为字符串 -
私有静态无效解密(String inputKEY,String inputIV,String inputFile,String outputFile){ 密码cipher = null;
//File error checking and file handling (i.e. generating file paths)... InputStream encryptedData = Files.newInputStream(loadFilePath); PBEKeySpec pbeKeySpec = new PBEKeySpec(inputKEY.tochararray()); SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256"); SecretKey pbeKey = null; try { pbeKey = keyFac.generateSecret(pbeKeySpec); } catch (InvalidKeySpecException e) { // Todo Auto-generated catch block e.printstacktrace(); } byte[] initVect = new byte[16]; encryptedData.read(initVect); IvParameterSpec iv = new IvParameterSpec(Base64.getDecoder().decode(initVect); byte[] salt = new byte[16]; encryptedData.read(salt); PBEParameterSpec pbeParamSpec = new PBEParameterSpec(Base64.getDecoder().decode(salt),1000,iv); cipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256"); System.out.println("my salt should be" + Base64.getEncoder().encodetoString(Base64.getDecoder().decode(salt))); cipher.init(Cipher.DECRYPT_MODE,pbeParamSpec); try (CipherInputStream decryptStream = new CipherInputStream(encryptedData,cipher); OutputStream decryptedOut = Files.newOutputStream(saveFile)){ final byte[] bytes = new byte[1024]; for(int length=decryptStream.read(bytes); length!=-1; length = decryptStream.read(bytes)){ decryptedOut.write(bytes,length); } } catch (IOException e) { //This is caught when decryption is run System.out.println("Something went wrong with reading and writing these files!"); System.out.println("Please check you have the latest version of this program"); System.out.println("Contact your IT admin to make sure you have sufficient privileges"); } System.out.println("SUCESS! Decryption finished,saved at specified location");
}
根据我对PBE的理解,我认为有些事情是不对的,因此,我实施它的方式可能是错误的。谁能指出什么地方错了?
解决方法
主要问题是:
- IV和Salt不能写在
for
循环内。 - IV以
encrypt
的形式存储,没有经过Base64编码,但是以decrypt
的Base64解码。 - 这16个字节的salt存储在
encrypt
中(不必要)以Base64编码,即存储24个字节。但是,在decrypt
中仅加载16个字节。
也:
- 在编码/解码时,有时未指定编码,因此使用默认编码。
-
encrypt
和decrypt
为key和IV使用不同的参数类型。 - 代码中有很多复制/粘贴错误。
注意:与您的代码相反,linked代码除了确定密钥外,还从密码和盐中确定IV。 在您的代码中,IV已通过。因此,您必须确保密钥/ IV对只能使用一次。通常,每次加密都会生成一个随机IV。
在下面的代码(基于您的代码,但是为了简单起见,没有异常处理)中,这些问题已得到解决/优化。此外,代码适用FileInputStream
和FileOutputStream
而不是您的类(但这不是必需的):
private static void encrypt(String key,byte[] initVector,String inputFile,String outputFile) throws Exception {
// Key
PBEKeySpec pbeKeySpec = new PBEKeySpec(key.toCharArray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
// IV
IvParameterSpec iv = new IvParameterSpec(initVector);
// Salt
SecureRandom rand = new SecureRandom();
byte[] salt = new byte[16];
rand.nextBytes(salt);
// ParameterSpec
int count = 1000; // should be larger,see Michael Fehr's comment
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt,count,iv);
// Cipher
Cipher cipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");
cipher.init(Cipher.ENCRYPT_MODE,pbeKey,pbeParamSpec);
try (FileInputStream fin = new FileInputStream(inputFile);
FileOutputStream fout = new FileOutputStream(outputFile);
CipherOutputStream cipherOut = new CipherOutputStream(fout,cipher)) {
// Write IV,Salt
fout.write(initVector);
fout.write(salt);
// Encrypt
final byte[] bytes = new byte[1024];
for (int length = fin.read(bytes); length != -1; length = fin.read(bytes)) {
cipherOut.write(bytes,length);
}
}
}
private static void decrypt(String key,byte[] initVect,String outputFile) throws Exception {
try (FileInputStream encryptedData = new FileInputStream(inputFile);
FileOutputStream decryptedOut = new FileOutputStream(outputFile)) {
// Key
PBEKeySpec pbeKeySpec = new PBEKeySpec(key.toCharArray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
// Read IV
if (initVect == null) {
initVect = encryptedData.readNBytes(16);
}
IvParameterSpec iv = new IvParameterSpec(initVect);
// Read salt
byte[] salt = encryptedData.readNBytes(16);
// ParameterSpec
int count = 1000;
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt,iv);
// Cipher
Cipher cipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");
cipher.init(Cipher.DECRYPT_MODE,pbeParamSpec);
try (CipherInputStream decryptStream = new CipherInputStream(encryptedData,cipher)) {
// Decrypt
final byte[] bytes = new byte[1024];
for (int length = decryptStream.read(bytes); length != -1; length = decryptStream.read(bytes)) {
decryptedOut.write(bytes,length);
}
}
}
}
编辑-关于decrypt
中盐和IV的读数:
GPI 在其评论中指出,FileInputStream.read(byte[] b)
通常读取b.length
字节,但这不保证。更可靠的方法是确定读取数据的长度,然后循环调用该方法,直到数据完成为止。另一种选择是使用InputStream.readNBytes(int len)
,它被保证 读取len
字节(除非遇到流末尾或引发异常),如 Zabuzard 已建议。在代码中,现在使用后者,即read
被readNBytes
取代。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。