如何读取使用AES算法加密并解密的加密文件?

时间:2014-03-27 17:20:17

标签: java encryption

我正在尝试读取普通文本文件并使用AES算法对其进行加密,然后我尝试读取该加密文件并对其进行解密。加密工作正常。但是在解密时,我收到一条错误,说“使用填充密码解密时,输入长度必须是16的倍数”。我不知道我做错了什么。任何建议都会对我有所帮助。谢谢。

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class crypt {

    public static void main(String args[]) throws Exception {


        String keyString = "averylongtext!@$@#$#@$#&(&}{23432432432dsfsdf";
        FileWriter fileWriter = null, fileWriter1 = null;
        File enc = new File("C:\\test\\encrypted.txt");
        File dec = new File("C:\\test\\decrypted.txt");
        String path = "C:\\test\\normal_file.txt";
        String path2 = "C:\\test\\encrypted.txt";
        fileWriter = new FileWriter(enc);
        fileWriter1 = new FileWriter(dec);


        String input = readFile(path, StandardCharsets.UTF_8);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] iv = new byte[cipher.getBlockSize()];
        new SecureRandom().nextBytes(iv);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        digest.update(keyString.getBytes());
        byte[] key = new byte[16];
        System.arraycopy(digest.digest(), 0, key, 0, key.length);
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
        byte[] encrypted = cipher.doFinal(input.getBytes());
        System.out.println(new String(encrypted));

   //writing encrypted information to a new file encrypted.txt
        fileWriter.write(new String(encrypted));
        fileWriter.close();

  //reading encrypted information from the file encrypted.txt
  //This part is where the error is
        encrypted = readFile(path2, StandardCharsets.UTF_8).getBytes();
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        byte[] decrypted = cipher.doFinal(encrypted);
        System.out.println("decrypted: \n" + new String(decrypted, "UTF-8"));

  //writing the decrypted information to the file decrypted.txt
        fileWriter1.write(new String(decrypted));
        fileWriter1.close();

    }

 //method to read a file
    static String readFile(String path, Charset encoding) throws IOException {
        byte[] encoded = Files.readAllBytes(Paths.get(path));
        return encoding.decode(ByteBuffer.wrap(encoded)).toString();
    }

}

1 个答案:

答案 0 :(得分:3)

你的问题在这里:

fileWriter.write(new String(encrypted));

看起来您在阅读文件时看到my post,但您还没有掌握字符编码的概念。

良好密码的输出是不可预测的字节,范围为0到255.

另一方面,文字包含字符;为了在数字计算机中表示它们,为每个字符字形分配不同的数字。因为不同的人使用不同的字符集,所以多年来已经创建了许多不同的编码方案。一些西方语言每个字符仅使用7位。其他人使用8位,但仍然没有为每个8位代码分配一个字符。其他系统使用多个字节,但只有某些字节序列表示有效字符。

为什么我告诉你这个?

好吧,当你说new String(encrypted)时,你正在使用一堆伪随机字节并尝试使用系统上的默认字符编码将它们转换为字符。通常,会有字节或字节序列不能转换为该编码下的字符。这些字节将替换为字符�(U + FFFD,Unicode"替换字符")。这种替换破坏了密文;不同的字节序列都被相同的符号替换。当您尝试解密时,此缺少的信息会导致块大小或填充错误。

密文不是文本。不要将其转换为String,或将其作为文本写入文件。使用InputStreamOutputStreambyte[]来处理密文。仅在加密前和解密后将信息视为文本。

您的密钥派生也不安全。 Read how to use PBKDF2安全地从密码中获取密钥。

/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
try (OutputStream fos = Files.newOutputStream(output, StandardOpenOption.CREATE_NEW);
     CipherOutputStream os = new CipherOutputStream(fos, cipher);
     InputStream is = Files.newInputStream(input)) {
  byte[] buffer = new byte[4096];
  while (true) {
    int n = is.read(buffer);
    if (n < 0)
      break;
    os.write(buffer, 0, n);
  }
  os.flush();
}

如果您确实需要将密文转换为String,请使用Base-64或Base-85等编码。这些将允许您仅使用US-ASCII字符打印密文。