JAVA SECURITY – PACKAGES JCA/JCE - Part II
Posted On June 19, 2007 by Raja Kishore Reddy filed under Java
It will be immediately evident that the names will be Greek and Latin to us, unless we have a background in Crypto terminology. That is why a broad outline was given in the previous section of this article. The function of some of the engines will be evident from out earlier discussion. A few more of the remaining items will be clear when we deal with code examples.
Cryptographic Engines
1. KeyGenerator (symmetric)
(Blowfish, DES, TripleDES, HmacMD5, HmacSHA1 and RC5);
2. KeyPairGenerator (asymmetric)
(DiffieHellman, DSA and RSA);
3. Mac (message authentication code)
(HmacMD5 and HmacSHA1);
4. MessageDigest
(MD5 and SHA1);
5. Signature
(MD5withRSA, SHA1withRSA and SHA1withDSA);
6. Cipher
(Blowfish, DES, TripleDES, etc.);
7. CertificateFactory
(X509);
8. KeyAgreement
(DiffieHellman);
9. KeyFactory;
10. SecretKeyFactory;
11. SecureRandom;
(SHA1PRNG, i.e. SHA1-pseudo-random-number-generator);
12. TrustManagerFactory;
13. KeyManagerFactory;
14. KeySore
(JKS and PHCS12);
15. SSLContext;
16. AlgorithmParameterGenerator; and
17. AlgorithmParameters.
Let us now see a series of code examples to get familiar with some of the above engines. For all the examples, we are using JDK1.4.2. Our working directory is g:\securitydemos.
cd to g:\securitydemos
We should set path as:
c:\windows\command;d:\jdk1.4.2\bin
The easiest to understand is the MessageDigest. Code 1 "demo1.java" creates the messagedigest of the string s1, by SHA (Secure Hash Algorithm) method. The given string is first converted into a byte array, because the function md.digest(), accepts only a bytearray.md.update() simply adds the array to existing arrays, if any. The digest object thus created is simply saved as object to the file.
Code 1: demo1.java
// creation of message-digest
// storing the string & digest in file
import java.io.*;
import java.security.*;
class demo1
{
public static void main(String args[])
{
try
{
MessageDigest md = MessageDigest.getInstance("SHA");
String s1 = " we are learning java";
byte[] array = s1.getBytes();
md.update(array);
FileOutputStream fos =
new FileOutputStream("demo1test");
ObjectOutputStream oos =
new ObjectOutputStream(fos);
oos.writeObject(s1);
oos.writeObject(md.digest ());
System.out.println(" digest ready!");
}catch(Exception e1)
{ System.out.println(""+e1);}
}
}
In Code 2, demo2.java, we learn how a given messagedigest can be used for checking the Integrity of data. We begin by getting the original string as well as the existing hash. Next, we create another hash of the original string by the same algorithm to get hash2. We then compare hash1 with hash2. If they are not equal, we get the message "corrupted". From these examples, it will be appreciated how much the Java API shields the programmer from the inner workings of the highly mathematical theory of Cryptology.
Code 2: demo2.java
// getting a string and digest from file
// creating a hash and verifying the digest
import java.io.*;
import java.security.*;
class demo2
{
public static void main(String args[])
{
try
{
FileInputStream fis =
new FileInputStream("demo1test");
ObjectInputStream ois =
new ObjectInputStream(fis);
Object ob1 = ois.readObject();
String s1 = (String) ob1;
System.out.println(s1);
Object ob2 = ois.readObject();
byte[] array1= (byte[]) ob2;
MessageDigest md =
MessageDigest.getInstance("SHA");
md.update(s1.getBytes());
if(MessageDigest.isEqual(md.digest(),
array1))
{ System.out.println("valid"); }
else
{ System.out.println("corrupted");
}
} catch(Exception e1)
{ System.out.println(""+e1);}
}
}
In the third example, code 3 (demo3.java), we see how the DES algorithm creates a secret key. Cipher class is the Encryption and Decryption engine. After initializing the Cipher engine for encrypting mode, we give the command ci.doFinal(). This creates the encrypted message of the specified string. We should also get the initvector, by the command ci.getIV().
To avoid writing a separate example, we also illustrate the process of decrypting here, in the next stage. We get the initvector and then define the cipher for decrypt mode. After this, ci.doFinal() does the decryption.
Code 3: demo3.java
// creation of secret key
// encryption using secret key
// decryption using secret key
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
class demo3
{
public static void main(String args[])
{
try
{
KeyGenerator kg =
KeyGenerator.getInstance("DES");
// DES= Data Encryption Standard
Key key = kg.generateKey();
Cipher ci =
Cipher.getInstance("DES/CBC/PKCS5Padding");
ci.init(Cipher.ENCRYPT_MODE, key);
String s ="we are learning Java";
byte[] array1 = s.getBytes();
byte[] array2 = ci.doFinal(array1);
byte[] initvector = ci.getIV();
System.out.println
("string has been encrypted");
System.out.println
("we are now decrypting");
IvParameterSpec spec =
new IvParameterSpec(initvector);
ci.init
(Cipher.DECRYPT_MODE, key, spec);
byte[] array3 = ci.doFinal(array2);
String s2 = new String(array3);
System.out.println(s2);
}
catch(Exception e1)
{ System.out.println(""+e1);}
}
}
Code 4 (demo4.java) displays information about the Providers available in JDK.
Code 4: demo4.java
import java.security.*;
import java.util.*;
class demo4
{
public static void main(String args[])
{
try
{
Provider[] array =
Security.getProviders();
int n = array.length;
for(int j=0; j<n; j++)
{
System.out.println(array[j]);
for(Enumeration e=array[j].keys();
e.hasMoreElements();)
{
System.out.println("\t"+e.nextElement());
}
}
} catch(Exception e1) {System.out.println(""+e1);}
}
}
When we execute the program, we get a long list. We can run the program as: >java demo4 > list.txt and then inspect list.txt.
In code 5 (demo5), we create a private/public keypair, using the DSA (Digital Signature Algorithm). KeyPairGeenrator class is used for that. By specifying a pseudo-random number, the keypair is easily generated by the command kpg.genKeyPair(). After this, we get the public key and private key. Next, we specify the Signature algorithm as SecureHashDSA. After initializing the sig with the private key, we create the byte array for the string to be signed and ask the sig to sign that array [sig.sign();].
Now the datastring has been signed by the sender's private key. In the next section, we have loaded sig with the public key and used the command to verify with the original array. This will return 'true' if the keys belong to the same pair. The code is almost self-explanatory, except for the syntax.
When we run demo5, we get the following output:
G:\SECURI~1>java demo5
public & private keys ready!
the data has been signed by private key
now verifying with public key
verified & found ok
Code5: demo5.java
// creation of public & private keys
// signing the data by private key
// verifying the data by public key
import java.security.*;
import javax.crypto.*;
class demo5
{
public static void main(String args[])
{
try
{
SecureRandom sr = new SecureRandom();
byte[] pr =new byte[100];
sr.nextBytes(pr);
KeyPairGenerator kpg =
KeyPairGenerator.getInstance("DSA");
kpg.initialize(512,sr);
KeyPair kp = kpg.genKeyPair();
PublicKey pubkey = kp.getPublic();
PrivateKey prikey = kp.getPrivate();
System.out.println
("public & private keys ready!");
Signature sig =
Signature.getInstance("SHA1withDSA");
sig.initSign(prikey);
String s1 = "we are learning Java";
byte[] array1= s1.getBytes();
sig.update(array1);
byte[] array2 = sig.sign();
System.out.println
("the data signed by private key");
System.out.println
("now verifying with public key");
sig.initVerify(pubkey);
sig.update(array1);
boolean ok = sig.verify(array2);
if(ok==true)
{ System.out.println("authentic..ok"); }
else
{ System.out.println("not authentic"); }
}
catch(Exception e1)
{ System.out.println(""+e1);}
}
}
We will conclude this tutorial with an illustration for Password-based secret key.
Users are supposed to remember their password, easily. If a secret key can be generated based on the password, it will be easier to generate. Users can then generate such a secret key and use it to encrypt and decrypt their files. The algorithm uses a 'salt' and an integer number for iteration. The paramspec is created using these two values. The Cipher engine for PBEWithMD5AndDES (PBE=Password-based-encryption, MD5=MessageDigest-5 &DES=DataEncryptionStandard) is then created. The program asks for a password and then the string to be encrypted. A secret key based on the password supplied is generated and used to encrypt the data.
Just for illustration, the program asks for the password again. When we type it correctly, the secret key is again generated and used to decrypt the encrypted data. Finally, the original string is printed.
The output of code 6 (demo6) is shown below (system prompt and output in bold):
G:\SECURI~1>java demo6
enter the password
abcd1234
enter the string to be encrypted
we are studying java cryptography
encryption over
now decrypting
enter the password again!
abcd1234
we are studying java cryptography
Code 6: demo6.java
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.io.*;
class demo6
{
public static void main(String args[])
{
String salt="saltings";
int n = 20; // iterations
byte[] a = salt.getBytes();
PBEParameterSpec paramspec=
new PBEParameterSpec (a,n);
try
{
Cipher cipher =
Cipher.getInstance("PBEWithMD5AndDES");
DataInputStream ins =
new DataInputStream(System.in);
System.out.println ("enter the password");
String s1=ins.readLine();
System.out.println("enter the datastring");
String s2=ins.readLine();
byte[] array1 = s2.getBytes();
PBEKeySpec keyspec =
new PBEKeySpec(s1.toCharArray());
SecretKeyFactory factory=
SecretKeyFactory.getInstance
("PBEWithMD5AndDES");
SecretKey key = factory.generateSecret(keyspec);
cipher.init
(Cipher.ENCRYPT_MODE, key, paramspec);
byte[] array2 = cipher.doFinal(array1);
System.out.println("encryption over");
System.out.println("now decrypting");
System.out.println
("enter the password again!");
String s3=ins.readLine();
keyspec =
new PBEKeySpec(s3.toCharArray());
factory= SecretKeyFactory.getInstance("PBEWithMD5AndDES");
key = factory.generateSecret(keyspec);
cipher.init
(Cipher.DECRYPT_MODE, key, paramspec);
byte[] array3 = cipher.doFinal(array2);
String s4=new String(array3);
System.out.println(s4);
}
catch(Exception e1)
{System.out.println(""+e1);}
}
}
That completes our introductory tutorial on JCA/JCE. All the above programs have been tested and work correctly. Students are encouraged to follow this up with the reference material mentioned below.
RERFERENCE BOOKS
1. Mastering Java Security (Cryptography, Algorithms & Architecture) by Rich Helton and Johennie Helton (Wiley/DreamTech).
2. Professional Java JDK 5 edition by Clay Richardson and others (Wrox/Wiley/DreamTech).
3. Java Security by Scott Oaks (SPD/OReilley).
4. Java Distributed Objects by Bill McCarty.
5. Professional Java WebServices by MackHendricks (SUN) and others, Chapter 6 on Security by James Milbury.
The author can be reached at rs.ramaswamy@gmail.com.
Cryptographic Engines
1. KeyGenerator (symmetric)
(Blowfish, DES, TripleDES, HmacMD5, HmacSHA1 and RC5);
2. KeyPairGenerator (asymmetric)
(DiffieHellman, DSA and RSA);
3. Mac (message authentication code)
(HmacMD5 and HmacSHA1);
4. MessageDigest
(MD5 and SHA1);
5. Signature
(MD5withRSA, SHA1withRSA and SHA1withDSA);
6. Cipher
(Blowfish, DES, TripleDES, etc.);
7. CertificateFactory
(X509);
8. KeyAgreement
(DiffieHellman);
9. KeyFactory;
10. SecretKeyFactory;
11. SecureRandom;
(SHA1PRNG, i.e. SHA1-pseudo-random-number-generator);
12. TrustManagerFactory;
13. KeyManagerFactory;
14. KeySore
(JKS and PHCS12);
15. SSLContext;
16. AlgorithmParameterGenerator; and
17. AlgorithmParameters.
Let us now see a series of code examples to get familiar with some of the above engines. For all the examples, we are using JDK1.4.2. Our working directory is g:\securitydemos.
cd to g:\securitydemos
We should set path as:
c:\windows\command;d:\jdk1.4.2\bin
The easiest to understand is the MessageDigest. Code 1 "demo1.java" creates the messagedigest of the string s1, by SHA (Secure Hash Algorithm) method. The given string is first converted into a byte array, because the function md.digest(), accepts only a bytearray.md.update() simply adds the array to existing arrays, if any. The digest object thus created is simply saved as object to the file.
Code 1: demo1.java
// creation of message-digest
// storing the string & digest in file
import java.io.*;
import java.security.*;
class demo1
{
public static void main(String args[])
{
try
{
MessageDigest md = MessageDigest.getInstance("SHA");
String s1 = " we are learning java";
byte[] array = s1.getBytes();
md.update(array);
FileOutputStream fos =
new FileOutputStream("demo1test");
ObjectOutputStream oos =
new ObjectOutputStream(fos);
oos.writeObject(s1);
oos.writeObject(md.digest ());
System.out.println(" digest ready!");
}catch(Exception e1)
{ System.out.println(""+e1);}
}
}
In Code 2, demo2.java, we learn how a given messagedigest can be used for checking the Integrity of data. We begin by getting the original string as well as the existing hash. Next, we create another hash of the original string by the same algorithm to get hash2. We then compare hash1 with hash2. If they are not equal, we get the message "corrupted". From these examples, it will be appreciated how much the Java API shields the programmer from the inner workings of the highly mathematical theory of Cryptology.
Code 2: demo2.java
// getting a string and digest from file
// creating a hash and verifying the digest
import java.io.*;
import java.security.*;
class demo2
{
public static void main(String args[])
{
try
{
FileInputStream fis =
new FileInputStream("demo1test");
ObjectInputStream ois =
new ObjectInputStream(fis);
Object ob1 = ois.readObject();
String s1 = (String) ob1;
System.out.println(s1);
Object ob2 = ois.readObject();
byte[] array1= (byte[]) ob2;
MessageDigest md =
MessageDigest.getInstance("SHA");
md.update(s1.getBytes());
if(MessageDigest.isEqual(md.digest(),
array1))
{ System.out.println("valid"); }
else
{ System.out.println("corrupted");
}
} catch(Exception e1)
{ System.out.println(""+e1);}
}
}
In the third example, code 3 (demo3.java), we see how the DES algorithm creates a secret key. Cipher class is the Encryption and Decryption engine. After initializing the Cipher engine for encrypting mode, we give the command ci.doFinal(). This creates the encrypted message of the specified string. We should also get the initvector, by the command ci.getIV().
To avoid writing a separate example, we also illustrate the process of decrypting here, in the next stage. We get the initvector and then define the cipher for decrypt mode. After this, ci.doFinal() does the decryption.
Code 3: demo3.java
// creation of secret key
// encryption using secret key
// decryption using secret key
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
class demo3
{
public static void main(String args[])
{
try
{
KeyGenerator kg =
KeyGenerator.getInstance("DES");
// DES= Data Encryption Standard
Key key = kg.generateKey();
Cipher ci =
Cipher.getInstance("DES/CBC/PKCS5Padding");
ci.init(Cipher.ENCRYPT_MODE, key);
String s ="we are learning Java";
byte[] array1 = s.getBytes();
byte[] array2 = ci.doFinal(array1);
byte[] initvector = ci.getIV();
System.out.println
("string has been encrypted");
System.out.println
("we are now decrypting");
IvParameterSpec spec =
new IvParameterSpec(initvector);
ci.init
(Cipher.DECRYPT_MODE, key, spec);
byte[] array3 = ci.doFinal(array2);
String s2 = new String(array3);
System.out.println(s2);
}
catch(Exception e1)
{ System.out.println(""+e1);}
}
}
Code 4 (demo4.java) displays information about the Providers available in JDK.
Code 4: demo4.java
import java.security.*;
import java.util.*;
class demo4
{
public static void main(String args[])
{
try
{
Provider[] array =
Security.getProviders();
int n = array.length;
for(int j=0; j<n; j++)
{
System.out.println(array[j]);
for(Enumeration e=array[j].keys();
e.hasMoreElements();)
{
System.out.println("\t"+e.nextElement());
}
}
} catch(Exception e1) {System.out.println(""+e1);}
}
}
When we execute the program, we get a long list. We can run the program as: >java demo4 > list.txt and then inspect list.txt.
In code 5 (demo5), we create a private/public keypair, using the DSA (Digital Signature Algorithm). KeyPairGeenrator class is used for that. By specifying a pseudo-random number, the keypair is easily generated by the command kpg.genKeyPair(). After this, we get the public key and private key. Next, we specify the Signature algorithm as SecureHashDSA. After initializing the sig with the private key, we create the byte array for the string to be signed and ask the sig to sign that array [sig.sign();].
Now the datastring has been signed by the sender's private key. In the next section, we have loaded sig with the public key and used the command to verify with the original array. This will return 'true' if the keys belong to the same pair. The code is almost self-explanatory, except for the syntax.
When we run demo5, we get the following output:
G:\SECURI~1>java demo5
public & private keys ready!
the data has been signed by private key
now verifying with public key
verified & found ok
Code5: demo5.java
// creation of public & private keys
// signing the data by private key
// verifying the data by public key
import java.security.*;
import javax.crypto.*;
class demo5
{
public static void main(String args[])
{
try
{
SecureRandom sr = new SecureRandom();
byte[] pr =new byte[100];
sr.nextBytes(pr);
KeyPairGenerator kpg =
KeyPairGenerator.getInstance("DSA");
kpg.initialize(512,sr);
KeyPair kp = kpg.genKeyPair();
PublicKey pubkey = kp.getPublic();
PrivateKey prikey = kp.getPrivate();
System.out.println
("public & private keys ready!");
Signature sig =
Signature.getInstance("SHA1withDSA");
sig.initSign(prikey);
String s1 = "we are learning Java";
byte[] array1= s1.getBytes();
sig.update(array1);
byte[] array2 = sig.sign();
System.out.println
("the data signed by private key");
System.out.println
("now verifying with public key");
sig.initVerify(pubkey);
sig.update(array1);
boolean ok = sig.verify(array2);
if(ok==true)
{ System.out.println("authentic..ok"); }
else
{ System.out.println("not authentic"); }
}
catch(Exception e1)
{ System.out.println(""+e1);}
}
}
We will conclude this tutorial with an illustration for Password-based secret key.
Users are supposed to remember their password, easily. If a secret key can be generated based on the password, it will be easier to generate. Users can then generate such a secret key and use it to encrypt and decrypt their files. The algorithm uses a 'salt' and an integer number for iteration. The paramspec is created using these two values. The Cipher engine for PBEWithMD5AndDES (PBE=Password-based-encryption, MD5=MessageDigest-5 &DES=DataEncryptionStandard) is then created. The program asks for a password and then the string to be encrypted. A secret key based on the password supplied is generated and used to encrypt the data.
Just for illustration, the program asks for the password again. When we type it correctly, the secret key is again generated and used to decrypt the encrypted data. Finally, the original string is printed.
The output of code 6 (demo6) is shown below (system prompt and output in bold):
G:\SECURI~1>java demo6
enter the password
abcd1234
enter the string to be encrypted
we are studying java cryptography
encryption over
now decrypting
enter the password again!
abcd1234
we are studying java cryptography
Code 6: demo6.java
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.io.*;
class demo6
{
public static void main(String args[])
{
String salt="saltings";
int n = 20; // iterations
byte[] a = salt.getBytes();
PBEParameterSpec paramspec=
new PBEParameterSpec (a,n);
try
{
Cipher cipher =
Cipher.getInstance("PBEWithMD5AndDES");
DataInputStream ins =
new DataInputStream(System.in);
System.out.println ("enter the password");
String s1=ins.readLine();
System.out.println("enter the datastring");
String s2=ins.readLine();
byte[] array1 = s2.getBytes();
PBEKeySpec keyspec =
new PBEKeySpec(s1.toCharArray());
SecretKeyFactory factory=
SecretKeyFactory.getInstance
("PBEWithMD5AndDES");
SecretKey key = factory.generateSecret(keyspec);
cipher.init
(Cipher.ENCRYPT_MODE, key, paramspec);
byte[] array2 = cipher.doFinal(array1);
System.out.println("encryption over");
System.out.println("now decrypting");
System.out.println
("enter the password again!");
String s3=ins.readLine();
keyspec =
new PBEKeySpec(s3.toCharArray());
factory= SecretKeyFactory.getInstance("PBEWithMD5AndDES");
key = factory.generateSecret(keyspec);
cipher.init
(Cipher.DECRYPT_MODE, key, paramspec);
byte[] array3 = cipher.doFinal(array2);
String s4=new String(array3);
System.out.println(s4);
}
catch(Exception e1)
{System.out.println(""+e1);}
}
}
That completes our introductory tutorial on JCA/JCE. All the above programs have been tested and work correctly. Students are encouraged to follow this up with the reference material mentioned below.
RERFERENCE BOOKS
1. Mastering Java Security (Cryptography, Algorithms & Architecture) by Rich Helton and Johennie Helton (Wiley/DreamTech).
2. Professional Java JDK 5 edition by Clay Richardson and others (Wrox/Wiley/DreamTech).
3. Java Security by Scott Oaks (SPD/OReilley).
4. Java Distributed Objects by Bill McCarty.
5. Professional Java WebServices by MackHendricks (SUN) and others, Chapter 6 on Security by James Milbury.
The author can be reached at rs.ramaswamy@gmail.com.
