/**
 * rsa.java
 * lance rsacle.java, rsacode.java et rsadecode.java
 * Jean-Paul Quelen 08/01/18 modifié le 28/08/22
 */

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.math.BigInteger;
import java.util.StringTokenizer;

public class rsa {
	static final long serialVersionUID = 220828L;

	public static void main (String [] args) {
		int w = 500;
		int h = 300;

// Création et lancement des applications
		rsacle rcle = new rsacle();
		rsacode rcode = new rsacode();
		rsadecode rdecode = new rsadecode();

// Création des fenêtres contenant les applications
		JFrame fcle = new JFrame ("RSAclé");
		fcle.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		fcle.add (rcle);
		fcle.setSize (w, h);
		fcle.setVisible (true);

		JFrame fcode = new JFrame ("RSAcode");
		fcode.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		fcode.add (rcode);
		fcode.setSize (w, h);
		fcode.setLocation (20, 20);
		fcode.setVisible (true);

		JFrame fdecode = new JFrame ("RSAdécode");
		fdecode.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		fdecode.add (rdecode);
		fdecode.setSize (w, h);
		fdecode.setLocation (40, 40);
		fdecode.setVisible (true);
	}

protected static class rsavar {
	static BigInteger n, c, d;
	static int ng;
}

/**
 * rsacle.java - 03/10/01
 * avec des "BigInteger" - 06/05/02 - 16/01/11
 * Calcule à partir des deux nombres premiers p et q, la clé publique n, l'exposant c et
 * l'exposant "privé" d. Attention pas de test p, q entiers premiers différents.
 * modifié le 08/01/18 et le 28/08/22
 */

protected static class rsacle extends JPanel implements ActionListener {
	static final long serialVersionUID = 220828L;
	JTextField tp, tq;
	TextArea ta;
	BigInteger p, q, n, pm1qm1, c, d, deux, trois;
	JButton suivant, ok;
//	rsavar v;
	boolean recalc, b;

	public rsacle() {
		setFont (new Font ("Arial", Font.PLAIN, 12));
		setLayout (new BorderLayout ());
		JPanel pnl = new JPanel ();
		pnl.setBackground (Color.lightGray);
		add (pnl, BorderLayout.NORTH);
		pnl.add (new JLabel ("p ="));
		pnl.add (tp = new JTextField (5));
		pnl.add (new JLabel ("q ="));
		pnl.add (tq = new JTextField (5));
		pnl.add (ok = new JButton ("ok"));
		ok.addActionListener (this);
		pnl.add (suivant = new JButton (">>"));
		suivant.addActionListener (this);
		add (ta = new TextArea (), BorderLayout.CENTER);
		deux = BigInteger.valueOf (2);
		trois = BigInteger.valueOf (3);
//		v = new rsavar ();
		p = q = n = pm1qm1 = c = d = rsavar.n = rsavar.c = rsavar.d = BigInteger.ZERO;
	}

/*
 * retourne "true" si le nombre n est premier "false" sinon.
 */

	private boolean premier (BigInteger n) {
		if (n.isProbablePrime (4)) {
			if (n.compareTo (deux) == 0)
				return true;
			else {
				boolean pr = true;
				BigInteger p = trois;
				while (pr && (p.multiply(p).compareTo(n) <= 0)) {
					pr = (n.remainder(p).compareTo(BigInteger.ZERO) != 0);
					p = p.add (deux);
				}
			return pr;
			}
		} else
			return false;
	}

	public void actionPerformed (ActionEvent evt) {
		if (evt.getSource() == ok) {
			try {
				p = new BigInteger (tp.getText());
			}
			catch (NumberFormatException nfe) {}
			try {
				q = new BigInteger (tq.getText());
			}
			catch (NumberFormatException nfe) {}
			c = BigInteger.ONE;
			rsavar.n = n = p.multiply (q);
			pm1qm1 = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
		}
		tp.setText (p.toString());
		tq.setText (q.toString());
		String s = "\nn = " + n.toString () + "\n\n";
/*
 * recherche c tel que c et (p - 1)(q - 1) soient premiers entre eux.
 */
		BigInteger j = c.add (deux);
		boolean b = true;
		while ((b = (j.gcd(pm1qm1).compareTo (BigInteger.ONE) != 0)) && (j.compareTo (pm1qm1) < 0))
			j = j.add (deux);
		if (! b) {
			c = j;
/*
 * détermination de l'entier d correspondant
 */
			BigInteger i = BigInteger.ONE;
			do
				i = i.add (pm1qm1);
			while (i.remainder(c).compareTo(BigInteger.ZERO) != 0);
			rsavar.d = d = i.divide (c);
		}
		if (b)
			s += "plus de c";
		else {
			s += "c = " + c.toString () + "\n\nd = " + d.toString ();
			rsavar.c = c;
		}
		ta.setText (s);
	}
}
/**
 * rsacode.java - 03/10/01 - 01/12/01 - 25/01/03
 * avec des BigInteger 05/05/02 - 16/01/11
 * Code un message à partir de la clé publique n, de l'exposant c.
 * modifié le 08/01/18 et le 28/08/22
 */

protected static class rsacode extends JPanel implements ActionListener {
	static final long serialVersionUID = 220828L;
	JTextField tn, te, tng;
	TextArea ta, tb, tc;
	JButton maj, code, eff;
	int ng;
	BigInteger n, c;
//	rsavar v;
	String alphanum;

	public rsacode() {
		setFont (new Font ("Arial", Font.PLAIN, 10));
		setBackground (Color.lightGray);
//		v = new rsavar ();
		n = c = rsavar.n = rsavar.c = BigInteger.ZERO;
		add (new JLabel ("n ="));
		add (tn = new JTextField (n.toString (), 5));
		add (new JLabel ("c ="));
		add (te = new JTextField (c.toString (), 5));
		add (new JLabel ("nb ="));
		add (tng = new JTextField ("1", 1));
		ng = 1;
		add (maj = new JButton ("maj"));
		maj.addActionListener (this);
		add (code = new JButton ("code"));
		code.addActionListener (this);
		add (eff = new JButton ("x"));
		eff.addActionListener (this);
		add (ta = new TextArea (2, 40));
		add (tb = new TextArea (2, 40));
		add (tc = new TextArea (2, 40));
		alphanum = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789";
	}

	public void actionPerformed (ActionEvent evt) {
		if (evt.getSource() == maj) {
			tn.setText (rsavar.n.toString());
			n = rsavar.n;
			te.setText (rsavar.c.toString());
			ng = 1;
			BigInteger bi = BigInteger.TEN;
			ng = 0;
			while (bi.compareTo (n) < 0) {
				bi = bi.multiply (BigInteger.TEN);
				ng ++;
			}
			rsavar.ng = ng;
			tng.setText (Integer.toString (ng));
		} else if (evt.getSource() == eff) {
			ta.setText ("");
			tb.setText ("");
			tc.setText ("");
		} else if (evt.getSource() == code) {
			try {
				n = new BigInteger (tn.getText());
			}
			catch (NumberFormatException nfe) {}
			try {
				c = new BigInteger (te.getText());
			}
			catch (NumberFormatException nfe) {}
			int ng1 = ng;
			try {
				ng1 = Integer.parseInt (tng.getText());
			}
			catch (NumberFormatException nfe) {}
			if (ng1 > 0)
				ng = ng1;
			rsavar.ng = ng;
			tng.setText (Integer.toString (ng));
			String s = ta.getText().toUpperCase();

/*
 * tous les caractères sont mis en majuscules
 * seules les lettres ou chiffres sont prises en compte et sont
 * remplacées par leur place dans l'ordre alphabétique (1 à 37)
 */
			if (s.length() != 0) {
				String s1 = "";				// uniquement la chaine des rangs des lettres et chiffres pour codage
				String s2 = "";				// la chaîne des rangs avec des espaces pour affichage
				String s3 = "";				// la chaîne de caractères pour affichage
				for (int i = 0; i < s.length(); i++) {
					char sci = s.charAt (i);
					int j = alphanum.indexOf (sci) + 1;
					if (j > 0) {
						if (j < 10) {
							s1 += "0";
							s2 += "0";
						}
						s1 += Integer.toString (j);
						s2 += Integer.toString (j) + " ";
						s3 += sci;
					}
				}
				ta.setText (s3);
				tb.setText (s2);

/*
 * groupement de ng chiffres pour obtenir des nombres de 1 à 10^(ng + 1)
 */
				int index = 0;
				int i1 = s1.length () / ng;
				BigInteger[] nm = new BigInteger [i1 + 1];
				int k = 0;
				for (int j = 0; j < i1; j++) {
					int l = k + ng;
					nm [index ++] = new BigInteger (s1.substring (k, l));
					k = l;
				}
				if (s1.length () % ng != 0)
					nm [index ++] = new BigInteger (s1.substring (k));

/*
 * a ^ c % n
 */
				for (int i = 0; i < index; i ++)
					nm [i] = nm[i].modPow (c, n);

/*
 * affichage du résultat
 */
				s = "";
/*				int rc = 0;
				int isnl = n.toString().length();
				for (int i = 0; i < index; i++) {
					BigInteger nmi = nm [i];
					int jmx = isnl - nmi.toString().length();
					for (int j = 0; j < jmx; j++)
						s += "0";
					s += nmi.toString () + " ";
				}*/
				for (int i = 0; i < index; i ++)
					s += nm [i].toString () + " ";
				tc.setText (s + "\n");
			}
		}
	}
}

/**
 * rsadecode.java - 03/10/01 - 01/12/01
 * avec des biginteger 05/05/02 - 16/01/11
 * Décode un message à partir de la clé publique n, de l'exposant d.
 * modifié le 08/01/18 et le 28/08/22
 */

protected static class rsadecode extends JPanel implements ActionListener {
	static final long serialVersionUID = 220828L;
	JTextField tn, td, tng;
	TextArea ta, tb, tc;
	JButton maj, code, eff;
	int ng;
	BigInteger n, d;
//	rsavar v;
	String alphanum;

	public rsadecode() {
		setFont (new Font ("Arial", Font.PLAIN, 10));
		setBackground (Color.lightGray);
//		v = new rsavar ();
		n = d = rsavar.n = rsavar.d = BigInteger.ZERO;
		add (new JLabel ("n ="));
		add (tn = new JTextField (n.toString (), 5));
		add (new JLabel ("d ="));
		add (td = new JTextField (d.toString (), 5));
		add (new JLabel ("nb ="));
		add (tng = new JTextField ("1", 2));
		ng = 1;
		add (maj = new JButton ("maj"));
		maj.addActionListener (this);
		add (code = new JButton ("décode"));
		code.addActionListener (this);
		add (eff = new JButton ("x"));
		eff.addActionListener (this);
		add (ta = new TextArea (2, 40));
		add (tb = new TextArea (2, 40));
		add (tc = new TextArea (2, 40));
		alphanum = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789";
	}

	public void actionPerformed (ActionEvent evt) {
		if (evt.getSource() == maj) {
			tn.setText (rsavar.n.toString());
			n = rsavar.n;
			td.setText (rsavar.d.toString());
			BigInteger bi = BigInteger.TEN;
			ng = 0;
			while (bi.compareTo (n) < 0) {
				bi = bi.multiply (BigInteger.TEN);
				ng ++;
			}
			rsavar.ng = ng;
			tng.setText (Integer.toString (ng));
		} else if (evt.getSource() == eff) {
			ta.setText ("");
			tb.setText ("");
			tc.setText ("");
		} else if (evt.getSource() == code) {
			try {
				n = new BigInteger (tn.getText());
			}
			catch (NumberFormatException nfe) {}
			try {
				d = new BigInteger (td.getText());
			}
			catch (NumberFormatException nfe) {}
			int ng1 = ng;
			try {
				ng1 = Integer.parseInt (tng.getText());
			}
			catch (NumberFormatException nfe) {}
			if (ng1 > 0)
				ng = ng1;
			rsavar.ng = ng;
			tng.setText (Integer.toString (ng));
/*
 * convertit une chaîne en tableau de nombres
 */
			String s = ta.getText ();
			if ((s != null) && (s.length () != 0)) {
				StringTokenizer st = new StringTokenizer (s, " ", false);
				int index = Math.max (1, st.countTokens());
				BigInteger[] nm = new BigInteger [index];
				index = 0;
				while (st.hasMoreTokens()) {
					s = st.nextToken();
					try {
						nm [index] = new BigInteger (s);
						index ++;
					}
					catch (NumberFormatException e) {}
				}
/*
 * a ^ d % n.
 */
				for (int i = 0; i < index; i ++)
					nm [i] = nm[i].modPow(d, n);
/*
 * affichage
 */
				s = "";
				int ixm1 = index - 1;
				for (int i = 0; i < ixm1; i++) {
					BigInteger nmi = nm [i];
					int lnm = nmi.toString().length();
					for (int i1 = lnm; i1 <ng; i1++)
						s += "0";
					s += nmi.toString();
				}
				String s1 = nm [ixm1].toString();
				if ((s.length() % 2 == 0) && (s1.length() % 2 == 0) || (s.length() % 2 != 0) && (s1.length() % 2 != 0))
					s += s1;
				else
					s += "0" + s1;
				tb.setText (s);
				s1 = "";
				int sl = s.length ();
				int anl = alphanum.length ();
				for (int i = 0; i < sl; i += 2) {
					int j = Integer.parseInt (s.substring (i, i + 2));
					if ((j >= 1) && (j <= anl))
						s1 += alphanum.charAt (j - 1);
					else
						s1 += "?";
				}
				tc.setText (s1 + "\n");
			}
		}
	}
}

}
