我一直在做我自己的小项目,我正在尝试制作一个简单的密码管理器。我目前遇到的问题是让它以某种方式工作,以便在运行时将加密的密码保存到一个文件中,然后在另一次运行时调用它,它会被解密,并显示你的密码对于您调用的用户名。
对于稍后我想添加到程序中的内容,我确实需要将加密/解密方法分开。
当前错误是:
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
非常感谢任何帮助。
代码如下:
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.Scanner;
import javax.crypto.spec.SecretKeySpec;
public class PasswordManager3
{
static String key = "SimplePasswordMg";
static String password1 = "";
static String password2 = "";
static String username = "";
public static void main(String[] args)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException,
BadPaddingException, IOException
{
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
System.out.println("Enter New to input a new password, or Retrieve to retrieve an old password:");
Scanner scanner1 = new Scanner(System.in);
String answer = scanner1.nextLine();
if(answer.equalsIgnoreCase("New")) {
System.out.println("Please enter a username: ");
Scanner scanner2 = new Scanner(System.in);
username = scanner2.nextLine();
System.out.println("Please enter a password: ");
Scanner scanner3 = new Scanner(System.in);
password1 = scanner3.nextLine();
System.out.println("Please enter your password again: ");
Scanner scanner4 = new Scanner(System.in);
password2 = scanner4.nextLine();
if (password1.equalsIgnoreCase(password2)) {
Files.write(Paths.get(username + ".txt"), encrypt(password1, cipher, aesKey));
System.out.println("Your password has been stored.");
}
else {
System.out.println("The passwords you entered did not match. Exiting password manager.");
}
}
else if(answer.equalsIgnoreCase("Retrieve")) {
System.out.println("Please enter the username you would like to retrieve the password for: ");
Scanner scanner5 = new Scanner(System.in);
username = scanner5.nextLine();
BufferedReader in = new BufferedReader(new FileReader(username + ".txt"));
String encryptedpass = in.readLine();
byte[] encryptedpass2 = encryptedpass.getBytes("UTF-8");
System.out.println(decrypt(encryptedpass2, cipher, aesKey));
}
else {
System.out.println("You entered an incorrect option, program exited.");
}
}
public static byte[] encrypt(String str, Cipher cipher, Key aesKey)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException
{
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(key.getBytes("UTF-8"));
return encrypted;
}
public static String decrypt(byte[] byte1, Cipher cipher, Key aesKey)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
cipher.init(Cipher.DECRYPT_MODE, aesKey);
String decrypted = new String(cipher.doFinal(byte1));
return decrypted;
}
}
最佳答案
您不是在编写文本文件。加密数据实际上是随机位,您的 main
将 encrypt
的返回值直接传递给 Files.write(Path,byte[])
将其写入二进制文件。
当你用 FileReader
读回它时,它使用你的平台的默认编码,你没有识别,有时你的用户环境,这可能会或可能不会破坏一些字节;使用 readLine()
可能会丢弃部分数据,并且当它从不是有效字符开始时使用 getBytes("UTF-8")
对其进行编码大约为 99.6%肯定会破坏剩下的一切。因此,您传递给 decrypt
的值是完全错误的,无法解密。
简单的对称修复方法是使用File.readAllBytes(Path)
以二进制形式读取(整个)文件,然后解密byte[]
返回的值。
或者,如果出于某种原因你真的想要文本文件(我没有看到任何),你需要先将加密值编码为文本形式并写入,可能添加一个行终止符,然后读取它返回(如果您选择的话,作为一行)并在解密之前对其进行解码。 Base64 和十六进制(缩写为 hex)是两种最常用的二进制数据文本编码方法。
另外: 使用完全可打印的 ASCII 且甚至包含部分英文单词的 key 会极大地削弱您的加密,从标称的 128 位到更接近 20-30 位,这很容易被任何半胜任的攻击者破坏。使用任何硬编码 key 也是一种危险,尽管这是一个更难的问题,并且没有单一、简单和好的解决方案。
默认情况下,您在 ECB 模式下使用 AES。使用 ECB 作为密码(以及几乎任何其他东西)不是一个好主意;了解为什么谷歌“ECB penguin”和“Adobe password breach”和/或查看
https://crypto.stackexchange.com/questions/11451/can-ecb-mode-really-leak-some-characters
https://crypto.stackexchange.com/questions/11456/what-does-it-mean-if-second-half-of-8-char-string-encrypted-in-3des-is-always-id
关于java - 使用 AES 加密/解密并将信息存储在文本文件中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34215024/