Sunday, November 2, 2008

::Basic Java Cryptography, How to Program::

Bijairimi Abdul Awal, Romain Babel: DSV KTH, 2006


Intro

This article is about basic message confidentiality. When we talk about confidentiality, we would eventually run into cryptography, which involves two processes; encryption and decryption. In this article I write a code that implements two cryptographic algorithms; DES and RSA, together with Base64 encoding standard which is used to encode data traverses over the Internet.

Selected algorithms

Secret key cryptography

DES stands for “Data Encryption Standard” and is the archetypal block cipher; which means that the algorithm takes a fixedlength string of plaintext bits and transforms it through a series of complicated operations into another cipher text bit string of the same length. The transformation is performed thanks to the secret key. Then the sender and the receiver will share the secret key. This key has both encryption and decryption feature.
Public key cryptography

RSA is named after its creator Dr. Ronald L. Rivest, Dr. Adi Shamir, and Dr. Leonard M. Adleman. The public key cryptography use two keys; public and private. The sender messages will be encrypted by using the target/receiver's public key and only can be decrypted back by the receiver, by using his/her private key. The security of the public key cryptography lies on the difficulties of factoring large numbers.

Base64 encoding

Base64 encoding/decoding is used to encode the arbitrary data into the plain ASCII text, and to decode the plain ASCII text back into the original form of data. By using Base64 encoding, all types of data are possible to be sent through email via the Internet. Base64 encoding is very important to make sure the arbitrary data that is generated and been sent from one platform of system such as Unix, is readable and accessible by the receiver which might be use a different kind of system such as Windows.

Implementation and description of the code

Both, DES and RSA encryption code is compiled and built on Sun Java 1.5 and eclipse platform version 3.0.1 and for Base64 encoding, I use an already written public class Base64 by Robert Harder.

DES

First the secret shared key is generated,

key = KeyGenerator.getInstance("DES").generateKey();

and then stored into the value key. Now select DES algorithm, CBC mode and PKCS5 padding for ecryption and decryption:

Cipher.getInstance("DES/CBC/PKCS5Padding");

A Cipher object obtained from getInstance must be initialized in one of the two modes (encoding or decoding), which is defined like final constant integer in the Cipher class. The reference name for these modes are:

  • ENCRYPT_MODE
  • DECRYPT_MODE

Each method of initialization takes a parameter mode (opmode) and initializes the Cipher object for this mode. Other parameters include the key (key) and parameters of the algorithm (params):

encrypt.init(Cipher.ENCRYPT_MODE, key, paramSpec);
decrypt.init(Cipher.DECRYPT_MODE, key, paramSpec);

Now we can use those variables to write the encryption code which will retrieve cleartext data from an input file and convert it into ciphertext and write it into an output file:

public void encryption(InputStream cleartext, OutputStream ciphertext) {
try {
ciphertext = new CipherOutputStream(ciphertext, encrypt);
int numRead = 0;
while ((numRead = cleartext.read(buffer)) >= 0) {
ciphertext.write(buffer, 0, numRead);
}
ciphertext.flush();
ciphertext.close();
} catch (java.io.IOException e){
}
}

The decryption it is the same as the encryption. The method reads each character, decrypt it, and write the decrypted text into a file (the file name is “cleartext”):

public void decryption(InputStream ciphertext, OutputStream cleartext) {
try {
ciphertext = new CipherInputStream(ciphertext, decrypt);
int numRead = 0;
while ((numRead = ciphertext.read(buffer)) >= 0) {
cleartext.write(buffer, 0, numRead);
}
cleartext.flush();
cleartext.close()
} catch (java.io.IOException e) {
}
}

In my test function, first apply the Base64 encoding standard

InputStream in = new Base64.InputStream(new FileInputStream("desCleartext.txt"), Base64.ENCODE);
FileOutputStream out = new FileOutputStream("desEncodedStream1.txt");
int numRead = 0;
while ((numRead = in.read(buffer)) >= 0)
{
out.write(buffer, 0, numRead);
}
out.close();

then encrypt the encoded file using DES algo:

des.encryption(new FileInputStream("desEncodedStream1.txt"), new
FileOutputStream("desCiphertext.txt"));

Now it's done! the base64 encoded data is encrypted using DES algorithm and the file (desCiphertext.txt) is ready to be sent over the network.

On the other hand the decryption is a backward of the encryption process: it means that the receiver decyrpts the file using the same algorithm and then decode it back using Base64 encoding:

des.decryption(new FileInputStream("desCiphertext.txt"), new FileOutputStream("desEncodedStream2.txt"));
try
{
InputStream in = new Base64.InputStream(newFileInputStream("desEncodedStream2.txt"), Base64.DECODE);
FileOutputStream out = new FileOutputStream("desDecodedStream.txt");
int numRead = 0;
while ((numRead = in.read(buffer)) >= 0)
{
out.write(buffer, 0, numRead);
}
out.close();
}

RSA

The public class and the main method is written in two separate files; RSAencryption.java and testRSAencryption.java. To program the RSA in java, we use the java.security package.

The main method is located in testRSAencryption.java file. In this main method, a key pair (public key and private key) will be created by using generateKeyPair() method. The step of creating a key pair is shown as
below:

KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair key = keyGen.generateKeyPair();

Then the key pair is passed to the public class RSAencryption to be used in encryption and decryption process.

RSAencryption rsa = new RSAencryption(key);

After creating a key pair, we encode the arbitrary data using the code below:

InputStream in = new Base64.InputStream(new FileInputStream("rsaCleartext.txt"), Base64.ENCODE);
FileOutputStream out = new FileOutputStream("rsaEncodedStream1.txt");
int numRead = 0;
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);
}
out.close();

This code will call the InputStream() method in the public class Base64. The input file rsaCleartext.txt, and the encoded data will be saved in the output file rsaEncodedStream1.txt. Then, the encoded data will be encrypted using RSA algorithm, and the encrypted data will be written in the output file rsaCiphertext.txt.

rsa.encryption(new FileInputStream("rsaEncodedStream1.txt"), new FileOutputStream("rsaCiphertext.txt"));

The encrypted data then will be decrypted and the result will be saved in the output file rsaEncodedStream2.txt.

rsa.decryption(new FileInputStream("rsaCiphertext.txt"), new FileOutputStream("rsaEncodedStream2.txt"));

The contents of both file rsaEncodedStream1.txt and rsaEncodedStream2.txt must be same. To test this we run the diff command. Finally, the encoded data will be decoded back to its original data:

InputStream in = new Base64.InputStream(new FileInputStream("rsaEncodedStream2.txt"), Base64.DECODE);
FileOutputStream out = new FileOutputStream("rsaDecodedStream.txt");
int numRead = 0;
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);
}
out.close();

Sample codes:

DesEncryption.java

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.io.*;
import java.security.spec.AlgorithmParameterSpec;
public class DESencryption {
private Cipher encrypt;
private Cipher decrypt;
private SecretKey key;
private byte[] buffer = new byte[1024];
   public DESencryption() {
try {
key = KeyGenerator.getInstance("DES").generateKey();
} catch (java.security.NoSuchAlgorithmException e){
}
      byte[] iv = new byte[]{
(byte)0x8E, 0x12, 0x39, (byte)0x9C,
0x07, 0x72, 0x6F, 0x5A
};
      AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
try {
encrypt = Cipher.getInstance("DES/CBC/PKCS5Padding");
decrypt = Cipher.getInstance("DES/CBC/PKCS5Padding");
encrypt.init(Cipher.ENCRYPT_MODE, key, paramSpec);
decrypt.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (java.security.InvalidAlgorithmParameterException e) {
} catch (javax.crypto.NoSuchPaddingException e) {
} catch (java.security.NoSuchAlgorithmException e) {
} catch (java.security.InvalidKeyException e) {
}
}
   public void encryption(InputStream cleartext, OutputStream ciphertext) {
try {
ciphertext = new CipherOutputStream(ciphertext, encrypt);
         int numRead = 0;
while ((numRead = cleartext.read(buffer)) >= 0) {
ciphertext.write(buffer, 0, numRead);
}
ciphertext.flush();
ciphertext.close();
} catch (java.io.IOException e) {
}
}
   public void decryption(InputStream ciphertext, OutputStream cleartext) {
try {
ciphertext = new CipherInputStream(ciphertext, decrypt);
         int numRead = 0;
while ((numRead = ciphertext.read(buffer)) >= 0) {
cleartext.write(buffer, 0, numRead);
}
cleartext.flush();
cleartext.close();
} catch (java.io.IOException e) {
}
}
   public static void main(String args[]){
try {
DESencryption des = new DESencryption();
         byte[] buffer = new byte[1024];
         // Encode the cleartext to Base64 encoding
try {
InputStream in = new Base64.InputStream(new FileInputStream("desCleartext.txt"), Base64.ENCODE);
FileOutputStream out = new FileOutputStream("desEncodedStream1.txt");
            int numRead = 0;
while ((numRead = in.read(buffer)) >= 0) {
out.write(buffer, 0, numRead);
}
out.close();
}
catch (java.io.FileNotFoundException e){}
catch (java.io.IOException e){}
         // Encrypt the Base64 encoded data
des.encryption(new FileInputStream("desEncodedStream1.txt"), new FileOutputStream("desCiphertext.txt"));
         // Decrypt the encrypted Base 64 encoded data
des.decryption(new FileInputStream("desCiphertext.txt"), new FileOutputStream("desEncodedStream2.txt"));
         // Decode the Base64 encoded data
try {
InputStream in = new Base64.InputStream(new FileInputStream("desEncodedStream2.txt"), Base64.DECODE);
FileOutputStream out = new FileOutputStream("desDecodedStream.txt");
            int numRead = 0;
while ((numRead = in.read(buffer)) >= 0) {
out.write(buffer, 0, numRead);
}
out.close();
}
catch (java.io.FileNotFoundException e){}
catch (java.io.IOException e){}
} catch (Exception e) {
}
}
}

RsaEncryption.java

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import java.io.*;
import java.security.*;
public class RSAencryption {
Cipher encrypt;
Cipher decrypt;
   public RSAencryption(KeyPair key) {
try {
encrypt = Cipher.getInstance("RSA/ECB/PKCS1Padding");
decrypt = Cipher.getInstance("RSA/ECB/PKCS1Padding");
         encrypt.init(Cipher.ENCRYPT_MODE, key.getPublic());
decrypt.init(Cipher.DECRYPT_MODE, key.getPrivate());
} catch (java.security.NoSuchAlgorithmException e){
} catch (javax.crypto.NoSuchPaddingException e) {
} catch (java.security.InvalidKeyException e) {
}
}
   byte[] buffer = new byte[1024];
   // do the encryption
public void encryption(InputStream cleartext, OutputStream ciphertext) {
try {
ciphertext = new CipherOutputStream(ciphertext, encrypt);
         int numRead = 0;
while ((numRead = cleartext.read(buffer)) >= 0) {
ciphertext.write(buffer, 0, numRead);
}
ciphertext.close();
} catch (java.io.IOException e) {
}
}
   // do the decryption
public void decryption(InputStream ciphertext, OutputStream cleartext) {
try {
ciphertext = new CipherInputStream(ciphertext, decrypt);
         int numRead = 0;
while ((numRead = ciphertext.read(buffer)) >= 0) {
cleartext.write(buffer, 0, numRead);
}
cleartext.close();
} catch (java.io.IOException e) {
}
}
}

testRsaEncryption.java

import java.io.*;
import java.security.*;
public class testRSAencryption {
   public static void main(String args[]){
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair key = keyGen.generateKeyPair();
         RSAencryption rsa = new RSAencryption(key);
         byte[] buf = new byte[1024];
         // Encode the cleartext to Base64 encoding
try {
InputStream in = new Base64.InputStream(new FileInputStream("rsaCleartext.txt"), Base64.ENCODE);
FileOutputStream out = new FileOutputStream("rsaEncodedStream1.txt");
            int numRead = 0;
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);
}
out.close();
} catch (java.io.FileNotFoundException e){
} catch (java.io.IOException e){
}
         // Encrypt the Base64 encoded data
rsa.encryption(new FileInputStream("rsaEncodedStream1.txt"), new FileOutputStream("rsaCiphertext.txt"));
         // Decrypt the encrypted Base 64 encoded data
rsa.decryption(new FileInputStream("rsaCiphertext.txt"), new FileOutputStream("rsaEncodedStream2.txt"));
         // Decode the Base64 encoded data
try {
InputStream in = new Base64.InputStream(new FileInputStream("rsaEncodedStream2.txt"), Base64.DECODE);
FileOutputStream out = new FileOutputStream("rsaDecodedStream.txt");
            int numRead = 0;
while ((numRead = in.read(buf)) >= 0) {
out.write(buf, 0, numRead);
}
out.close();
} catch (java.io.FileNotFoundException e){
} catch (java.io.IOException e){
}
} catch (Exception e) {
}
} // end public static void main
} // end public class testRSAencryption

2 comments:

sufian said...

please put little intro on ur main page..if anybody want to view complete post..he can click on "Read More On This Article".. more easy to read for readers

Amber Salm said...

Wow. This is so nice of you. You have given the complete program that depicts how cryptography functions. I am bookmarking this article so that I can study it later on.
e signatures