如何用AES256加密/解密Java中的PKCS#12存档文件?

时间:2014-02-20 10:27:14

标签: java file encryption aes pkcs#12

我的应用程序需要加密已使用密码notasecret加密的PKCS#12 archive file。该文件应该是AES256加密的。

我尝试使用this solution进行加密。我的想法是加密从文件中读取的字符串并将其写入新文件,然后在需要时对其进行解密。

现在我正在测试字符串是否已加密并正确解密(无论创建的新文件是否与原始文件相同)。事实并非如此。

问题

我的代码应该通过加密和解密.p12文件来创建一个相同的文件,但生成的文件更大(原始版本:1.7KB,新版本:2.5KB),并且它作为PKCS#12存档是不可读的。中间的字符串长度测试显示相同的长度。

任何线索?文件是一种格式还是另一种格式(PKCS#12存档,二进制文件,纯文本)是否重要? 如果要加密的文件是明文,则以下代码效果很好。

代码

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.security.AlgorithmParameters;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

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

    char[] password = "password".toCharArray();
    byte[] salt = new byte[8];

    /* 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();

    String file = "someprivatekey.p12";
    String keyFileText = readFile(file);

    byte[] ciphertext = cipher.doFinal(keyFileText.getBytes("UTF-8"));

    Cipher otherCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    otherCipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));

    String plaintext = new String(otherCipher.doFinal(ciphertext), "UTF-8");

    if (plaintext.equals(keyFileText))
      System.out.println("decrypted plaintext same as plaintext");
    System.out.println("plaintext length: " + plaintext.length() + " keyFileText length: " + keyFileText.length());

    writeFile(plaintext, "new-" + file);
  }

  private static void writeFile(String contents, String filePath) {
    PrintWriter out = null;
    try {
      out = new PrintWriter(new FileOutputStream(filePath));
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    }
    out.write(contents);
    out.close();
  }

  private static String readFile(String filePath) {
    FileInputStream fis = null;
    int buf;
    StringBuilder contents = null;
    try {
      fis = new FileInputStream(filePath);
      contents = new StringBuilder();
      while ((buf = fis.read()) != -1) {
        contents.append((char) buf);
      }
      fis.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return contents.toString();
  }
}

2 个答案:

答案 0 :(得分:0)

您将二进制文件视为String。二进制文件将包含许多不能被视为字符的字符。请重写您的代码,并假设PKCS#12文件是二进制文件(使用例如FileInputStream / FileOutputStream组合,或者 - 对于这么大的文件 - 使用readAllBytes而不是输入流。

答案 1 :(得分:0)

感谢@owlstead关于错误使用数据的提示,我设法通过将数据视为byte[]而不是String来正确加密和解​​密文件。这些是相关的变化:

private static void writeFile(byte[] contents, String filePath) {
  FileOutputStream out = null;
  try {
    out = new FileOutputStream(filePath);
    out.write(contents);
    out.close();
  } catch (Exception e) {
    e.printStackTrace();
  }
}

private static byte[] readFile(String filePath) {
  FileInputStream fis = null;
  File f = new File(filePath);
  int buf, i = 0;
  byte[] array = null;
  try {
    fis = new FileInputStream(f);
    array = new byte[(int) f.length()];
    while ((buf = fis.read()) != -1) {
      array[i++] = (byte) buf;
    }
    fis.close();
  } catch (Exception e) {
    e.printStackTrace();
  }
  return array;
}