簡體   English   中英

如何讀取使用AES算法加密的加密文件並解密?

[英]How to read an encrypted file which was encrypted using AES algorithm and decrypt it?

我試圖讀取普通的文本文件並使用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();
    }

}

您的問題在這里:

fileWriter.write(new String(encrypted));

看起來您看過關於讀取文件的文章,但是您尚未掌握字符編碼的概念。

好的密碼的輸出是不可預測的字節,范圍為0到255。

另一方面,文本包含字符。 為了在數字計算機中表示它們,每個字符字形分配了不同的數字。 由於不同的人使用不同的字符集,因此多年來已經創建了許多不同的編碼方案。 某些西方語言每個字符僅使用7位。 其他人使用8位,但是仍然沒有為每個8位代碼分配一個字符。 其他系統使用多個字節,但是僅某些字節序列表示有效字符。

我為什么要告訴你呢?

好吧,當您說new String(encrypted) ,您要獲取一堆偽隨機字節,並嘗試使用系統上的默認字符編碼將其轉換為字符。 通常,在這種編碼下,會有字節或字節序列無法轉換為字符。 這些字節將被替換為字符(U + FFFD,Unicode“替換字符”)。 這種替換會破壞密文; 不同的字節序列都用相同的符號替換。 當您嘗試解密時,此丟失的信息會導致錯誤的塊大小或填充錯誤。

密碼文字不是真正的文字。 不要將其轉換為String ,也不要將其作為文本寫入文件。 使用InputStreamOutputStreambyte[]處理密文。 僅在加密之前和解密之后將信息視為文本。

您的密鑰派生也不安全。 閱讀如何使用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字符打印密文。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM