import java.io.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.math.BigInteger;

public class PubliCrypt extends Applet implements ActionListener {

  TextField qText, pText, pqText, eText, dText, nText, expText;
  TextArea mText, cText, messages;
  Button goE, goD, goKey;

  public void init() {
    setLayout(null);
    messages = new TextArea("No errors");
    qText = new TextField("Enter first prime number");
    pText = new TextField("Enter second prime number");
    pqText = new TextField();
    eText = new TextField("Enter encryption key");
    dText = new TextField();
    cText = new TextArea();
    mText = new TextArea("Enter message here for en/decrypting");
    nText = new TextField("Enter n here");
    expText = new TextField("Enter encryption or decryption key here");

    Label pLabel = new Label("p:");
    Label qLabel = new Label("q:");
    Label pqLabel = new Label("n:");
    Label eLabel = new Label("e:");
    Label dLabel = new Label("d:");
    Label nLabel = new Label("n:");
    Label expLabel = new Label("Key:");
    Label mLabel = new Label("Message:");
    Label cLabel = new Label("Code:");

    goD = new Button("Decrypt");
    goKey = new Button("Generate key");
    goE = new Button("Encrypt");

    messages.setEditable(false);
    pqText.setEditable(false);
    dText.setEditable(false);

    pLabel.setBounds(0, 0, 25, 25);
    pText.setBounds(25, 0, 600, 25);
    qLabel.setBounds(0, 30, 25, 25);
    qText.setBounds(25, 30, 600, 25);
    pqLabel.setBounds(0, 60, 25, 25);
    pqText.setBounds(25, 60, 600, 25);
    eLabel.setBounds(0, 90, 25, 25);
    eText.setBounds(25, 90, 600, 25);
    dLabel.setBounds(0, 120, 25, 25);
    dText.setBounds(25, 120, 600, 25);
    goKey.setBounds(0, 150, 80, 25);
    messages.setBounds(200, 150, 350, 100);
    nLabel.setBounds(0, 255, 50, 25);
    nText.setBounds(60, 255, 600, 25);
    goE.setBounds(0, 225, 75, 25);
    goD.setBounds(80, 225, 75, 25);
    expLabel.setBounds(0, 285, 50, 25);
    expText.setBounds(60, 285, 600, 25);
    mLabel.setBounds(0, 315, 55, 25);
    mText.setBounds(60, 315, 600, 125);
    cLabel.setBounds(0, 445, 50, 25);
    cText.setBounds(60, 445, 600, 125);

    add(messages);
    add(pLabel);
    add(pText);
    add(qLabel);
    add(qText);
    add(pqLabel);
    add(pqText);
    add(eLabel);
    add(eText);
    add(dLabel);
    add(dText);
    add(goKey);
    add(nLabel);
    add(nText);
    add(expLabel);
    add(expText);
    add(mLabel);
    add(mText);
    add(cLabel);
    add(cText);
    add(goE);
    add(goD);
  }
  
  public void start() {
    goE.addActionListener(this);
    goD.addActionListener(this);
    goKey.addActionListener(this);
  }
  
  public void stop() {
    goD.addActionListener(null);
    goE.addActionListener(null);
    goKey.addActionListener(null);
  }
  
  public void actionPerformed(ActionEvent e) {
    messages.setText("No errors");
    if (e.getSource() == goKey) {
      dText.setText("");
      pqText.setText("");
      keyGen();
      } else if (e.getSource() == goE) {
	cText.setText("");
	encrypt();
      } else {
	mText.setText("");
	decrypt();
      }
  }

  public void keyGen() {
    try {
      BigInteger p = new BigInteger(pText.getText());
      BigInteger q = new BigInteger(qText.getText());
      BigInteger pq = p.multiply(q);
      pqText.setText(pq.toString());
      BigInteger o = new BigInteger("1");
      BigInteger phi = pq.subtract(p).subtract(q).add(o);
      BigInteger e = new BigInteger(eText.getText());
      dText.setText(e.modInverse(phi).toString());
    }
    catch(NumberFormatException e) {
      messages.setText("p, q and e must be integers");
    }
    catch(/*Arithmetic*/ Exception e) {
      messages.setText("e must be coprime to (p-1)(q-1)");
    }
  }

  public void encrypt() {
    BigInteger n = new BigInteger("0");
    BigInteger k = new BigInteger("0");
    try {
      n = new BigInteger(nText.getText());
      k = new BigInteger(expText.getText());
    }
    catch(NumberFormatException e) {
      messages.setText("n and key must be integers.");
      System.exit(1);
    }
    String m = new String();
    for (int x = 0; x < mText.getText().length(); x++) {
      m += String.valueOf( (int) mText.getText().charAt(x) + 100);
    }
    BigInteger mess = new BigInteger(m);
    if (mess.compareTo(n) == 1) {
      messages.setText("Message bigger than modulus (break into smaller messages or use larger modulus)");
      System.exit(1);
    }
    BigInteger c = mess.modPow(k, n);
    cText.setText(c.toString());
  }

  public void decrypt() {
    BigInteger n = new BigInteger("0");
    BigInteger k = new BigInteger("0");
    try {
      n = new BigInteger(nText.getText());
      k = new BigInteger(expText.getText());
    }
    catch(NumberFormatException e) {
      messages.setText("n and key must be integers.");
      System.exit(1);
    }
    BigInteger mess = new BigInteger(cText.getText());
    BigInteger c = mess.modPow(k, n);
    String code = c.toString();
    String m = new String();
    for (int x = 0; x < code.length(); x += 3) {
      char ch[] = new char[1];
      ch[0] = (char) (Integer.parseInt(code.substring(x, x+3)) - 100);
      m += new String(ch);
    }
    mText.setText(m);
  }
}
