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.0	11 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 ;
	long elapsed;	
	int probability;
	/**
	 * Constructor.
	 *
	 * @param	primeSize		Bit length of each prime number.
	 */
	public RSA( int primeSize )
	{
		this.primeSize = primeSize ;

		// Generate two distinct large prime numbers p and q.
		generatePrimeNumbers() ;

		// Generate Public and Private Keys.
		generatePublicPrivateKeys() ;
	}

	public RSA()
	{
		probability=10;
	}
	public long getElapsedTime()
	{
		return elapsed;
	}
	public void setProbability(int p)
	{
		probability=p;
	}

	/**
	Set the prime size
	*/
	public void setPrimeSize(int primeSize)
	{
		this.primeSize=primeSize;
	}
	/**
	 * Generate two distinct large prime numbers p and q.
	 */
	public void generatePrimeNumbers()
	{
            //System.out.println
            //System.out.println("Starting time is ");
            long start=getCurrentTime();
		p = new BigInteger( primeSize, probability, new Random() ) ;


		do
		{
			q = new BigInteger( primeSize, probability, new Random() ) ;
		}
		while( q.compareTo( p ) == 0 ) ;
              //  System.out.println("Prime number search competed at");
            long end=getCurrentTime();
         //   System.out.println("Total elapsed time= "+(new Timestamp(end-start)).getNanos());
            
                
	}
        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()
	{
		// N = p * q
		long start=getCurrentTime();
		generatePrimeNumbers();
		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 ) ;
		  long end=getCurrentTime();
			elapsed=end-start;
        //    System.out.println("Total elapsed time for  Key GEnration= "+(end-start)+"ms"+(new Timestamp(end-start)).getNanos()+"ns");
	}


	/**
	 * Encrypts the plaintext (Using Public Key).
	 *
	 * @param	message			String containing the plaintext message to be encrypted.
	 * @return	The ciphertext as a BigInteger array.
	 */
	public BigInteger[] encrypt( String message )
	{
		int i ;
		byte[] temp = new byte[1] ;
		  long start=getCurrentTime();	

		byte[] digits = message.getBytes() ;

		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=getCurrentTime();
//     System.out.println("Total elapsed time for Encryption= "+((end-start))+"ms"+(new Timestamp(end-start)).getNanos()+"ns");
		    elapsed=end-start;
		return( encrypted ) ;
	
	}


	/**
	 * Decrypts the ciphertext (Using Private Key).
	 *
	 * @param	encrypted		BigInteger array containing the ciphertext to be decrypted.
	 * @return	The decrypted plaintext.
	 */
	public String decrypt( BigInteger[] encrypted )
	{
		int i ;
		  long start=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=getCurrentTime();
//   System.out.println("Total elapsed time for DEcryption= "+((end-start))+"ms"+(new Timestamp(end-start)).getNanos()+"ns");
		    elapsed=end-start;
		return( new String( charArray ) ) ;
	}


	/**
	 * Get prime number p.
	 *
	 * @return	Prime number p.
	 */
	public BigInteger getp()
	{
		return( p ) ;
	}


	/**
	 * Get prime number q.
	 *
	 * @return	Prime number q.
	 */
	public BigInteger getq()
	{
		return( q ) ;
	}


	/**
	 * Get r.
	 *
	 * @return	r.
	 */
	public BigInteger getr()
	{
		return( r ) ;
	}


	/**
	 * Get modulus N.
	 *
	 * @return	Modulus N.
	 */
	public BigInteger getN()
	{
		return( N ) ;
	}


	/**
	 * Get Public exponent E.
	 *
	 * @return	Public exponent E.
	 */
	public BigInteger getE()
	{
		return( E ) ;
	}


	/**
	 * Get Private exponent D.
	 *
	 * @return	Private 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 ) ;
		}else
    {
      args=new String[1];
      args[0]="1024";
    }
      args=new String[1];
      //args[0]="512";
      args[0]="1024";*/
		// Get bit length of each prime number
		int primeSize;// = Integer.parseInt( args[0] ) ;


		// Generate Public and Private Keys
		
	//	RSA rsa = new RSA( primeSize ) ;
		 RSA rsa = new RSA( );
		 primeSize=128;
         rsa.setPrimeSize(primeSize);
	
          System.out.println( "Key_Size  Probability                 Key_Generation_time" ) ;
        for(int p=1;p<=64;p*=2)
        {
                rsa.setProbability(p);
                 System.out.print( ""+primeSize+"          " ) ;
                System.out.print( ""+(1-1/Math.pow(2,(double)p))+"                      " ) ;
                rsa.generatePublicPrivateKeys();
                System.out.println(""+rsa.getElapsedTime());
        }
		System.out.println( "Key_Size  MSg_Size   Key_Generation   Encryption     Decryption" ) ;

		String plaintext="India is a Great Country.Indian History vouches for it's great heritage and knowledge";
	for(primeSize=128;primeSize<=1024;primeSize*=2)
	{
		
		System.out.print( ""+primeSize+"          " ) ;
		System.out.print( ""+plaintext.length()+"             " ) ;
		rsa.setPrimeSize(primeSize);
		rsa.generatePublicPrivateKeys();
		System.out.print(""+rsa.getElapsedTime()+"            ");
	//	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(""+rsa.getElapsedTime()+"             ");
	/*	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 + "]" ) ;
		 System.out.println(""+rsa.getElapsedTime());

		if(primeSize==512||primeSize==128)
		{
				int i,m=0;
			   StringBuffer ptext=new StringBuffer(4096);
				StringBuffer ts=new StringBuffer(128);
			ts.append("RAMA");
			while(ts.length()<256)//making a message of 128B length
			{
				ts.append(ts.toString());
			}
			for(int mSize=0;mSize<=4096;)
			{
				  System.out.print( ""+primeSize+"           " ) ;
		                System.out.print( ""+mSize+"               \"         " ) ;
				 BigInteger[] ctext =rsa.encrypt(ptext.toString());
				 System.out.print(""+rsa.getElapsedTime()+"              ");
				rsa.decrypt(ctext);
				 System.out.println(""+rsa.getElapsedTime());
					 ptext.append(ts);
                                        mSize=ptext.length();
			}//for msize
		}//if primeSize
			                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
			
	}//for primesize..

		
}//main
}//endof RSA

