

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.
 *
 *
 */
class EncryptRSA
{
                                                                    
        /**
         * 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 ;

	/* Declare variables to take different timevalues for calculating 
	 * the statistics
	 */

	 BigInteger tmDiffPrimeGen;
	 BigInteger tmDiffPubPrv;
	 BigInteger tmDiffCalc_Encr;
	 BigInteger tmDiffCalc_Decr;


	


        /**
         * Constructor.
         *
         * @param       primeSize               Bit length of each prime
         * number

         */
        public EncryptRSA( int primeSize )
        {
		long start, end;
                this.primeSize = primeSize ;

                // Generate two distinct large prime numbers p and q.
		// and note time of their generation
	        start=getCurrentTime();
                generatePrimeNumbers() ;
	        end=getCurrentTime();
		tmDiffPrimeGen = BigInteger.valueOf((new Timestamp(end-start)).getNanos());

                // Generate Public and Private Keys.
		// and note the time of their generation
	        start=getCurrentTime();
                generatePublicPrivateKeys() ;
	        end=getCurrentTime();
		tmDiffPubPrv = BigInteger.valueOf((new Timestamp(end-start)).getNanos());
        }
                                                                     



        /**
         * 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();

                return currentTime;
        }



        /**
         * Generate Public and Private Keys.
         */
        public void generatePublicPrivateKeys()
        {
                                                                    
                // 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).
         *
         * @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, end;



		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 ) ;

		end = getCurrentTime();
		tmDiffCalc_Encr = BigInteger.valueOf((new Timestamp(end-start)).getNanos());
		

                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, end;


		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() ) ;

		end = getCurrentTime();
		tmDiffCalc_Decr = BigInteger.valueOf((new Timestamp(end-start)).getNanos());
			
                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 ) ;
        }


	/**
	 * Get the total time taken in finding the PrimeNos P, Q
	 * @return	Time To Find Prime Nos.
	 */
	public BigInteger getTimePrimeGen()
	 {
		return ( tmDiffPrimeGen );
	 }


	/**
	 * Get the total time taken in finding the Public and Private Keys
	 * @return	Time To Find Public-Private Keys
	 */
	public BigInteger getTimePubPrv()
	 {
		return ( tmDiffPubPrv );
	 }


	/**
	 * Get the total time taken to encrypt the message
	 * @return	Time To Encrypt the message
	 */
	public BigInteger getTimeEncr()
	 {
		return ( tmDiffCalc_Encr );
	 }


	/**
	 * Get the total time taken to decrypt the message
	 * @return	Time To Decrypt the message
	 */
	public BigInteger getTimeDecr()
	 {
		return ( tmDiffCalc_Decr );
	 }
	




        /**
         * RSA Main program for Unit Testing.
         */
        public static void main( String[] args ) throws IOException
        {
		FileReader messageFile = new FileReader("Message.txt");
                if( args.length != 2 )
                {
                        System.out.println( "Syntax: java EncryptRSA BitSize Probability" ) ;
                        System.out.println( "e.g. java RSA 8 10" ) ;
                        System.out.println( "e.g. java RSA 512 12" ) ;

                        System.exit( -1 ) ;
                }
                // Get bit length of each prime number
                int bitSize = Integer.parseInt( args[0] ) ;
		int probValue = Integer.parseInt( args[1]);
		int lenMessage;

                // Generate Public and Private Keys
                EncryptRSA encrRSA = new EncryptRSA( bitSize ) ;


                String plaintext = ( new BufferedReader( messageFile )).readLine() ;
		lenMessage = plaintext.length();
                                                                    


                // Encrypt Message 
                BigInteger[] ciphertext = encrRSA.encrypt( plaintext ) ;
		


                String recoveredPlaintext = encrRSA.decrypt( ciphertext ) ;



		// Now read the various time values and output these to the
		// standard output which is then redirected to a file for
		// storage.This has been done to keep the code simple by
		// excluding the file handling code from the main program,

		System.out.println("KeySize\t\t: "+bitSize+"\t\tMessageLen\t: "+lenMessage+"\tProb. Constt.\t: "+probValue);
		System.out.println("PrimeKeyGenTime\t: "+encrRSA.getTimePrimeGen().toString()+"\tPubPrvKeyGenTime: "+encrRSA.getTimePubPrv().toString());
		System.out.println("TimeToEncrypt\t: "+encrRSA.getTimeEncr().toString()+"\tTimeToDecrypt\t: "+encrRSA.getTimeDecr().toString());

        }
}




