Thursday, February 7, 2013

Cryptography in Java

I spent an evening recently playing around with cryptography in Java. I had to look up something similar for work and then it piqued my interest so I had a bit of a play with it.

What I was looking to do was AES encrypt a file using a key derived from a passphrase. After some digging around it seems that the way to create a key is to use the PKCS #5 algorithm and hash the result. This can be done in Java with a key factory using the algorithm PBKDF2WithHmacSHA1:

    private static SecretKey generatePBKKey(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {

        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[16];
        rand.nextBytes(salt);
        KeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, 2, 128);
        SecretKey generatedKey = keyFactory.generateSecret(pbeKeySpec);

        SecretKey encKey = new SecretKeySpec(generatedKey.getEncoded(), "AES");

        return encKey;
    }


This key can then be used to initialise a AES Cipher which you can then encrypt the file with using a CipherOutputStream. Creating the AES Cipher was done using AES with ECB (Electronic Code Book) and PKCS5 padding.

        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key);


 I wasn't sure what block mode and padding to use and this seemed to be what most use.

The best doco I could find from Oracle was the Java 6 guide which ran through the main classes - http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html

 The full code of what I ended up with is over the jump.





package neb;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Provider.Service;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class CryptoTest {

    /**
     * @param args
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws IOException
     * @throws InvalidKeySpecException
     */
    public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeyException, IOException, InvalidKeySpecException {

        for (Provider provider : Security.getProviders()) {

            System.out.println("provider " + provider.getName());
            for (Service service : provider.getServices()) {
                System.out.println("sevice available " + service.getAlgorithm());
            }
        }

        String password = "please7890123456";
        Key key = generatePBKKey(password);
        System.out.println("key " + key.getEncoded());
        Cipher factoryEncryptCipher = createAndInitialiseCipherUsingPKCS(key, Cipher.ENCRYPT_MODE);

        String fileName = "EncryptMe.txt";

        String encryptedFileName = encrypt(fileName, factoryEncryptCipher);

        Cipher factoryDecryptCipher = createAndInitialiseCipherUsingPKCS(key, Cipher.DECRYPT_MODE);
        decrypt(encryptedFileName, factoryDecryptCipher);
    }

    private static String encrypt(String fileName, Cipher cipher) throws IOException {

        File inputFile = new File(fileName);

        FileInputStream fileInputStream = new FileInputStream(inputFile);

        String returnFileName = fileName + "-encrypted.txt";
        File outputFile = new File(returnFileName);

        FileOutputStream fileOutputStream = new FileOutputStream(outputFile);

        CipherOutputStream cipherOutputStream = new CipherOutputStream(fileOutputStream, cipher);

        byte[] bytes = new byte[8];
        int bytesRead = fileInputStream.read(bytes);

        while (bytesRead != -1) {

            cipherOutputStream.write(bytes, 0, bytesRead);
            bytesRead = fileInputStream.read(bytes);
        }

        cipherOutputStream.flush();
        cipherOutputStream.close();
        fileInputStream.close();

        return returnFileName;
    }

    private static String decrypt(String fileName, Cipher cipher) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException, IOException {

        File encryptedFile = new File(fileName);

        FileInputStream fileInputStream = new FileInputStream(encryptedFile);
        CipherInputStream cipherInputStream = new CipherInputStream(fileInputStream, cipher);

        String decryptedFileName = fileName + "-decrypted.txt";
        File outputFile = new File(decryptedFileName);

        FileOutputStream fileOutputStream = new FileOutputStream(outputFile);

        byte[] bytes = new byte[8];
        int bytesRead = cipherInputStream.read(bytes);

        while (bytesRead != -1) {

            fileOutputStream.write(bytes, 0, bytesRead);
            bytesRead = cipherInputStream.read(bytes);
        }

        fileOutputStream.flush();
        cipherInputStream.close();
        fileOutputStream.close();

        return decryptedFileName;
    }

    private static Cipher createAndInitialiseCipherUsingPKCS(Key key, int mode)
            throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException {

        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");

        cipher.init(mode, key);

        return cipher;
    }

    private static SecretKey generatePBKKey(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {

        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[16];
        rand.nextBytes(salt);
        KeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, 2, 128);
        SecretKey generatedKey = keyFactory.generateSecret(pbeKeySpec);

        SecretKey encKey = new SecretKeySpec(generatedKey.getEncoded(), "AES");

        return encKey;
    }

    private static Cipher createAndInitialiseCipherManualKey(String password, int mode)
            throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, InvalidKeyException {

        Cipher cipher = Cipher.getInstance("AES");

        SecretKeySpec secretKeySpec = new SecretKeySpec(password.getBytes(), "AES");

        cipher.init(mode, secretKeySpec);

        return cipher;
    }
}

No comments:

Post a Comment