/***************************************************************************************************

	File Name:		Cryptography.java.
 	Author:			Mindfire Solutions.
 	Create Date:	25th June 2007.
 	CopyRight: 		Copyright (c) 2007 Mindfire Solutions, Inc.

***************************************************************************************************/

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java .util.Enumeration;
import java.util.Date;
import java.security.*;
import java.security.SecureRandom;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.Key;
import java.security.spec.RSAPublicKeySpec;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.cert.X509Certificate;
import javax.security.auth.x500.X500Principal;
import java.security.cert.CertificateFactory;
import java.security.cert.Certificate;
import org.bouncycastle.x509.X509V1CertificateGenerator;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.util.encoders.Hex;


/***************************************************************************************************

 	Class Name:Cryptography

 	Description:
 		This class use  constructor to initialze KeyStoreName.It handles methods to create a new
 		KeyStore and add or delete Key into KeyStore.It handles methods for generating
 		AsymmetricCipherKeyPair(Public Key and Private Key)	using RSA algorithm and use those keys
 		for encrypting given data and deccrypting encrypted	data.

***************************************************************************************************/

public class Cryptography
{
	private AsymmetricBlockCipher cipher = null;
	private String keyStoreFileName = null;

	public Cryptography()
	{
		/*Empty*/
	}

    public Cryptography(String keyStoreName)throws Exception
	{
		 keyStoreFileName = keyStoreName;
	}

   	/******************************************************************************************

		Method Name:keyPairGeneration

		Arguments:
			1.	strength [Type: Integer]
				- The size, in bits, of the keys we want to produce.

		Return:
			A KeyPair containing PublicKey and Private Key.
			[Type: AsymmetricCipherKeyPair Object]

		Description:
			Gernerate KeyPair of specified size.

	*******************************************************************************************/

	public AsymmetricCipherKeyPair keyPairGeneration(int strength)throws Exception
	{
		SecureRandom securerandom = new SecureRandom();
		BigInteger pubExp = new BigInteger("10001", 16);

		RSAKeyGenerationParameters RSAKeyGenParams = new RSAKeyGenerationParameters
												(pubExp,securerandom, strength, 80);
		RSAKeyPairGenerator rsakeygen = new RSAKeyPairGenerator();
		rsakeygen.init(RSAKeyGenParams);
		AsymmetricCipherKeyPair keypair = rsakeygen.generateKeyPair();

		return keypair;
	}

   	/******************************************************************************************

		Method Name:encryption

	  	Arguments:
	  		1.	input	[Type: String]
				- String to be encrypted.
			2.	modulus	[Type: String]
				- Used to generate RSA Public Key.
			3.	exponent	[Type: String]
				- Used to generate RSA Public Key.

	  	Return:
	  		Encrypted String.
	  		[Type: String]

	  	Description:
			This method is used to encrypt bytes from a given string and convert the bytes into
			string.This method uses AsymmetricBlockCipher to perform the encryption.

	*******************************************************************************************/
	public String encryption(String input, String modulus, String exponent)throws Exception
	{
		String encString = null;

		BigInteger intModulus = new BigInteger(modulus);
		BigInteger intExponent = new BigInteger(exponent);
		RSAKeyParameters RSApubKey = new RSAKeyParameters(false, intModulus, intExponent);
		byte[] toEncrypt = input.getBytes();
		cipher = new PKCS1Encoding(new RSAEngine());
		cipher.init(true, RSApubKey);
		byte[] encByte = cipher.processBlock(toEncrypt, 0, toEncrypt.length);
		byte[] encValue = Hex.encode(encByte);
		encString = new String(encValue);

		return encString;
	}

   	/******************************************************************************************

		Method Name:decryption

		Arguments:
			1.	input [Type: String]
				-String to be decrypted.
			2.	modulus [Type: String]
				- Used to generate RSA Private Key.
			3.	privExponent [Type: String]
				- Used to generate RSA Private Key.
			4.	pubExponent [Type: String]
				- Used to generate RSA Private Key.
			5.	primeP [Type: String]
				- Used to generate RSA Private Key.
			6.	primeQ [Type: String]
				- Used to generate RSA Private Key.
			7.	primeExponentP [Type: String]
				- Used to generate RSA Private Key.
			8.	primeExponentQ [Type: String]
				- Used to generate RSA Private Key.
			9.	crtCoefficient [Type: String]
				- Used to generate RSA Private Key.

		Return:
			A Plain Text.
			[Type: String]

		Description:
			Method For Decrypting Encrypted String.

	*******************************************************************************************/

	public String decryption(String input,String modulus,String privExponent,String pubExponent,
							String primeP,String primeQ,String primeExponentP,String primeExponentQ,
							String crtCoefficient)throws Exception
	{
		BigInteger intModulus = new BigInteger(modulus);
		BigInteger intPrivExponent = new BigInteger(privExponent);
		BigInteger intPubExponent = new BigInteger(pubExponent);
		BigInteger intPrimeP = new BigInteger(primeP);
		BigInteger intPrimeQ = new BigInteger(primeQ);
		BigInteger intPrimeExponentP = new BigInteger(primeExponentP);
		BigInteger intPrimeExponentQ = new BigInteger(primeExponentQ);
		BigInteger intCrtCoefficient = new BigInteger(crtCoefficient);

		RSAPrivateCrtKeyParameters RSAprivKey =	new RSAPrivateCrtKeyParameters
						(intModulus, intPubExponent, intPrivExponent, intPrimeP,
						intPrimeQ, intPrimeExponentP, intPrimeExponentQ, intCrtCoefficient);
		byte[] toDecrypt = Hex.decode(input);
		cipher = new PKCS1Encoding(new RSAEngine());
		cipher.init(false, RSAprivKey);
		byte[] decByte = cipher.processBlock(toDecrypt, 0, toDecrypt.length);
		String decString = new String(decByte);
		return decString;
	}

   	/******************************************************************************************

		Method Name:keyStoreGenerator

		Arguments :
			1.	keyStorePassword	[Type: String]
				- Password to generate the keystore integrity check.

		Return:
			Nothing.
			[Type: Void]

		Description:
			This method generates an empty KeyStore with specified password using JCEKS.

	*******************************************************************************************/
	public void keyStoreGenerator(String keyStoreName, String keyStorePassword)throws Exception
	{
		  FileOutputStream stream = null;

		  try
		  {
			  stream= new FileOutputStream(keyStoreName);
			  KeyStore keystore = KeyStore.getInstance( "JCEKS", "SunJCE" );

			  //Create empty KeyStore
			  keystore.load(null,null);

			  //Save KeyStore
			  keystore.store(stream, keyStorePassword.toCharArray());
		  }
		  catch (Exception e)
		  {
			  e.printStackTrace();
		  }
		  finally
		  {
			  try
			  {
				  stream.close();
			  }	catch (Exception e){}
		  }
	}

   	/******************************************************************************************

		Method Name:importKey

		Arguments:
			1.	keyAlias  	[Type: String]
				- Alias for adding key in the KeyStore.
			2.	keyStorePassword	[type: String]
				- Password of the KeyStore to which key is to be imported.
			3.	modulus 	[Type: String]
				- Used to generate a RSAPublicKeySpec.
			4.	publicExponent 	[Type: String]
				- Used to generate a RSAPublicKeySpec.

		Return:
			Nothing.
			[Type: Void]

		Description :
			Add a new Public key in the Keystore with spcified key alias.Can not add key if the
			specified key alias already present in the KeyStore.

	*******************************************************************************************/

	public void importKey(String keyAlias, String keyStorePassword,
							String modulus, String publicExponent)throws Exception
	{

		// read keystore file
		FileInputStream keyStoreFile = new FileInputStream(keyStoreFileName);
		KeyStore keyStore = KeyStore.getInstance( "JCEKS", "SunJCE" );
		keyStore.load(keyStoreFile, keyStorePassword.toCharArray());

		//check whether specified alias present or not
		if(keyStore.isKeyEntry(keyAlias))
		{
				throw new KeyStoreException("Alias already exist in KeyStore.");
		}
		else
		{
			BigInteger RSAmod = new BigInteger(modulus);
			BigInteger RSApubExp = new BigInteger(publicExponent);

			// create RSAPublicKeySpec key spec
			RSAPublicKeySpec RSAPubSpec =
							new RSAPublicKeySpec(RSAmod, RSApubExp);

			KeyFactory keyfactory = KeyFactory.getInstance("RSA");
			RSAPublicKey publicKey = (RSAPublicKey)keyfactory.generatePublic(RSAPubSpec);

			// add the key to the keystore
			keyStore.setKeyEntry(keyAlias, publicKey, keyStorePassword.toCharArray(), null);

			//save keystore
			FileOutputStream keyStoreName = new FileOutputStream(keyStoreFileName);
			keyStore.store(keyStoreName, keyStorePassword.toCharArray());
		}
	}

	/******************************************************************************************

		Method Name:importKey

		Arguments:
			1.	keyAlias	[Type: String]
				- Alias for adding key in the KeyStore.
			2.	keyStorePassword	[type: String]
				- Password of the KeyStore to which key is to be imported.
			3.	modulus [Type: String]
				- Used to generate new RSAPrivateCrtKeySpec.
			4.	privExponent	[Type: String]
				- Used to generate new RSAPrivateCrtKeySpec.
			5.	pubExponent	[Type: String]
				- Used to generate RSA Private Key.
			6.	primeP [Type: String]
				- Used to generate new RSAPrivateCrtKeySpec.
			7.	primeQ	[Type: String]
				- Used to generate new RSAPrivateCrtKeySpec.
			8.	primeExponentP 	[Type: String]
				- Used to generate new RSAPrivateCrtKeySpec.
			9.	primeExponentQ	[Type: String]
				- Used to generate new RSAPrivateCrtKeySpec.
			10.	crtCoefficient [Type: String]
				- Used to generate new RSAPrivateCrtKeySpec.

		Return:
			Nothing.
			[Type: Void]

		Description :
			Add a new Private key in the Keystore with spcified key alias.Can not add key if the
			specified key alias already present in the KeyStore.

	*******************************************************************************************/
	public void importKey(String keyAlias, String keyStorePassword, String modulus,
							String privExponent, String pubExponent,String primeP,
							String primeQ, String primeExponentP,String primeExponentQ,
							String crtCoefficient)throws Exception
	{

		// read keystore file
		FileInputStream keyStoreFile = new FileInputStream(keyStoreFileName);
		KeyStore keyStore = KeyStore.getInstance( "JCEKS", "SunJCE" );
		keyStore.load(keyStoreFile, keyStorePassword.toCharArray());

		//check whether specified alias present or not
		if(keyStore.isKeyEntry(keyAlias))
		{
				throw new KeyStoreException("Alias already exist in KeyStore.");
		}
		else
		{
			BigInteger RSAmod = new BigInteger(modulus);
			BigInteger RSApubExp = new BigInteger(pubExponent);
			BigInteger RSAPrivExp = new BigInteger(privExponent);
			BigInteger RSAp = new BigInteger(primeP);
			BigInteger RSAq = new BigInteger(primeQ);
			BigInteger RSADp = new BigInteger(primeExponentP);
			BigInteger RSADq = new BigInteger(primeExponentQ);
			BigInteger RSAqInv = new BigInteger(crtCoefficient);

			// create RSAPublicKeySpec key spec using key file
			RSAPrivateCrtKeySpec RSAPrivSpec =
			new RSAPrivateCrtKeySpec(RSAmod, RSApubExp,RSAPrivExp,RSAp,RSAq,RSADp,RSADq,RSAqInv);

			KeyFactory keyfactory = KeyFactory.getInstance("RSA");
			RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey)keyfactory.generatePrivate(RSAPrivSpec);

			Provider newProvider = (java.security.Provider)Class.forName
							("org.bouncycastle.jce.provider.BouncyCastleProvider").newInstance();
			Security.addProvider(newProvider);

			BigInteger intmod = new BigInteger(modulus);
			BigInteger intpubExp = new BigInteger(pubExponent);

			// create RSAPublicKeySpec key spec using key file
			RSAPublicKeySpec RSAPubSpec =
							new RSAPublicKeySpec(intmod, intpubExp);

			keyfactory = KeyFactory.getInstance("RSA");
			RSAPublicKey publicKey = (RSAPublicKey)keyfactory.generatePublic(RSAPubSpec);

			// generate the certificate
			X509V1CertificateGenerator  certGen = new X509V1CertificateGenerator();

			certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
			certGen.setIssuerDN(new X500Principal("CN=Certificate"));
			certGen.setNotBefore(new Date(System.currentTimeMillis() - 50000));
			certGen.setNotAfter(new Date(System.currentTimeMillis() + 50000));
			certGen.setSubjectDN(new X500Principal("CN=Certificate"));
			certGen.setPublicKey(publicKey);
			certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");

			Certificate[] chain = new Certificate[1];
			chain[0] = certGen.generate(privateKey,"BC");


			// add the key to the keystore
			keyStore.setKeyEntry(keyAlias, privateKey, keyStorePassword.toCharArray(), chain);

			//save keystore
			FileOutputStream keyStoreName = new FileOutputStream(keyStoreFileName);
			keyStore.store(keyStoreName, keyStorePassword.toCharArray());
		}

	}

   /******************************************************************************************

		Method Name:retrieveKey

		Arguments:
			1.	keyAlias	[Type: String]
				- Alias for retrieving key from the KeyStore.
			2.	keyStorePassword	[Type: String]
				- Password of the KeyStore from which key is to be retrieved.

		Return:
			The key related to the specified alias name.
			[Type: Key]

		Description:
			This method is used to retrieve Key from KeyStore using alias for that key.

	*******************************************************************************************/
	public Key retrieveKey(String keyAlias,String keyStorePassword)throws Exception
	{
		String retKeyVal = null;
		Key key = null;

		// read keystore file
		KeyStore keyStore = KeyStore.getInstance( "JCEKS", "SunJCE" );
		FileInputStream keyStoreFile = new FileInputStream(keyStoreFileName);
		keyStore.load(keyStoreFile, keyStorePassword.toCharArray());


		//check whether specified alias present or not
		if(keyStore.isKeyEntry(keyAlias))
		{
			//get key from keystore
			key = keyStore.getKey(keyAlias, keyStorePassword.toCharArray());
		}
		else
		{
			throw new KeyStoreException ("Alias doesn't exist in KeyStore for retrieval.");
		}

		return key;
	}

   /******************************************************************************************

		Method Name:deleteKey

		Arguments:
			1.	keyAlias	[Type: String]
				- Alias for deleting key from the KeyStore.
			2.	keyStorePassword  [Type: String]
				- Password of the KeyStore from which key is to be deleted.

		Return:
			Nothing.
			[Type: Void]

		Description:
			Delete a specified key from KeyStore.

	*******************************************************************************************/
	public void deleteKey(String keyAlias,String keyStorePassword) throws Exception
	{

		// read KeyStore file
		KeyStore keyStore = KeyStore.getInstance( "JCEKS", "SunJCE" );
		FileInputStream keyStoreFile = new FileInputStream(keyStoreFileName);
		keyStore.load(keyStoreFile, keyStorePassword.toCharArray());

		//check whether specified alias present or not
		if(keyStore.isKeyEntry(keyAlias))
		{
			//Delete key from KeyStore
			keyStore.deleteEntry(keyAlias);

			//save KeyStore
			FileOutputStream keyStoreName = new FileOutputStream(keyStoreFileName);
			keyStore.store(keyStoreName, keyStorePassword.toCharArray());

		}
		else
		{
			throw new KeyStoreException("Alias doesn't exist in KeyStore for deletion.");
		}
	}

   /******************************************************************************************

		Method Name:listKeys

		Argument:
			1.	keyStorePassword	[Type: String]
				- Password of the KeyStore from which all the aliases will be retrieve.

		Return:
			An array containing all the keyaliases of the KeyStore.
			[Type: Array]

		Description:
			Generate an array containg  all the alias.

	*******************************************************************************************/
	public String[] listKeys(String keyStorePassword)throws Exception
	{
		String[] aliasArray = null;

		// read KeyStore file
		KeyStore keyStore = KeyStore.getInstance( "JCEKS", "SunJCE" );
		FileInputStream keyStoreFile = new FileInputStream(keyStoreFileName);
		keyStore.load(keyStoreFile, keyStorePassword.toCharArray());

		//Get total no. of alias in a KeyStore
		int totalAlias = keyStore.size();

		if(totalAlias > 0)
		{

			aliasArray = new String[totalAlias];

			Enumeration enum = keyStore.aliases();
			while(enum.hasMoreElements())
			{
				String alias = enum.nextElement().toString();
				aliasArray[totalAlias -1] = alias;
				totalAlias = totalAlias -1;
			}
		}
		else
		{
			throw new KeyStoreException ("KeyStore is empty. No Alias present.");
		}

		return aliasArray;
	}
}

/***************************************************************************************************

	End of File.

***************************************************************************************************/