【问题标题】:Encrypting/Decrypting with AES and storing information in text file使用 AES 加密/解密并将信息存储在文本文件中
【发布时间】:2015-12-12 03:36:43
【问题描述】:

我一直在做我自己的小项目,我正在尝试制作一个简单的密码管理器。我目前遇到的问题是让它以某种方式工作,以便在运行时将加密的密码保存到文件中,然后在再次运行时,您可以调用它,它将被解密,向您显示您的密码为您调用的用户名。

对于我想稍后添加到程序中的内容,我确实需要将加密/解密方法分开。

当前错误是:

线程“main”javax.crypto.IllegalBlockSizeException 中的异常:使用填充密码解密时输入长度必须是 16 的倍数

非常感谢任何帮助。

代码如下:

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;
    }

}

【问题讨论】:

  • 加密数据不是文本,包含它的文件也不是文本文件,不应命名为.txt
  • 谢谢@EJP,我不太清楚为什么要存储为文本文件,我想已经很晚了,我并没有真正考虑过。再次感谢!

标签: java encryption file-io cryptography


【解决方案1】:

您不是在编写文本文件。 加密数据实际上是随机位,您的 main 将来自 encrypt 的返回直接传递给 Files.write(Path,byte[]),后者将其写入为二进制。

当您使用FileReader 重新读取它时,它会使用您没有识别的平台的默认编码,有时还会使用您的用户环境,这可能会或可能不会破坏某些字节;使用readLine() 可能会丢弃部分数据,并使用getBytes("UTF-8") 对其进行编码,当它从不是有效的字符开始时,大约 99.6% 肯定会破坏剩下的任何内容。结果你传递给decrypt的值是完全错误的,无法解密。

简单的对称修复方法是使用File.readAllBytes(Path) 以二进制形式读取(整个)文件,然后解密返回的byte[] 值。

或者,如果您出于某种原因确实需要文本文件(我没有看到任何内容),您需要首先将加密值编码为文本形式并写入,可能添加行终止符,然后读取它返回(如果您选择的话,作为一行)并在解密之前对其进行解码。 Base64 和十六进制(缩写为 hex)是对二进制数据进行文本编码的两种最常用的方法。

另外:使用一个全是可打印的 ASCII 并且甚至包含部分英文单词的密钥会极大地削弱您的加密,从标称的 128 位到更像是 20-30 位,这很容易被任何不称职的攻击者破坏。使用任何硬编码的密钥也是一种危险,尽管这是一个更难的问题,并且没有单一、简单和好的解决方案。

默认情况下,您在 ECB 模式下使用 AES。将 ECB 用于密码(以及几乎其他任何东西)是一个坏主意;了解为什么使用谷歌“ECB penguin”和“Adobe 密码泄露”和/或查看
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

【讨论】:

  • 非常感谢。已经很晚了,所以即使我也不确定我为什么将它存储为文本文件。使用File.readAllBytes(Path) 解决了我的所有问题。非常感谢!