2471. Java Advanced - Cryptography
Encryption and Decryption


Introduce how to encrypt and decrypt file with java.

1. Java Cryptography

1.1 Java Cryptography API

The Java Cryptography API enables you to encrypt and decrypt data in Java, as well as manage keys, sign and authenticate messages, calculate cryptographic hashes and much more. The term cryptography is often abbreviated to crypto, so sometimes you will see references to Java crypto instead of Java Cryptography. The two terms refer to the same topic though.

1.2 Java Cryptography Extension

The Java cryptography API is provided by what is officially called the Java Cryptography Extension(JCE).

The Java Cryptography Extension has been part of the Java platform for a long time now. The JCE was initially kept separate from Java because the US had some export restrictions on encryption technology. Therefore the strongest encryption algorithms were not included in the standard Java platform. You could obtain these stronger encryption algorithms for Java JCE if you were a company inside the US, but the rest of the world had to make due with the weaker algorithms (or implement their own crypto algorithms and plug into JCE). Today (2017) the US encryption export rules have been eased a lot. Therefore most of the world can benefit from the international encryption standards via Java JCE.

Java Cryptographic Extension (JCE) framework which implements the standard cryptographic algorithms such as AES, DES, DESede and RSA.

2. Advanced Encryption Standard

Advanced Encryption Standard(AES) is a symmetric-key algorithm that uses the same key for both encryption and decryption of data. We will use AES in the following sample to encrypt and decrypt files.

2.1 Eclipse Project

In Eclipse, create a new Java project.

2.2 CryptoUtils

Defined two method for encryption and decryption.

  • encrypt()
  • decrypt()
// CryptoUtils.java
package johnny.tutorial.encryption;

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 javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class CryptoUtils {
    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES";

    public static void encrypt(String key, File inputFile, File outputFile)
            throws CryptoException {
        doCrypto(Cipher.ENCRYPT_MODE, key, inputFile, outputFile);
    }

    public static void decrypt(String key, File inputFile, File outputFile)
            throws CryptoException {
        doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
    }

    private static void doCrypto(int cipherMode, String key, File inputFile,
            File outputFile) throws CryptoException {
        try {
            // Create a Key from a given byte array for a given algorithm
            Key secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
            // Get an instance of Cipher class for a given algorithm transformation
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            // Initialize the Cipher with an appropriate mode (encrypt or decrypt) and the given Key
            cipher.init(cipherMode, secretKey);

            // Get content to be encrypted or decrypted
            FileInputStream inputStream = new FileInputStream(inputFile);
            byte[] inputBytes = new byte[(int) inputFile.length()];
            inputStream.read(inputBytes);

            // Invoke doFinal() method to perform encryption or decryption
            byte[] outputBytes = cipher.doFinal(inputBytes);

            // Save the encrypted or decrypted content to output file
            FileOutputStream outputStream = new FileOutputStream(outputFile);
            outputStream.write(outputBytes);

            inputStream.close();
            outputStream.close();

        } catch (NoSuchPaddingException | NoSuchAlgorithmException
                | InvalidKeyException | BadPaddingException
                | IllegalBlockSizeException | IOException ex) {
            throw new CryptoException("Error encrypting/decrypting file:", ex);
        }
    }
}

2.3 CryptoException

Create a customized exception for Crypto errors.

// CryptoException.java
package johnny.tutorial.encryption;

public class CryptoException extends Exception {
    public CryptoException() {
    }

    public CryptoException(String message, Throwable throwable) {
        super(message, throwable);
    }
}

2.4 CryptoUtilsTest

Create a new Junit test class for CryptoUtils.

// CryptoUtilsTest.java
package johnny.tutorial.encryption.test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

import org.junit.Test;

import johnny.tutorial.encryption.CryptoException;
import johnny.tutorial.encryption.CryptoUtils;
import org.junit.Assert;

public class CryptoUtilsTest {
    private static final String ORIGINAL_FILE = "document.txt";
    private static final String ENCRYPTED_FILE = "encrypted.txt";
    private static final String DECRYPTED_FILE = "decrypted.txt";
    private static final String KEY_16 = "Mary has one cat";
    private static final String KEY_17 = "Water is purified";

    @Test
    public void test() {
        // Take a look the original content
        String original = readFile(ORIGINAL_FILE);
        System.out.print("Original content: ");
        System.out.println(original);

        File inputFile = new File(ORIGINAL_FILE);
        File encryptedFile = new File(ENCRYPTED_FILE);
        File decryptedFile = new File(DECRYPTED_FILE);

        try {
            CryptoUtils.encrypt(KEY_16, inputFile, encryptedFile);
            CryptoUtils.decrypt(KEY_16, encryptedFile, decryptedFile);
        } catch (CryptoException ex) {
            System.out.println(ex.getMessage());
            ex.printStackTrace();
        }

        // Take a look the encrypted content
        String encrypted = readFile(ENCRYPTED_FILE);
        System.out.print("Encrypted content: ");
        System.out.println(encrypted);

        // Take a look the decrypted content
        String decrypted = readFile(DECRYPTED_FILE);
        System.out.print("Decrypted content: ");
        System.out.println(decrypted);

        Assert.assertEquals(original, decrypted);
    }

    /*
     * Read content from the given file
     */
    private String readFile(String filename) {
        String content = "";
        try {
            BufferedReader br = new BufferedReader(new FileReader(filename));
            try {
                StringBuilder sb = new StringBuilder();
                String line = br.readLine();

                while (line != null) {
                    sb.append(line);
                    sb.append(System.lineSeparator());
                    line = br.readLine();
                }
                content = sb.toString();
            }
            finally {
                br.close();
            }
       } catch (IOException ioe) {
            System.out.println(ioe.getMessage());
       } finally {
       }
       return content;
    }

}

3. Testing

3.1 Orignal File

Create a text file in the project root folder with name ‘document.txt’ and the following content.

Happy work, happy life!

3.2 Run Junit

In the console, you should see the following output. The encrypted content is unreadable.

Original content: Happy work, happy life!

Encrypted content: �C�D3\(��u7��칿~�qN��]ZJ��l

Decrypted content: Happy work, happy life!

4. More Notes

4.1 Key Size

The AES algorithm requires that the key size must be 16 bytes (or 128 bit). So if you provide a key whose size is not equal to 16 bytes, a java.security.InvalidKeyException will be thrown. In case your key is longer, you should consider using a padding mechanism that transforms the key into a form in which its size is multiples of 16 bytes.

String key = "Mary has one cat";  //length = 16, works
String key = "Water is purified"; //length = 17, InvalidKeyException occurs.

4.2 JSE Packages

If you get ‘InvalidKeyException : Illegal Key Size’ error when ‘cipher.init(cipherMode, secretKey)’ is executed, go to http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html, download Java Cryptography Extension (JCE) files. Make sure you download the proper version, here we need JCE for java 8.

Then, extract local_policy.jar and US_export_policy.jar from the downloaded package and copy them to $JAVA_HOME/jre/lib/security, for example /opt/jdk1.8.161/jre/lib/security. Restart your application, the error should be gone.

5. Source Files

6. References