

import java.math.BigInteger ;
import java.util.* ;
import java.io.* ;
import java.sql.*;
//MALLIKARJUN aug 14
//RSA MAIN CLASS

public class RSA1
{
//prime number size
int primeSize ;

//TWO LARGE INTEGERS
	BigInteger p, q ;
	
// Modulus 
	BigInteger N ;
	
//r=(p-1) * (q-1)
	BigInteger r ;
	
// (E,N) -encryption  (D,N) -decryption 
	BigInteger E, D ;
	
	//-------------------------------------------------------------------------------------------------------------------------
//CONSTRUCTER FOR RSA1

	public RSA1( int primeSize, int prob )
	{
	
		this.primeSize = primeSize ;
		long start=getCurrentTime();
	//generate p and q
	
		generatePrimeNumbers(prob);

	// Generate Public and Private Keys
	
		generatePublicPrivateKeys();
		
		 long end=getCurrentTime();
		 
            //System.out.println("Total elapsed time for key generation= "+(new Timestamp(end-start).getNanos()));
	    System.out.println("KEY GENERATION TIME    "+(end-start)+" in milliseconds ");
            
	}
//--------------------------------------------------------------------------------------------------------------------------

	//generate distinct p and q
	
	public void generatePrimeNumbers(int prob)
	{
        p = new BigInteger( primeSize, prob, new Random() ) ;
	do
	{
		q = new BigInteger( primeSize, prob, 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 keys E and D
	
	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 ) ;
		
	}


	//encrypting the plain TEXT
	public BigInteger[] encrypt( String message )
	{
		long start=getCurrentTime();
		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 ) ;
		long end=getCurrentTime();
		System.out.println("ENCRYRPTION TIME   "+(end-start)+" in millis");
		return( encrypted ) ;
	}

//decryption of cipher text 
	public String decrypt( BigInteger[] encrypted )
	{			
		long start=getCurrentTime();
		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() ) ;
		long end=getCurrentTime();
		System.out.println("DECRYPTION TIME   "+(end-start)+" in milliseconds");

		return( new String( charArray ) ) ;
	}


	// returns p
	public BigInteger getp()
	{
		return( p ) ;
	}


	//returns q
	public BigInteger getq()
	{
		return( q ) ;
	}


	//return r
	public BigInteger getr()
	{
		return( r ) ;
	}

	//return N
	public BigInteger getN()
	{
		return( N ) ;
	}

	//return E
	public BigInteger getE()
	{
		return( E ) ;
	}


	// returns D
	public BigInteger getD()
	{
		return( D ) ;
	}

//---------------------------------------------------------------------------------------------------------------------------
	
//----------------------------------------------------------------------------------------------------------------------------
	
	public static void main( String[] args ) throws IOException
	{
	 		if( args.length != 2 )
		{
			System.out.println( "Syntax: java RSA PrimeSize prob" ) ;
			System.out.println( "e.g. java RSA keysize probability" ) ;
			System.out.println( "e.g. java RSA 512  10 " ) ;
      			System.exit( -1 ) ;
		}
		int keySize = Integer.parseInt( args[0] ) ;
		System.out.println("KEYSIZE  "+keySize+" ") ;
		System.out.println("PROBABILITY OF BEING PRIME   "+Integer.parseInt(args[1])+" ") ;
		int primeSize=keySize/2;
		RSA1 rsa = new RSA1( primeSize,Integer.parseInt(args[1]) ) ;
		System.out.println( "E:[" + rsa.getE().toString( 16 ).toUpperCase() + "] " ) ;
		System.out.println( "D:[" + rsa.getD().toString( 16 ).toUpperCase() + "] " ) ;
		//String plaintext=rsa.messageGenerator(Integer.parseInt(args[1]));
		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 + "]" ) ;
		
		
	}
}

