0

I'm creating a application in JavaFX that first Zip the selected folder and then crypt it. The proces of Zip the folder and crypt it its Ok, the problem is when i try to uncrypt it: appears a warning with IVParameterSpec.

I'm thinking that the problem is that I need to save somwhere the IVS. Maybe at the beggining of the encrypted file? How can i do it?

I'm following this tutorial for the AES with SHA-256 encryption with this modifications: http://karanbalkar.com/2014/02/tutorial-76-implement-aes-256-encryptiondecryption-using-java/

Crypt & UnCrypt:

private static String password;
private static String salt;
private static int pswdIterations = 65536  ;
private static int keySize = 256;
private byte[] ivBytes;
public void encryptToFile(byte[] bytes, File out) throws Exception {  
    byte[] saltBytes = salt.getBytes("UTF-8");

    System.out.println("Salt bfre:" +salt);

    // Derive the key
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(),
            saltBytes,
            pswdIterations,
            keySize
            );

    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

    //encrypt the message
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secret);
    AlgorithmParameters params = cipher.getParameters();
    ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();

    CipherOutputStream os = new CipherOutputStream(new FileOutputStream(out, true), cipher);
    os.write(bytes);
    os.close();

}

public byte[] decryptToFile(File in) throws Exception {  
    byte[] saltBytes = salt.getBytes("UTF-8");

    // Derive the key
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(),
            saltBytes,
            pswdIterations,
            keySize
            );

    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

    // Decrypt the message
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));

    FileInputStream is = new FileInputStream(in);
    byte[] encBytes = new byte[(int) in.length()];
    is.read(encBytes);
is.close();    

    byte[] decryptedBytes = null;
    try {
        decryptedBytes = cipher.doFinal(encBytes);
    } catch (IllegalBlockSizeException | BadPaddingException e) {
        e.printStackTrace();
    }

    return decryptedBytes;

}

And now in the main:

@FXML private void btDoIt (ActionEvent event){
    if (tests()){
        HashClass hash = new HashClass(tfPassword.getText());

        SimetricWithSha256 aes256 = new SimetricWithSha256();
        aes256.setPassword(tfPassword.getText());
        aes256.setSalt(hash.sumCalcToString("SHA-256"));

        if (rbCrypt.isSelected()){

            AppZip appZip = new AppZip(tfPath.getText(),destCrypt);
            ByteArrayOutputStream baos = appZip.zip();
            try {
                aes256.encryptToFile(baos.toByteArray(), new File (tfPath.getText()+destCrypt) );
                baos.close();

            } catch (Exception ex) {
                Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
            }

        }else if (rbUncrypt.isSelected() ){
            try {                    
                byte[] decrypted = aes256.decryptToFile(new File (tfPath.getText() ));
                FileOutputStream fos = new FileOutputStream(tfPath.getText()+destDeCrypt);
                fos.write(decrypted);
                fos.close();


            } catch (Exception ex) {
                Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
            } 

        }
    }
}

If I do the proces of decryption inmediatly after of de encryption it works fine. The problem is when I do it in the second option: else if (rbUncrypt.isSelected() )

I recive this error in the line of the uncrypt function where "cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));":

java.lang.NullPointerException at javax.crypto.spec.IvParameterSpec.(IvParameterSpec.java:53) at encriptarusb.SimetricWithSha256.decryptToFile(SimetricWithSha256.java:137)

Maybe is because i must to save the ivBytes??

Thanks!

selan
  • 956
  • 3
  • 11
  • 27
  • I think the problem is that i don't save the IVs and Salt bytes. A question: it is secure to save it in the firsts bytes of the encrypted file? What is the tipic/secure form for store it? – selan Apr 08 '15 at 19:22
  • You are correct that you can safely prepend IV and salt to the ciphertext. IV is there to make the ciphertext semantically secure (different from similar plaintexts when the same key was used) and salt is there to prevent rainbow tables. – Artjom B. Apr 08 '15 at 21:28
  • So if i want to prepend the IVs to the encrypted file. How i know the size? If I do ivBytes.length the result is 16. Is that the lenght that i must to recover in the beggining of the encrypted file? – selan Apr 09 '15 at 08:50
  • Yes, IV is always 16 bytes long for AES. The salt may be different so you have to simply choose one size and be consistent. – Artjom B. Apr 09 '15 at 08:54
  • If I'm using the password for generate de salt is not necesary to save it because always i'm generating the same. Its true? – selan Apr 09 '15 at 09:01
  • The salt is supposed to be unique, so no you need to generate a new random salt. Since you're using PBKDF2, it can output arbitrary length hashes, so you can simply output key size+iv size and use 16 bytes of the output for the IV and 32 bytes for the key. The you would only prepend the salt to the ciphertext. – Artjom B. Apr 09 '15 at 09:15
  • Ok I will study that. But I thought to generate the salt using the same password that i use for generate the secretKeySpec, for not more complications. When I can recuperate the IV's I will perfectionate my code using a fully random salt. For the moment I have a new problem trying to solve this question: http://stackoverflow.com/questions/29535663/encrypt-a-file-appending-ivsbytes-in-unique-file-execption-given-final-block – selan Apr 09 '15 at 10:16

1 Answers1

1

Basically the problem was that I don't save the IVBytes used to make the encryption more randomly.

So if I want to decrypt the file I need the same IV and the same salt. In this example I use how salt the hash (SHA-256) of the password (but is better to use a randomly generated salt).

For the IVBytes I put it into the same file that I encrypted, because they are not necessarly to be secret. During the decryption I will take the first bytes of the files (the IV bytes) for the decryption:

public void encryptOfFile(byte[] bytes, File out) throws Exception {  

    byte[] saltBytes = salt.getBytes("UTF-8");

    System.out.println("Salt bfre:" +salt);

    // Derive the key
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(),
            saltBytes,
            pswdIterations,
            keySize
            );

    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

    //encrypt the message
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secret);
    AlgorithmParameters params = cipher.getParameters();
    ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();

    //First copy the IVBytes at the beginning  of the file
    FileOutputStream os = new FileOutputStream(out, true);
    os.write(ivBytes);

    CipherOutputStream cos = new CipherOutputStream(os, cipher);
    cos.write(bytes);

    cos.close();
    os.close();

}

public byte[] decryptToFile(File in) throws Exception {  
    byte[] saltBytes = salt.getBytes("UTF-8");

    // Derive the key
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(),
            saltBytes,
            pswdIterations,
            keySize
            );

    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");


    //Get IVBytes of the first 16 bytes of the file
    FileInputStream is = new FileInputStream(in);
    byte [] ivBytesRecovered = new byte [16];
    if (is.read(ivBytesRecovered) != ivBytesRecovered.length) {
        //is.close();
        throw new IllegalStateException("Too short file");
    }

    // Decrypt the message
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytesRecovered));        

    byte[] encBytes = new byte[(int) in.length()-16];
    is.read(encBytes);

    byte[] decryptedBytes = null;
    try {
        decryptedBytes = cipher.doFinal(encBytes);
    } catch (IllegalBlockSizeException | BadPaddingException e) {
        e.printStackTrace();
    }

    is.close();

    return decryptedBytes;

}

In adition, if you want to generate a random salt:

public String generateSalt() {
    SecureRandom random = new SecureRandom();
    byte bytes[] = new byte[20];
    random.nextBytes(bytes);
    String s = new String(bytes);
    return s;
}

Here my references (three examples that I use to do it):

http://javapapers.com/java/java-file-encryption-decryption-using-aes-password-based-encryption-pbe/
http://karanbalkar.com/2014/02/tutorial-76-implement-aes-256-encryptiondecryption-using-java/
http://cryptofreek.org/2012/12/10/encrypting-and-decrypting-java-part-2/

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
selan
  • 956
  • 3
  • 11
  • 27
  • Your salt is of variable length. `random.nextBytes(bytes);` fills `bytes` with any possible byte value, but `new String(bytes);` converts only those bytes to characters that are printable, so you may lose some characters. Since you generate a salt as bytes, then you should keep using the `byte[]` everywhere. – Artjom B. Apr 11 '15 at 15:04