//RSA.java
// Program to study perfromance of RSA as a function of Key Size//
// In this program same message i.e. Plain text is encrypted and decrypted by using various sizes for p & q
// The different key sizes generated are 128, 256, 512, 768, 1024

// submitted by Roll No: 03429801   Name : Ramrao Wagh
import java.math.BigInteger ;
import java.util.* ;
import java.io.* ;
import java.sql.*;


public class RSA
{
	
	int primeSize ;
       	
	BigInteger p, q ;

	BigInteger N ;
	
	BigInteger r ;
	
	BigInteger E, D ;
        
        long interval;
        
       
	
	public RSA( int primeSize )
	{
                long start=getCurrentTime();
		this.primeSize = primeSize ;

		// Generate two distinct large prime numbers p and q.
		generatePrimeNumbers() ;

		// Generate Public and Private Keys.
		generatePublicPrivateKeys() ;
                long end=getCurrentTime();
                interval = end-start;
           
            
	}


	/**
	 * 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()
	{
		// 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] ;


		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 ) ;


		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 ;


		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() ) ;


		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 ) ;
	}

        public long getInterval()
        {
            return (interval);
        }

	/**
	 * RSA Main program for Unit Testing.
	 */
	public static void main( String[] args ) throws IOException
	{
            int KeySize[];
            KeySize = new int[8];
            KeySize[0] = 128;
            KeySize[1] = 256;
            KeySize[2] = 512;
            KeySize[3] = 768;
            KeySize[4] = 1024;
            
            
            FileOutputStream g, f;
             
            if (args.length < 2) 
            { System.out.println( " Please enter the names of two output files on the command line");
            }
            // try {
                 //g= new FileOutputStream("rsatime.txt");
                 //f= new FileOutputStream("rsadata.txt");
                 g= new FileOutputStream(args[0]);
                 f= new FileOutputStream(args[1]);
                 PrintStream gout = new PrintStream ( g);
                 PrintStream fout = new PrintStream (f);
                 //System.out.println(args[0]);
                 //System.out.println(args[1]);
          //   } catch (IOException e) {
          //       System.err.println(" I/O error ");
         //    }
            // PrintStream gout = new PrintStream ( g);
            // PrintStream fout = new PrintStream (f);

		
              gout.println(" RSA Performance Evaluation using  JAVA " ); 
              gout.println(" with fixed message size but varying key size " ); 
              fout.println(" RSA Public & Private Keys, Message, CipherText and recovered Text");
  
                //args[0]="1024";
		// Get bit length of each prime number
		//int primeSize = Integer.parseInt( args[0] ) ;
                int primeSize;
                int KeyCount;
                long StartTimeEncrypt, StartTimeDecrypt;
                long EndTimeEncrypt, EndTimeDecrypt;
                long IntervalEncrypt, IntervalDecrypt, TotalTime;
                String plaintext = "Wishing you a Very Happy Independence Day on 15/8/2003";
                System.out.println( "Plaintext: [" + plaintext + "]" ) ;
                System.out.println( "Generating Public & Private keys" ) ; 
                //System.out.println( "KeySize       Key Generation  Time        Encryption Time       Decryption time");
                gout.println( "Plaintext: [" + plaintext + "]" ) ;
                 fout.println ("plaintext=["+plaintext+"]");
                gout.println( "KeySize       Key Generation  Time        Encryption Time       Decryption Time   Total Time in ms");
               
                for ( KeyCount = 0; KeyCount < 5; KeyCount++)
                {
                    TotalTime=0;
                    primeSize = KeySize[KeyCount]; 
                
		// Generate Public and Private Keys
                System.out.println( "Generating Public & Private keys for key Size "+ primeSize ) ;
                 gout.print( primeSize ) ;
		gout.print( "    " ) ;
		RSA rsa = new RSA( primeSize ) ;

		                
                
		


		// 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
                TotalTime= rsa.getInterval();
                gout.print("       " + rsa.getInterval());
                 gout.print( "  ");
                 //TotalTime= rsa.getInterval();
                 System.out.println(" Encrypting plaintext .......");
                StartTimeEncrypt =rsa.getCurrentTime();
		BigInteger[] ciphertext = rsa.encrypt( plaintext ) ;
                EndTimeEncrypt =rsa.getCurrentTime();
                IntervalEncrypt = EndTimeEncrypt - StartTimeEncrypt;
                System.out.println(" Encryption Completed");
                TotalTime = TotalTime+ IntervalEncrypt;
                gout.print("                 "+ IntervalEncrypt);
		//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( "" ) ;
                System.out.println(" Decrypting Ciphertext .......");
                StartTimeDecrypt =rsa.getCurrentTime();
		String recoveredPlaintext = rsa.decrypt( ciphertext ) ;
                EndTimeDecrypt =rsa.getCurrentTime();
                IntervalDecrypt = EndTimeDecrypt - StartTimeDecrypt;
                System.out.println(" Decryption completed");
                TotalTime = TotalTime + IntervalDecrypt;
           
                //System.out.println("                 "+ (new Timestamp(IntervalDecrypt).getNanos()));
                gout.print("           "+ IntervalDecrypt);
                
                gout.println("         "+ TotalTime);
               
		//System.out.println( "N: [" + rsa.getN().toString( 16 ).toUpperCase() + "]" ) ;
		
                fout.print ("Keysize = ");
                fout.println( primeSize ) ;
                fout.println( "Generated prime numbers p and q" ) ;
		fout.println( "p: [" + rsa.getp().toString( 16 ) + "]" ) ;
		fout.println( "q: [" + rsa.getq().toString( 16 ) + "]" ) ;
		fout.println( "" ) ;

		fout.println( "The public key is the pair (N, E) which will be published." ) ;
		fout.println( "N: [" + rsa.getN().toString( 16 ) + "]" ) ;
		fout.println( "E: [" + rsa.getE().toString( 16 ) + "]" ) ;
		fout.println( "" ) ;

		fout.println( "The private key is the pair (N, D) which will be kept private." ) ;
		fout.println( "N: [" + rsa.getN().toString( 16 ) + "]" ) ;
		fout.println( "D: [" + rsa.getD().toString( 16 ) + "]" ) ;
		fout.println( "" ) ;
                fout.println( "Recovered plaintext: [" + recoveredPlaintext + "]" ) ;
                
	}
               gout.close();
               fout.close();
                g.close(); 
               f.close();
               System.out.println( "Program Over- Please examine the output files" ) ;

}
        
}

