Thursday, August 16, 2007

Encryption and Decryption using Symmetric Keys

This Tech Tip reprinted with permission by java.sun.com

Encryption and decryption can be done symmetrically -- here the same key is used to encrypt and decrypt the data. Because both parties have the same key, the decryption essentially is performed by reversing some part of the encryption process. The Blowfish algorithm is an example of a symmetric key. It is supported by the Java Cryptography Extension (JCE). You can find the appropriate APIs in the javax.crypto.* packages. In addition to Blowfish, examples of cipher algorithms currently supported by the JCE are the Digital Encryption Standard (DES), Triple DES Encryption (DESede), and Password-based encryption algorithm (PBEWithMD5AndDES).

Symmetric key algorithms tend to be be much faster than asymmetric key algorithms. In addition, as you saw in the first tip, the size of the text that can be encrypted depends on the size of the product of the two primes used to generate the public and private keys. With symmetric key algorithms you do not have a limitation on the total size of what can be encrypted. Although, depending on the symmetric cipher algorithms, the total input size has to be a multiple of block sizes and might require padding. A problem with symmetric keys is that keys must be shared among parties involved in encryption or decryption. So there is the danger of interception or unauthorized sharing.

You create a symmetric key much as you create a key pair. You use a factory method from the KeyGenerator class and pass in the algorithm as a String. When you call the generateKey() method, you get back an object that implements the Key interface instead of the KeyPair interface. The call looks something like this:
SecretKey key =
KeyGenerator.getInstance("DES").generateKey();

Next you need to create a Cipher. This is the workhorse for JCE. You again use a factory method of the Cipher class so that you can take advantage of different providers without changing the application. You create a Cipher like this:
Cipher cipher = Cipher.getInstance("DES");

A Cipher is used to encrypt and decrypt data that is passed in as byte arrays. The two essential methods you must use are init(), to specify which operation will be called, and doFinal(), to perform that operation. For example, the following two lines use the cipher and key instances you created to encrypt a byte array called textBytes. The result is stored in a byte array called encryptedBytes.
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes =
cipher.doFinal( textBytes );

Putting this together, the following program takes an input String and encrypts it. The encrypted String is then decrypted.
import javax.crypto.Cipher;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import java.security.Key;
import java.security.InvalidKeyException;

public class LocalEncrypter {

private static String algorithm = "DESede";
private static Key key = null;
private static Cipher cipher = null;

private static void setUp() throws Exception {
key = KeyGenerator.getInstance(algorithm).generateKey();
cipher = Cipher.getInstance(algorithm);
}

public static void main(String[] args)
throws Exception {
setUp();
if (args.length !=1) {
System.out.println(
"USAGE: java LocalEncrypter " +
"[String]");
System.exit(1);
}
byte[] encryptionBytes = null;
String input = args[0];
System.out.println("Entered: " + input);
encryptionBytes = encrypt(input);
System.out.println(
"Recovered: " + decrypt(encryptionBytes));
}

private static byte[] encrypt(String input)
throws InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] inputBytes = input.getBytes();
return cipher.doFinal(inputBytes);
}

private static String decrypt(byte[] encryptionBytes)
throws InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] recoveredBytes =
cipher.doFinal(encryptionBytes);
String recovered =
new String(recoveredBytes);
return recovered;
}
}

You can enter any text you like as a command line parameter. For example, if you submit the following on the command line:

java LocalEncrypter "Whatever phrase we would like to
input at this point"

You should see something like this as output:

Entered: Whatever phrase we would like to
input at this point
Recovered: Whatever phrase we would like to
input at this point

In this example, both the encryption and the decryption were done with the same Key object. Encryption and decryption ordinarily occur on different VMs at different times, so you need a method for securely transporting the key.

In the first tip you learned how to generate key pairs for asymmetric cipher algorithms. In the second tip, symmetric keys were used. Here's another technique, one that combines asymmetric and symmetric keys. In this technique a symmetric key is chosen at random and used to encrypt some data. The key itself is then encrypted using the other party's public key. The recipient then uses their private key to decrypt the symmetric key and then uses that decrypted key to decrypt the message. The modulus used in the asymmetric technique need only be large enough to encrypt the symmetric key. The symmetric key is used for a single transmission and then discarded. In this way, the weaknesses of each type are mitigated.

You can find out more about:

* DES Encryption
* Triple DES
* AES encryption (FIPS-197), which is proposed as a replacement for DES
* Blowfish

Copyright (c) 2004-2005 Sun Microsystems, Inc.
All Rights Reserved.

1 comment:

Unknown said...

That's an interesting process. I will try to execute the code you have posted to see how data is encrypted. Thank you very much for sharing this detail and program code.
digital signatures