Monday, February 23, 2009

Security Made Easy

This article introduces some Java security API usage and provides simple solutions and code samples to solve common messaging security issues. These issues are becoming more important when programming in the web/cloud environment.

The first thing many of us would think of when talking about communication security is to use SSL/TLS/HTTPS technology. And in deed, this probably is the most popular mechanism used in today's enterprise and web applications.

The SSL/HTTPS provides a secure communication channel to authenticate the server and/or client (if client also presents its own certificate). If successfully connected (authenticated), the information (messages) transmission in between the two communication parties can not be read (privacy) or modified (integrity).

The technology is well established today. Tutorials and code samples can be obtained from many related books or articles. So we will not repeat the topic here. For example, the following link provides useful references for Java in this topic.

http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html


As the messaging providers move to the web (or cloud), SSL/HTTPS may not be enough to address all the security requirements. For example, messages received by a service may be stored in a file or a database until consumed (or removed at a later time).

The information may be compromised, if not protected, while it is stored at the persistent storage.

The rest of the article addresses the issue with a famous "Bob and Alice" security example as described below:

Bob wanted to send a secret message to Alice with the following criteria.
  • Alice can validate that the message is authentic (authored by Bob).
  • Alice can validate that the message is not tampered (while persisted in store).
  • Only Bob and Alice can understand (decrypt) the content of the message.
A simple solution for the above requirement is to use the public-key infrastructure and a hybrid cryptosystem.
(http://en.wikipedia.org/wiki/Hybrid_cryptosystem)

Before a message is sent, Bob does the following to sign and encrypt the message.
  • Generate a SecretKey (symmetric key) and encrypt the message with the SecretKey. The encrypted message is a byte[] (encryptedMessage).
  • Sign the (clear) message with Bob's private key. The signature is a byte[] (messageSignatureFromBob).
  • Encrypt the secret key with Alice's public key. The encrypted secret key is a byte[] (encryptedSecretKey).
The BOLD information above are to be sent to Alice as the "Secured Message". In other words, the message to be sent contains at least the three components: (encryptedMessage), (messageSignatureFromBob), and (encryptedSecretKey).

When a "Secured Message" is received, Alice does the following to verify the signature and decrypt the message.
  • Decrypt the encryptedSecretKey with Alice's private key. Alice reconstructs the SecretKey after the key is decrypted.
  • Decrypt the encryptedMessage with the reconstructed SecretKey.
  • Verify the messageSignatureFromBob with Bob's public key.
The following Java code samples exercised the above Alice/Bob message exchange scenario.

0. Certificate configurations. Bob and Alice each has its own certificate (and its own private key). Bob and Alice also obtained each other's certificate in their keystore.

Below are examples to create a self-signed certificate, import, and export certificates.

0.1 Example to create a self-signed certificate for bob.

keytool -genkeypair -dname "cn=bob, ou=em, o=em, c=us" -alias bob -keyalg RSA -keypass bobpass -keystore /keystores/bob/keystore -storepass bobpass -validity 180
Note: The -keyalg option is used because the default uses DSA algorithm,
but the DSA key algorithm is not supported in Sun's JDK 1.6.x Cipher implementation.

0.2 Example to export bob's certificate:

keytool -exportcert -alias bob -keystore /keystores/bob/keystore -file bob.cer
0.3 Example to import Alice's certificate into Bob's keystore:

keytool -keystore /keystores/bob/keystore -importcert -file alice.cer -storepass bobpass

Alice would do similar certificate setups as Bob's configurations above.

Link to Java keytool usage:
http://java.sun.com/javase/6/docs/technotes/tools/windows/keytool.html


1. Bob does the following to generate the SecretKey, sign the message, and encrypt the message.

1.1 Obtain Alice's public key.

private static final String BobKeyStorePath = "/keystores/bob/keystore";

//get bob's keystore password
byte[] bobpass = getBobPasword();

//create key store instance
java.security.KeyStore bobKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
java.io.FileInputStream fis = new java.io.FileInputStream(BobKeyStorePath);

//load the key store
bobKeyStore.load(fis, bobpass);

//get alice's certificate
Certificate aliceCert= bobKeyStore.getCertificate("alice");

//get alice's public key
java.security.PublicKey alicePublicKey = aliceCert.getPublicKey();

1.2 Generate a secret key (symmetric key)

//generate a secret key with triple-DES algorithm
javax.crypto.KeyGenerator keygen = KeyGenerator.getInstance("DESede");

//this is used to encrypt msg
javax.crypto.SecretKey secretKey = keygen.generateKey();

1.3 Encrypt the SecretKey with Alice's public key.

//get a new cipher instance with RSA as the transformation name
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, alicePublicKey);

//this is the encrypted secret key
byte[] encryptedSecretKey = cipher.doFinal(secretKey.getEncoded());

1.4 Sign the message.

The signature process is to ensure that a message received by Alice was indeed sent by Bob. The signature also serves the purpose that Bob can not deny that he did not send the message.

//get bob's private key entry
KeyStore.PasswordProtection kp = new KeyStore.PasswordProtection (bobpass);
KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) bobKeyStore.getEntry("bob", kp);

//get bob's private key
java.security.PrivateKey bobPrivateKey = pkEntry.getPrivateKey();

//get a signature object for bob
java.security.Signature bobSignature = Signature.getInstance("SHA1withDSA");

//initialize the signature object with bob's private key,
//it is now ready to use
bobSignature.initSign(bobPrivateKey);

//message to be sent to Alice
String message = "message to Alice";

//encode the clear message with "utf8" encoding
byte[] data = message.getBytes("utf8");

//update data to signed by bob's signature object
bobSignature.update(data);

//sign the data.
byte[] messageSignatureFromBob = bobSignature.sign();

1.5. Encrypt the message using the secret key generated.

//get a cipher object with triple-DES algorithm
Cipher cipher = Cipher.getInstance("DESede");

//initialize the cipher object with geberated secret key (symmetric key)
cipher.init(Cipher.ENCRYPT_MODE, secretKey);

//encrypt the message
byte[] encryptedMessage = cipher.doFinal(data);


2. Alice does the following to verify the signature, decrypt the SecretKey, and decrypt the message.

2.1 Obtain Alice's private key.

private static final String AliceKeyStorePath = "/keystores/alice/keystore";
//get alice's keystore password
byte[] alicepass = getAlicePasword();

//create key store instance
java.security.KeyStore aliceKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
java.io.FileInputStream fis = new java.io.FileInputStream(AliceKeyStorePath);

//load the key store
aliceKeyStore.load(fis, alicepass);

//get alice private key entry
KeyStore.PasswordProtection kp = new KeyStore.PasswordProtection (alicepass);
KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) aliceKeyStore.getEntry("alice", kp);

//get alice's private key
java.security.PrivateKey alicePrivateKey = pkEntry.getPrivateKey();

2.2 Decrypt the SecretKey with alice's private key.

Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, alicePrivateKey);

//this is to be sent to receiver along with the encrypted msg
byte[] encodedSecretKey = cipher.doFinal(encryptedSecretKey);

//build the key
SecretKey secretKey= new SecretKeySpec (encodedSecretKey, "DESede") ;

2.3 Decrypt the message with the SecretKey.

//decrypt data
Cipher cipher2 = Cipher.getInstance("DESede");
cipher2.init(Cipher.DECRYPT_MODE, secretKey);

//decrypt message
byte[] data = cipher2.doFinal(encryptedMessage );

String message = new String (data, "utf8");


2.4 Verify signature with Bob's public key.

//get Bob's certificate
Certificate certificate = aliceKeyStore.getCertificate("bob");

//get bob's public key
PublicKey publicKey = certificate.getPublicKey();

//get signature object instance
java.security.Signature signature = Signature.getInstance("SHA1withDSA");

//initialize with bob's public key
signature.initVerify(publicKey);

//update with data obtained in 2.2 (decrypted message)
signature.update(data);

//returns true if the signature is valid
boolean isValidSignature = signature.verify(messageSignatureFromBob );

No comments:

Post a Comment