

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.
	 */
        String plaintext;
	int primeSize ;
        int certainity;
            
	/**
	 * 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.
	 *
	 * @param	primeSize		Bit length of each prime number.
	 */
	public RSA( int primeSize,String plaintext,int certainity )
	{
		this.primeSize = primeSize ;
                this.plaintext = plaintext ;
                this.certainity= certainity;
		// Generate two distinct large prime numbers p and q.
		generatePrimeNumbers() ;

		// Generate Public and Private Keys.
		generatePublicPrivateKeys() ;
	}


	/**
	 * Generate two distinct large prime numbers p and q.
	 */
	public void generatePrimeNumbers()
	{
            System.out.println("Generating the Prime Numbers p and q");
            System.out.println("Starting time is ");
            long start=getCurrentTime();
		p = new BigInteger( primeSize, certainity, new Random() ) ;


		do
		{
			q = new BigInteger( primeSize, certainity, new Random() ) ;
		}
		while( q.compareTo( p ) == 0 ) ;
                System.out.println("Prime number search competed at");
            long end=getCurrentTime();
            System.out.println("Total elapsed time (msec) = "+(end-start));
            
                
	}
        public long getCurrentTime()
        {                
          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] ;
                
                System.out.println("Encryption Starting time is ");
                long start=getCurrentTime();
		
		for( i = 0 ; i < bigdigits.length ; i++ )
			encrypted[i] = bigdigits[i].modPow( E, N ) ;
            System.out.println("Encryption competed at");
            long end=getCurrentTime();
            System.out.println("Total elapsed time (msec) = "+(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 ;


		BigInteger[] decrypted = new BigInteger[encrypted.length] ;

		System.out.println("Decryption Starting time is ");
                long start=getCurrentTime();		
		
                for( i = 0 ; i < decrypted.length ; i++ )
			decrypted[i] = encrypted[i].modPow( D, N ) ;

		System.out.println("Decryption competed at");
                long end=getCurrentTime();
                System.out.println("Total elapsed time (msec) = "+(end-start));
            
                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 void do_RSA()
       {
	/**
	 * RSA Main program for Unit Testing.
	 */
               System.out.println( "Key Size: [" + primeSize + "]" ) ;
               System.out.println( "Message Size: [" + plaintext.length() + "]" ) ;
		System.out.println( "" ) ;
          // Generate Public and Private Keys
		System.out.println( "Generated prime numbers p and q" ) ;
		System.out.println( "p: [" + getp().toString( 16 ).toUpperCase() + "]" ) ;
		System.out.println( "q: [" + 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: [" + getN().toString( 16 ).toUpperCase() + "]" ) ;
		System.out.println( "E: [" + 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: [" + getN().toString( 16 ).toUpperCase() + "]" ) ;
		System.out.println( "D: [" + getD().toString( 16 ).toUpperCase() + "]" ) ;
		System.out.println( "" ) ;
           // Encrypt Message
                
                BigInteger[] ciphertext = encrypt( plaintext ) ;                
                String recoveredPlaintext = decrypt( ciphertext ) ;              
                
                if(ciphertext.length < 100 )
                {
		   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( "Recovered plaintext: [" + recoveredPlaintext + "]" ) ;
                }
                else
                {
                   String encmsg="";                    
                    for(int i=0 ; i < ciphertext.length; i++)
                    {
                        encmsg+=ciphertext[i].toString( 16 ).toUpperCase();
                    }                     
                    writeToFile("./encrypt_msg",encmsg);
                   writeToFile("./decrypt_msg",""+recoveredPlaintext);
                    System.out.println("Since Large MSG To display :Encryted text written out to encypt_msg and output to decrypt_msg");
                }
		
       }
           
	public static void main( String[] args ) throws IOException
	{
                int primeSize=128;
                int certainity=10;
                String ptext;
                String plaintext="PLAIN MESSAGE";
		while(true)
                {
                    System.out.println("\t MENU");
                    System.out.println("1.Enter the Message");
                    System.out.println("2.Read Message From a (./input) File");
                    System.out.println("3.Enter the primesize");
                    System.out.println("4.Enter the certainity factor");
                    System.out.println("5.Do RSA");
                    System.out.println("7.Exit");
                    
                    System.out.print("Enter ur choice:");
                    String chstr = ( new BufferedReader( new InputStreamReader( System.in ) ) ).readLine() ;
                    int ch = Integer.parseInt(chstr);
                    switch(ch)
                    {
                        case 1:
                                 // Get message (plaintext) from user
                            System.out.println( "Please enter message (plaintext):" ) ;
                            plaintext = ( new BufferedReader( new InputStreamReader( System.in ) ) ).readLine() ;
                            System.out.println( "" ) ;
                             break;
                        case 2: plaintext=readFromFile("./input");                                 
                               break;
                        case 3:
                            System.out.println("Please Enter the Primesize : ");
                            ptext = ( new BufferedReader( new InputStreamReader( System.in ) ) ).readLine() ;
                            primeSize = Integer.parseInt( ptext ) ;
                            break;
                        case 4:
                            System.out.println("Please Enter the Certainity Factor: ");
                            ptext = ( new BufferedReader( new InputStreamReader( System.in ) ) ).readLine() ;
                            certainity = Integer.parseInt( ptext ) ;
                            break;
                        case 5:
                            RSA rsa=new RSA(primeSize,plaintext,certainity);
                            rsa.do_RSA();
                            break;
                        case 7: System.exit(0);
                            
                    }
                }
        }
     static String readFromFile(String filename)
     {
         String plaintext="";
        try {
            File inputFile = new File(filename);
            //File outputFile = new File("outagain.txt");

            FileInputStream fis = new FileInputStream(inputFile);
           // FileOutputStream fos = new FileOutputStream(outputFile);
            int c;

            while ((c = fis.read()) != -1) {
               //fos.write(c);
                plaintext=plaintext+(char)c;
            }

            fis.close();
           // fos.close();
        } catch (FileNotFoundException e) {
            System.err.println("FileStreamsTest: " + e);
        } catch (IOException e) {
            System.err.println("FileStreamsTest: " + e);
        }
         return plaintext;
    }
     static void writeToFile(String filename,String msg)
     {
          FileOutputStream out; // declare a file output object
          PrintStream p; // declare a print stream object
          try
           {
                        // Create a new file output stream
                        // connected to "myfile.txt"
                 out = new FileOutputStream(filename);

                        // Connect print stream to the output stream
                  p = new PrintStream( out );
		  p.println (msg);

                        p.close();
                }
                catch (Exception e)
                {
                        System.err.println ("Error writing to file");
                }
      }
    
}

