/*
 * GNU.FREE 2002
 *
 * Copyright (c) 1999, 2000, 2001, 2002
 * The Free Software Foundation (www.fsf.org)
 *
 * GNU.FREE Co-ordinator: Jason Kitcat <jeep@free-project.org>
 *
 * GNU site: http://www.gnu.org/software/free/
 * 
 * FREE e-democracy site: http://www.free-project.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program (COPYING); if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
package FreeTest;

import cryptix.util.core.Hex;
import cryptix.provider.rsa.RawRSAPublicKey;
import cryptix.provider.rsa.RawRSAPrivateKey;
import cryptix.provider.key.RawSecretKey;
import cryptix.provider.padding.OneAndZeroes;
import cryptix.provider.cipher.Blowfish;

import xjava.security.KeyGenerator;
import xjava.security.Cipher;
import xjava.security.FeedbackCipher;
import xjava.security.SecretKey;

import java.security.*;

import java.io.ByteArrayInputStream;
import java.util.StringTokenizer;

/**
 * Encrypts/Decrypts data using RSA. (Based on example code included with Cryptix package.)
 * Also includes methods relating to session keys (which then get encrypted with RSA)
 * and the subsequent encryption of data with the session key.
 *
 * @version 0.4 12 June 2001
 * @author  Jason Kitcat, Edwin Woudt
 * @since 1.7
 */
public class RSAEncrypt {

private static SecureRandom rnd;

/**
 * Initialise the SecureRandom for encryption. MUST BE DONE prior to using other methods.
 */
public static void init() throws Exception {
	 try {
		java.security.Security.addProvider(new cryptix.provider.Cryptix());
		rnd = new SecureRandom();
    	rnd.nextInt(); // ask for some bits of random data which will force the init.
	} catch(Exception e) {
		throw new Exception("RSA Encrypt initialistion error: " + e.toString());
	}
}
    
/**
 * Encrypts the data parameter using the public key and returns as a Hex-encoded string.
 *
 * @param  key  Hex-encoded public RSA key
 * @param  data  Data to be encrypted
 * @returns  Hex-encoded encrypted data.
 */
public static String pubEncrypt(String key, String data) throws Exception {

		String result = "";

        try {  // Cryptix likes to throw exceptions. We'll catch them all.

            // Read the public key
            PublicKey pubkey = new RawRSAPublicKey(new ByteArrayInputStream(Hex.fromString(key)));

			// prepare padding etc.
            RSAPKCS1 rsa = new RSAPKCS1(rnd);
            rsa.initEncrypt(pubkey);

            // encrypt
            result = Hex.toString(rsa.crypt(StringByteTools.asciiGetBytes(data)));

        } catch (Exception e) {
        	throw new Exception("RSAEncrypt.pubEncrypt: " + e.getMessage());
        }
        
        return result;
        
} //EOF pubEncrypt


/**
 * Decrypts supplied hexadecimal-encoded data using private RSA key.
 *
 * @param key  Private key
 * @param data  Hex-encoded data
 * @returns  Decoded string data
 */
public static String privDecrypt(String key, String data) throws Exception {

	String result = "";
	
	try {  // Cryptix likes to throw exceptions. We'll catch them all.
        
		// Read the private key
		PrivateKey seckey = new RawRSAPrivateKey(new ByteArrayInputStream(Hex.fromString(key)));
            
		// Step 1: init
		RSAPKCS1 rsa = new RSAPKCS1();
		rsa.initDecrypt(seckey);
		result = StringByteTools.asciiGetString(rsa.crypt(Hex.fromString(data)));
		
	} catch (Exception e) {
        	throw new Exception("RSAEncrypt.privDecrypt: " + e.getMessage());
	}

	return result;

} //EOF privDecrypt


/**
 * Using SecureRandom a session key is created.
 *
 * @returns  Hex-encoded session key
 */ 
public static String makeSessionKey() throws Exception {

	String result = "";

	try {
			byte[] pseudoRandom = new byte[100];
			
			rnd.nextBytes(pseudoRandom);

		 	MessageDigest md = MessageDigest.getInstance("SHA-1");
	 	
		 	md.update(pseudoRandom);
	 		byte mac[] = md.digest();
	 
			/* convert to hex */
		 	result = Hex.toString(mac);
		 	
	} catch (Exception e) {
        	throw new Exception("RSAEncrypt.makeSessionKey: " + e.getMessage());
	}

	return result;

}  //EOF makeSessionKey


/**
 * blowEncrypt uses Blowfish to encrypt data using the session key
 *
 * @param key  Hex-encoded key
 * @param data  Data to encrypt
 * @returns  Hex-encoded encrypted data
 */
 public static String blowEncrypt(String key, String data) throws Exception {

		RawSecretKey Bkey;
	 	byte[] ect;
		Cipher blowalg = Cipher.getInstance(new Blowfish(),null,new OneAndZeroes());
	 	
		/* build a Blowfish (16-round) key using ECB */
		Bkey = new RawSecretKey("Blowfish", Hex.fromString(key));
        
		blowalg.initEncrypt(Bkey);
		ect = blowalg.crypt(StringByteTools.asciiGetBytes(data));

		return Hex.toString(ect);
 
 } //EOF blowEncrypt


/**
 * blowDecrypt uses Blowfish to decrypt data using the session key
 *
 * @param key  Hex-encoded key
 * @param data  Hex-encoded Data to decrypt
 * @returns  Decrypted data
 */
 public static String blowDecrypt(String key, String data) throws Exception {

		RawSecretKey Bkey;
	 	byte[] dect;
		Cipher blowalg = Cipher.getInstance(new Blowfish(),null,new OneAndZeroes());
	 	
		/* build a Blowfish (16-round) key using ECB */
		Bkey = new RawSecretKey("Blowfish", Hex.fromString(key));
        
		blowalg.initDecrypt(Bkey);
		dect = blowalg.crypt(Hex.fromString(data));

		return StringByteTools.asciiGetString(dect);
 
 } //EOF blowDecrypt


/**
 * splitnDecrypt is a convenience method to split the session key from the data, retrieve the
 * session key and use it to return the data.
 *
 * @param key  Hex-encoded private key
 * @param data  Hex-encoded data
 * @returns  An array with Decrypted data as a String in 1 and session_key in 0
 */
 public static String[] splitnDecrypt(String key, String data) throws Exception {
 
		int i = 0;
		String session_key = "";
		String result[] = new String[] {"", ""};
		String[] splitdata = new String[] {"", ""};
	
		// split into session key and real data
		StringTokenizer splitter = new StringTokenizer(data, "-", false);

		while (splitter.hasMoreTokens() && i < splitdata.length) {
			splitdata[i] = splitter.nextToken();
			i++;
		}

		// decrypt session key
		session_key = privDecrypt(key,splitdata[0]);
		result[0] = session_key;
		
		// decrypt real data
		result[1] = blowDecrypt(session_key,splitdata[1]);
		
		return result;
		
 } //EOF splitnDecrypt

} //EOF Class
