Question

My application needs to encrypt a PKCS#12 archive file which is already encrypted with the passphrase notasecret. The file should be AES256 encrypted.

I tried using this solution for the encryption itself. My idea is to encrypt the string of characters read from file and writing that to a new file and then later decrypt it when I need it.

Right now I'm testing whether the string is encrypted and decrypted back correctly (whether the new file that is created is identical to the original). This is not the case.

Problem

My code below is supposed to create an identical file by encrypting and decrypting a .p12 file, but the resulting file is bigger (original: 1.7KB, new: 2.5KB) and it's unreadable as a PKCS#12 archive. The string length test in the middle displays identical lengths.

Any clues? Does it matter if the file is in one format or the other (PKCS#12 archive, binary, plaintext)? The below code works great if the file to encrypt is in plaintext.

Code

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();
  }
}
Was it helpful?

Solution

You are treating a binary file as a String. A binary file will contain many characters that cannot be treated as a character. Please rewrite your code and assume the PKCS#12 file is binary (using e.g. a FileInputStream / FileOutputStream combination or - for not such a big file - use readAllBytes instead of the input stream.

OTHER TIPS

Thanks to @owlstead's tip about using the data wrong, I managed to encrypt and decrypt the file correctly by treating the data as a byte[], not a String. These are the relevant changes:

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;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top