Tuesday, 11 March 2014

Symmetric + Asymmetric + Signature

This type of security is more strong and secure then asymmetric key encryption. We have already seen how we can combine symmetric and asymmetric key encryption here. In this post we add more security by adding the concept of Digital Signature. In Symmetric + Asymmetric key we encrypt the symmetric key with the public key of a asymmetric key to make the symmetric key an message more secure, but still the authenticity of the sender of the encrypted symmetric key is not verified. This can be achieved by using Digital signature. To understand Digital signature first refer this post .




In this post additional to combining symmetric and asymmetric key encryption we also use the senders private key to sign the encrypted symmetric key. At the receiving end the receiver can validate the identity of the sender by validating the signature of the sender by using the public key of the sender. For generating private key / public key pairs and creating keystores and trustores refer here.

Please note that a encryption is done using public key and signing a data is done using private key.

Below code in Java

For a maven project you will need the apache-common-codec jar as a dependency or else just download the jar and paste it in the folder which is in the classpath.

Maven dependency:
<dependency>
 <groupId>commons-codec</groupId>
 <artifactId>commons-codec</artifactId>
 <version>1.8</version>
</dependency>


Symmetric_AsymmetricKey_Signature.java

package symmetric_assymetric_signature;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.apache.commons.codec.binary.Base64;

public class Symmetric_AsymmetricKey_Signature {

 public static void main(String[] args) throws KeyStoreException,
   NoSuchAlgorithmException, CertificateException, IOException,
   NoSuchPaddingException, IllegalBlockSizeException,
   BadPaddingException, InvalidKeyException,
   UnrecoverableKeyException, NoSuchProviderException,
   SignatureException {

  /**
   * At the client side
   */
  KeyStoreUtil keyStoreUtil = new KeyStoreUtil();
  // Generate Symmetric key
  byte[] symmetrickey = keyStoreUtil.generateSessionKey();
  // Encrypt data by symmetric key
  String data = "ABC";
  System.out.println("Data : " + data);
  String encryptedData = keyStoreUtil.encryptWithAESKey(data,
    symmetrickey);
  System.out.println("encryptedData : " + encryptedData);

  // Encrypt symmetric key using trustore
  String encryptedKey = keyStoreUtil.encryptKey(symmetrickey, "mykey");
  System.out.println("encryptedKey : " + encryptedKey);

  // Generate the signature of symmetric key
  byte[] signedEncryptedKey = keyStoreUtil.signData(Base64
    .decodeBase64(encryptedKey));
  String signatureOfKey = Base64.encodeBase64String(signedEncryptedKey);
  System.out.println("Signed Ecrypted Key : " + signatureOfKey);

  /**
   * At the receiver
   */

  // Verify signature
  keyStoreUtil.verifySignature(Base64.decodeBase64(encryptedKey),
    signedEncryptedKey);

  // Decrypt symmteric key using keystore
  String decryptedKey = keyStoreUtil.decryptKey(encryptedKey, "mykey");
  System.out.println("decryptedKey : " + decryptedKey);

  // Decrypt data using decrypted symetric key

  System.out.println("Decrypted Data : "
    + keyStoreUtil.decryptWithAESKey(encryptedData, decryptedKey));

 }
}


Utility class KeyStoreUtil.java

package symmetric_assymetric_signature;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

public class KeyStoreUtil {

 private static KeyStore wcTrustStore;
 private static KeyStore visaKeyStore;

 private static String visaKeyStorePass;
 private static String wcTrustStorePassword;
 private static Cipher dCipher;
 private static Cipher eCipher;

 public KeyStoreUtil() throws KeyStoreException, NoSuchAlgorithmException,
   CertificateException, IOException {

  loadTrustStore();
  loadKeyStore();
 }

 private void loadTrustStore() throws KeyStoreException,
   NoSuchAlgorithmException, CertificateException, IOException {
  wcTrustStore = KeyStore.getInstance("JCEKS");
  wcTrustStorePassword = "password";
  // String filePath = wcCryptoConfig.getTrustStoreFileLocation() +
  // File.separator + wcCryptoConfig.getTrustStoreFile();
  FileInputStream stream = new FileInputStream("pptruststore.jck");
  wcTrustStore.load(stream, wcTrustStorePassword.toCharArray());
 }

 private void loadKeyStore() throws KeyStoreException,
   NoSuchAlgorithmException, CertificateException, IOException {
  visaKeyStore = KeyStore.getInstance("JCEKS");
  visaKeyStorePass = "password";
  // String filePath = wcCryptoConfig.getKeyStoreFileLocation() +
  // File.separator + wcCryptoConfig.getKeyStoreFile();
  FileInputStream stream = new FileInputStream("ppkeystore.jck");
  visaKeyStore.load(stream, visaKeyStorePass.toCharArray());

 }

 public String decrypt(String encryptedString, String keyAlias)
   throws KeyStoreException, UnrecoverableKeyException,
   InvalidKeyException, NoSuchAlgorithmException,
   NoSuchPaddingException, IllegalBlockSizeException,
   BadPaddingException {
  Key key = visaKeyStore.getKey(keyAlias, visaKeyStorePass.toCharArray());
  dCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
  dCipher.init(Cipher.DECRYPT_MODE, key);
  return new String(dCipher.doFinal(Base64.decodeBase64(encryptedString
    .getBytes())));
 }

 public String encrypt(byte[] inputString, String keyAlias)
   throws KeyStoreException, UnrecoverableKeyException,
   InvalidKeyException, NoSuchAlgorithmException,
   NoSuchPaddingException, IllegalBlockSizeException,
   BadPaddingException {
  Key key = null;
  // Key key = scsTrustStore.getKey(keyAlias, scsKeyStorePass);
  if (wcTrustStore.isCertificateEntry(keyAlias)) {
   Certificate cert = wcTrustStore.getCertificate(keyAlias);
   key = cert.getPublicKey();
  } else {
   key = wcTrustStore.getKey(keyAlias, wcTrustStorePassword
     .toCharArray());
  }

  eCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
  eCipher.init(Cipher.ENCRYPT_MODE, key);
  return Base64.encodeBase64String(eCipher.doFinal(inputString));
 }

 public byte[] generateSessionKey() throws NoSuchAlgorithmException {
  KeyGenerator generator = KeyGenerator.getInstance("AES");
  generator.init(128);
  SecretKey key = generator.generateKey();
  System.out.println("key size (in bytes):" + key.getEncoded().length);
  return (key.getEncoded());
 }

 public String encryptWithAESKey(String data, byte[] key)
   throws NoSuchAlgorithmException, NoSuchPaddingException,
   InvalidKeyException, IllegalBlockSizeException,
   BadPaddingException, UnsupportedEncodingException {
  SecretKey secKey = new SecretKeySpec(key, 0, 16, "AES");
  System.out.println("Secret key size (in bytes):"
    + secKey.getEncoded().length);
  Cipher cipher = Cipher.getInstance("AES");
  cipher.init(Cipher.ENCRYPT_MODE, secKey);
  byte[] newData = cipher.doFinal(data.getBytes());
  return Base64.encodeBase64String(newData);
 }

 public String decryptWithAESKey(String inputData, String key)
   throws NoSuchAlgorithmException, NoSuchPaddingException,
   InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
  Cipher cipher = Cipher.getInstance("AES");
  SecretKey secKey = new SecretKeySpec(Base64
    .decodeBase64(key.getBytes()), "AES");

  cipher.init(Cipher.DECRYPT_MODE, secKey);
  byte[] newData = cipher.doFinal(Base64.decodeBase64(inputData
    .getBytes()));
  return new String(newData);

 }

 /**
  * 
  * @param encryptedKey
  * @param keyAlias
  * @return
  * @throws KeyStoreException
  * @throws UnrecoverableKeyException
  * @throws InvalidKeyException
  * @throws NoSuchAlgorithmException
  * @throws NoSuchPaddingException
  * @throws IllegalBlockSizeException
  * @throws BadPaddingException
  */
 public String decryptKey(String encryptedKey, String keyAlias)
   throws KeyStoreException, UnrecoverableKeyException,
   InvalidKeyException, NoSuchAlgorithmException,
   NoSuchPaddingException, IllegalBlockSizeException,
   BadPaddingException {
  Key key = visaKeyStore.getKey(keyAlias, visaKeyStorePass.toCharArray());
  dCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
  dCipher.init(Cipher.DECRYPT_MODE, key);
  byte[] decryptedKey = dCipher.doFinal(Base64.decodeBase64(encryptedKey
    .getBytes()));
  SecretKey secKey = new SecretKeySpec(decryptedKey, "AES");
  return new String(Base64.encodeBase64String(secKey.getEncoded()));

 }

 /**
  * 
  * @param key
  * @param keyAlias
  * @return
  * @throws KeyStoreException
  * @throws UnrecoverableKeyException
  * @throws InvalidKeyException
  * @throws NoSuchAlgorithmException
  * @throws NoSuchPaddingException
  * @throws IllegalBlockSizeException
  * @throws BadPaddingException
  */
 public String encryptKey(byte[] key, String keyAlias)
   throws KeyStoreException, UnrecoverableKeyException,
   InvalidKeyException, NoSuchAlgorithmException,
   NoSuchPaddingException, IllegalBlockSizeException,
   BadPaddingException {

  return encrypt(key, keyAlias);

 }

 public byte[] signData(byte[] key) throws NoSuchAlgorithmException,
   NoSuchProviderException, UnrecoverableKeyException,
   KeyStoreException, InvalidKeyException, SignatureException {
  Signature dsa = Signature.getInstance("MD5withRSA");
  PrivateKey priv = (PrivateKey) visaKeyStore.getKey("mykey", "password"
    .toCharArray());
  dsa.initSign(priv);
  dsa.update(key);
  byte[] realSig = dsa.sign();
  return realSig;
 }

 public void verifySignature(byte[] key, byte[] sigToVerify ) throws NoSuchAlgorithmException,
   KeyStoreException, InvalidKeyException, SignatureException {
  Signature sig = Signature.getInstance("MD5withRSA");

  java.security.cert.Certificate cert =  wcTrustStore.getCertificate("mykey");
  PublicKey publicKey = cert.getPublicKey();
  
  sig.initVerify(publicKey);
  sig.update(key);
  boolean verifies = sig.verify(sigToVerify);
  System.out.println("signature verified:"+verifies);
 }

}


Output

key size (in bytes):16
Data : ABC
Secret key size (in bytes):16
encryptedData : FpNPg2fG4HP6MN/OACVKVA==
encryptedKey : Q1yj4FmOaslOX/jp3BgRx7RPCyUoLEc17Ze58APcY7sIh6+b8bW4ouAIOF2p7jCqSE4UX4F8w/4t4xw7LKKItNMr3Sa39eaYkMuPqYFtwUVjiaHGLjIPojcNwjRfiAy2RC93DYdhwI5ENluDkQRxus9lsg8zYYLytBLAgSPmjTA=
Signed Ecrypted Key : HWppc2xLU/JLSWpvjh+kyqzRmxnWSbfoVFU/9DWY4KwOZAnYvTUv+pLTt4J6WDuyz/3M2I721UqKgAfN4AC9hYdX8CbNbPl+LuEk4olKV8YAlncx5WOD0T/bikcrJm6QD5L26wYZv4vqr0WN4myJzQb2yiSq32umushlA5iEbvM=
signature verified:true
decryptedKey : z4KjtnlYqVqLl4uW39NfLg==
Decrypted Data : ABC


Share the post