import java.math.BigInteger ;
import java.util.* ;
import java.io.* ;
import java.sql.*;
/**
 * Class for RSA Algorithm (RSA.java).
 *
 * Generates Prime numbers and Public/Private Keys. Performs Encryption and 
Decryption. *
 * @author  Chue Wai Lian
 * @version
 *
 * 1.0.011 Apr 2001
 * <br>1st release.
 */
public class RSA
{
/**
 * Bit length of each prime number.
 */
int primeSize ;
            
/**
 * Two distinct large prime numbers p and q.
 */
BigInteger p, q ;
/**
 * Modulus N.
 */
BigInteger N ;
/**
 * r = ( p - 1 ) * ( q - 1 )
 */
BigInteger r ;
/**
 * Public exponent E and Private exponent D
 */
BigInteger E, D ;
/**
 * Constructor.
 *
 * @paramprimeSizeBit length of each prime number.
 */
public RSA( int primeSize ) throws IOException
{
	long start_gen=getCurrentTime();
	this.primeSize = primeSize ;
	// Generate two distinct large prime numbers p and q.
	generatePrimeNumbers() ;
	// Generate Public and Private Keys.
	generatePublicPrivateKeys() ;
		
	long end_gen=getCurrentTime();
	//System.out.println("Total elapsed time= "+(new Timestamp(end-start)).getNanos());
	File outputFile = new File("time_generate");
	FileWriter out = new FileWriter(outputFile, true);
	out.write((end_gen-start_gen)+"millisecs \n");
 	//out.write((new Timestamp(end_gen-start_gen)).getNanos()/1000000+"\n");
	out.close();	
}
/**
 * Generate two distinct large prime numbers p and q.
 */
public void generatePrimeNumbers()
{
	p = new BigInteger( primeSize, 10, new Random() ) ;
	do
	{
		q = new BigInteger( primeSize, 10, new Random() ) ;
	}
	while( q.compareTo( p ) == 0 ) ;
}
public long getCurrentTime()
{
	java.util.Date date=Calendar.getInstance().getTime();
	Timestamp a=new Timestamp(date.getTime()); 
	long currentTime=Calendar.getInstance().getTimeInMillis();
	//System.out.println("Time: "+currentTime);
	return currentTime;
        }
            
/**
 * Generate Public and Private Keys.
 */
public void generatePublicPrivateKeys() throws IOException
{
	// N = p * q
	N = p.multiply( q ) ;
	// r = ( p - 1 ) * ( q - 1 )
	r = p.subtract( BigInteger.valueOf( 1 ) ) ;
	r = r.multiply( q.subtract( BigInteger.valueOf( 1 ) ) ) ;
	// Choose E, coprime to and less than r
	do
	{
		E = new BigInteger( 2 * primeSize, new Random() ) ;
	}
	while( ( E.compareTo( r ) != -1 ) || ( E.gcd( r ).compareTo( BigInteger.valueOf( 1 ) ) != 0 ) ) ;// Compute D, the inverse of E mod r
	D = E.modInverse( r ) ;
}
/**
 * Encrypts the plaintext (Using Public Key).
 *
 * @parammessageString containing the plaintext message to be encrypted.
 * @returnThe ciphertext as a BigInteger array.
 */
public BigInteger[] encrypt( String message ) throws IOException
{
	int i ;
	byte[] temp = new byte[1] ;
	byte[] digits = message.getBytes() ;
	long start_encrypt=getCurrentTime();
	BigInteger[] bigdigits = new BigInteger[digits.length] ;
	for( i = 0 ; i < bigdigits.length ; i++ )
	{
		temp[0] = digits[i] ;
		bigdigits[i] = new BigInteger( temp ) ;
	}
	BigInteger[] encrypted = new BigInteger[bigdigits.length] ;
	for( i = 0 ; i < bigdigits.length ; i++ )
		encrypted[i] = bigdigits[i].modPow( E, N ) ;

	long end_encrypt=getCurrentTime();
	//System.out.println("Total elapsed time= "+(new Timestamp(end-start)).getNanos());
	File outputFile = new File("time_encrypt");
	FileWriter out = new FileWriter(outputFile, true);
	out.write((end_encrypt-start_encrypt)+"millisecs \n");
 	//out.write((new Timestamp(end_encrypt-start_encrypt)).getNanos()/1000000+"\n");
        out.close();
	return( encrypted ) ;
}
/**
 * Decrypts the ciphertext (Using Private Key).
 *
 * @paramencryptedBigInteger array containing the ciphertext to be decrypted.
 * @returnThe decrypted plaintext.
 */
public String decrypt( BigInteger[] encrypted ) throws IOException
{
	int i ;
	long start_decrypt=getCurrentTime();
	BigInteger[] decrypted = new BigInteger[encrypted.length] ;
	for( i = 0 ; i < decrypted.length ; i++ )
	decrypted[i] = encrypted[i].modPow( D, N ) ;
	char[] charArray = new char[decrypted.length] ;
	for( i = 0 ; i < charArray.length ; i++ )
	charArray[i] = (char) ( decrypted[i].intValue() ) ;
	
	long end_decrypt=getCurrentTime();
	//System.out.println("Total elapsed time= "+(new Timestamp(end-start)).getNanos());
	File outputFile = new File("time_decrypt");
	FileWriter out = new FileWriter(outputFile, true);
	out.write((end_decrypt-start_decrypt)+"millisecs \n");
 	//out.write((new Timestamp(end_decrypt-start_decrypt)).getNanos()/1000000+"\n");
        out.close();
	return( new String( charArray ) ) ;
}
/**
 * Get prime number p.
 *
 * @returnPrime number p.
 */
public BigInteger getp()
{
return( p ) ;
}
/**
 * Get prime number q.
 *
 * @returnPrime number q.
 */
public BigInteger getq()
{
return( q ) ;
}
/**
 * Get r.
 *
 * @returnr.
 */
public BigInteger getr()
{
return( r ) ;
}
/**
 * Get modulus N.
 *
 * @returnModulus N.
 */
public BigInteger getN()
{
return( N ) ;
}
/**
 * Get Public exponent E.
 *
 * @returnPublic exponent E.
 */
public BigInteger getE()
{
return( E ) ;
}
/**
 * Get Private exponent D.
 *
 * @returnPrivate exponent D.
 */
public BigInteger getD()
{
return( D ) ;
}
/**
 * RSA Main program for Unit Testing.
 */
public static void main( String[] args ) throws IOException
{
if( args.length < 1 )
{
System.out.println( "Syntax: java RSA PrimeSize" ) ;
System.out.println( "e.g. java RSA 8" ) ;
System.out.println( "e.g. java RSA 512" ) ;
System.exit( -1 ) ;
}
// Get bit length of each prime number
int primeSize = Integer.parseInt( args[0] ) ;
// Generate Public and Private Keys
RSA rsa = new RSA( primeSize ) ;
/*
System.out.println("Key Size: [" + primeSize + "]" ) ;
System.out.println("" ) ;
System.out.println("Generated prime numbers p and q" ) ;
System.out.println("p: [" + rsa.getp().toString( 16 ).toUpperCase() + "]" ) ;
System.out.println("q: [" + rsa.getq().toString( 16 ).toUpperCase() + "]" ) ;
System.out.println("" ) ;
System.out.println("The public key is the pair (N,E) which will be published."); 
System.out.println("N: [" + rsa.getN().toString( 16 ).toUpperCase() + "]" ); 
System.out.println("E: [" + rsa.getE().toString( 16 ).toUpperCase() + "]" ); 
System.out.println("" ) ;
System.out.println("The private key is the pair (N,D) which will be kept private." ) ;
System.out.println("N: ["+rsa.getN().toString( 16 ).toUpperCase() + "]" );
System.out.println("D: [" + rsa.getD().toString( 16 ).toUpperCase() + "]");
System.out.println("") ; */
// Get message (plaintext) from user
//System.out.println( "Please enter message (plaintext):" ) ;

String plaintext = ( new BufferedReader( new InputStreamReader(System.in ) )).readLine(); 
System.out.println("");// Encrypt Message

BigInteger[] ciphertext = rsa.encrypt( plaintext ); 
 

//System.out.print( "Ciphertext: [" ) ;
//for( int i = 0 ; i < ciphertext.length ; i++ )
//{
//	System.out.print( ciphertext[i].toString( 16 ).toUpperCase() ) ;
//	if( i != ciphertext.length - 1 )System.out.print( " " ) ;
//}
//System.out.println( "]" ) ;
//System.out.println( "" ) ;
String recoveredPlaintext = rsa.decrypt( ciphertext ) ;

 
System.out.println( "Recovered plaintext: [" + recoveredPlaintext + "]" ) ;
}
}
