Implementación del algoritmo de encriptación RSA en Java (parte 2)
Creación de la interfaz gráfica de usuario usando Swing
Siguiendo el anterior capítulo de este artículo, ahora crearemos el front-end para usar el programa de encriptación/desencriptación. Y aquí me gustaría tratar de mostrarles cómo crear interfaces gráficas desde cero, usando la “técnica” que yo uso. Esto puede parecer muy tonto para algunos, pero en verdad son muchos los casos en los que he visto a las personas de mi universidad maldiciéndo por no poder crear una bendita GUI. Por esto, me gustaría enfatizar en la creación de GUIs.
Lo primero es imaginarse como podría ser el diseño... poner la mente a trabajar, coger papel y lápiz y comenzar a diseñar. Y lo primero que se me ocurre es algo como esto:


Lo primero que salta a la vista es que: como artista me muero de hambre, y como editor de gráficos digitales: peor. Pero hasta aquí lo importante es tener claro qué se va a hacer. Una vez escogemos cual de los dos diseños vamos a usar (prefiero el segundo), a programar se dijo! Ten en cuenta que como vamos a usar Swing, debemos importar el paquete javax.swing.
Ahora, lo primero es crear la ventana que contendrá nuestro programa, para lo cual utilizaremos un objeto JFrame. Y comenzar a pensar cómo se van a distribuir los objetos dentro de la misma, para lo cual se hace uso de los “layouts”... tenemos muchas opciones, de las cuales me gustaría comentarles las más importantes y luego decidiremos cual es la más adecuada:
- Es posible crear los objetos de la interfaz de usuario, y asignarles coordenadas y tamaños específicos dentro de la ventana (hazlo solo cuando sea verdaderamente necesario).
- Es posible usar GridLayout, que nos permitirá manejar el contenido de la ventana como si fuera una tabla de X por Y celdas. Dentro de cada celda colocaríamos un objeto de la GUI.
- Otra opción es usar BorderLayout, que nos permite distribuir los objetos en cinco espacios: norte, sur, este, oeste y centro, siendo este último el más grande de todos.
Lo primero es ver qué coincidencias existen entre las posiciones de los objetos, y lo diagramamos de nuevo en papel:

Como podemos ver, en el área más grande (la de abajo, en verde) hay dos áreas de texto que ocupan el mismo tamaño... así que una buena opción sería meterlas en un GridLayout que tenga una fila y dos columnas. Arriba (en rojo), aunque no es la mejor interpretación que he hecho (la cuestión es que quede lo más sencillo posible), podemos observar que hay una cuadrícula de dos filas por tres columnas.
Así que lo más conveniente es usar dos contenedores que usen GridLayout. Uno para arriba, y el otro para abajo (¿o para el centro?). Y al JFrame, asignar el layout BorderLayout, situando dichos contenedores en NORTH y CENTER (se ve un poco mejor que en la hoja, pero solo un poco... al fin y al cabo no me había quedado tan feo):
Lo que en dividiéndolo mentalmente sería:
Además de esto, tenemos que decidir como vamos a manejar los eventos de la GUI, es decir, qué hacer cuando el usuario le dé clic en el botón “Generar Claves” o en el boitón de radio “Encriptar”, y para ello me gustaría enseñarles en este mismo programa 3 difirentes tipos de usar eventos:
- Desde una clase interna anónima
- Desde una clase interna
- Desde un método dentro de la clase
Pero vamos por partes... Con el fin de que entendamos y aprendamos de una manera más sencilla, explicaré parte por parte la clase VentanaRSA.java, comencemos con la importación de paquetes y la declaración de la misma:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.math.BigInteger;
public class VentanaRSA extends JFrame implements ActionListener{
No hay mucho que explicar aquí: importamos los paquetes para interfaces gráficas Swing y AWT, además del paquete que contiene las clases para manejar eventos y la clase BigInteger. Al declarar la clase (como va a ser una ventana) heredamos de la clase JFrame e implementamos la interfaz ActionListener para manejar eventos. Ahora declaramos los objetos que usaremos, y posteriormente los iniciaremos en el constructor:
private JTextField cajaTamPrimo;
private JButton btnGenenerarClaves;
private ButtonGroup grupoBotonesOpcion;
private JRadioButton btnEncriptar, btnDesEncriptar;
private JTextArea areaOrigen, areaDestino;
private Container cntAreas, cntGenClaves;
private RSA rsa;
private BigInteger[] textoCifrado;
En el constructor de la clase vamos a iniciar los objetos con sus respectivos valores, además de asignarle un manejador de eventos a cada objeto de la interfaz gráfica:
public VentanaRSA() {
super("Interfaz Grafica - RSA");
getContentPane().setLayout(new BorderLayout(3,3));
cajaTamPrimo = new JTextField();
cajaTamPrimo.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent evt) {
generarClaves();
}
});
btnGenenerarClaves = new JButton("Generar Claves");
btnGenenerarClaves.addActionListener(this);
btnEncriptar = new JRadioButton("Encriptar", false);
btnEncriptar.addActionListener( new ManejadorBotonOpcion() );
btnEncriptar.setEnabled(false);
btnDesEncriptar = new JRadioButton("Desencriptar", false);
btnDesEncriptar.addActionListener( new ManejadorBotonOpcion() );
btnDesEncriptar.setEnabled(false);
grupoBotonesOpcion = new ButtonGroup();
grupoBotonesOpcion.add(btnEncriptar);
grupoBotonesOpcion.add(btnDesEncriptar);
areaOrigen = new JTextArea();
areaOrigen.setWrapStyleWord(true);
areaOrigen.setLineWrap(true);
areaDestino = new JTextArea();
areaDestino.setLineWrap(true);
areaDestino.setEditable(false);
cntAreas = new Container();
cntGenClaves = new Container();
cntGenClaves.setLayout(new GridLayout(2,3));
cntGenClaves.add(new JLabel("Digita el tamaño del número primo: "));
cntGenClaves.add(cajaTamPrimo);
cntGenClaves.add(btnGenenerarClaves);
cntGenClaves.add(new JLabel("Digita el texto a cifar/descifrar:"));
cntGenClaves.add(btnEncriptar);
cntGenClaves.add(btnDesEncriptar);
cntAreas.setLayout(new GridLayout(1,2,5,5));
cntAreas.add(new JScrollPane(areaOrigen));
cntAreas.add(new JScrollPane(areaDestino));
getContentPane().add(cntGenClaves, BorderLayout.NORTH);
getContentPane().add(cntAreas, BorderLayout.CENTER);
setSize(650,300);
setVisible(true);
}
Paso a explicar lo más importante:
- El método super sirve para iniciar el objeto del cual hereda una clase y en este caso, que redamos de JFrame, le enviamos el texto que va a tener la barra del título de la ventana.
- Con el método getContentPane, asignamos a la ventana el layout que administrará su contenido... en este caso un BorderLayout.
- He aquí una parte interesante, vamos a definir el primer manejador de eventos usando una clase inrterna anónima. Esta se la asignamos al objeto cajaTamPrimo, y nos permitirá manejar los eventos que genere dicho objeto (en este caso el evento es que se de Enter despues de escribir algo sobre esta caja de texto).
- Lo primero es usar el método addActionListener para adicionar un manejador de eventos. Este método recibe un objeto que haya implementado la interfaz ActionListener, que manejará los eventos.
- Fijémonos que creamos una clase de tipo ActionListener, y por ende debemos redeclarar el método actionPerformed, el cual será invocado al producirse un evento. En este caso, cuando alguien escriba algo sobre la caja de texto y presione Enter, se invocará el método generarClaves().
- Ahora utilizaremos otra técnica para manejar eventos: se trata de que, como en este caso, la clase principal (VentanaRSA.java) implemente la interfaz ActionListener, y dicha clase maneje eventos. Te recuerdo que al implementar la interfaz ActionListener, una clase está obligada a declarar el método actionPerformed, vaya o no a usarlo. Esta vez vamos a asociar este manejador de eventos al botón “Generar claves” (btnGenenerarClaves), usando el método addActionListener así: btnGenenerarClaves.addActionListener(this).
- Cabe notar aquí, que usamos la palabra this, para indicar al método addActionListener, que el manejador de eventos del botón btnGenenerarClaves, será la misma clase donde este se encuentra.
- Hora de usar la tercera técnica de manejo de eventos... esta vez se trata de usar una clase interna, en donde se implementará alguna interfaz de manejo de eventos (en este caso la ya mencionada ActionListener)... esta técnica la vamos a usar para manipular los eventos generados por los botones de radio, y su uso es muy sencillo: basta con crear un objeto de la clase dentro del método addActionListener así: btnEncriptar.addActionListener( new ManejadorBotonOpcion() ); Más adelante veremos cómo está construida la clase ManejadorBotonOpcion
- Con respecto a la inicialización de los otros objetos de la GUI no creo que haya mucho que explicar, pero me gustaría recalcar el método setEnabled() que es bastante útil. Este método lo poseen casi todos los objetos de interfaz gráfica, y sirve para habilitar/inhabilitar los objetos mismos. Recibe un valor booleano, y por ejemplo cuando hacemos: btnEncriptar.setEnabled(false);, estamos indicando que el objeto btnEncriptar va a estar inhabilitado.
- El manejo de contenedores en muy simple. Recordemos que usaremos dos contenedores: uno para las areas de texto de abajo, y el otro para los objetos de arriba. Los contenedores no son más que objetos de la clase Container, a los que les asignamos un layout que distribuirá los objetos que contenga. En nuestro caso, recordemos que el contenedor superior será una cuadrícula de 2X3 celdas, por lo cual podremos asignar dicho layout de la siguiente forma: cntGenClaves.setLayout(new GridLayout(2,3)) Una vez hayamos definido el administrador de contenido, nos dispondremos a añadir los objetos de la interfaz gráfica que contendrá... esto lo hacemos con el método add, que no solo recibe objetos de la GUI, sino también otros contenedores: cntGenClaves.add(cajaTamPrimo);
- Otra cosa interesante por notar es el uso de los scrolls (JScrollPane), en este caso lo usamos para envlover las areas de texto (JTextArea), ya que posiblemente contendrán muchas letras y si es necesario que pueda mostrarnoslas todas. Para ello, al añadir el objeto al contenedor NO hacemos esto: add(areaOrigen) SINO que cargamos el area dentro del scroll así: add(new JscrollPane(areaOrigen)) De lo contrario lo más seguro es que no podamos acceder al texto que se encuentre abajo, cuando ya no quepa más.
- Además de los Scrolls, podemos usar los métodos setLineWrap y setWrapStyleWord, que no permitirá scrolls horizontales (causados por ejemplo por palabras muy largas).
- Al momento de añadir objetos al JFrame, lo podemos hacer directamente invocando el método getContentPane seguido del método add. En este caso, como usamos el layout BorderLayout, debemos pasarle un parámetro adicional al método add, que le indicará en qué parte situará el objeto (arriba, abajo, izquierda, derecha o centro), dicho parámetro es un entero que podemos tomar de las variables públicas estáticas de la clase BorderLayout... por ejemplo para situar el contenedor cntGenClaves arriba debemos poner lo siguiente: getContentPane().add(cntGenClaves, BorderLayout.NORTH);
- Por último, para asignarle un tamaño a la ventana y hacerla visible (por defecto viene invisible), debemos usar los métodos setSize(650,300) y setVisible(true) respectivamente.
Veamos ahora el método actionPerformed, que será invocado cada vez que el botón btnGenenerarClaves genere un evento:
public void actionPerformed( ActionEvent evento ) {
if(evento.getSource().equals(btnGenenerarClaves))
generarClaves();
}
Lo único a resaltar aquí es el método getSource de la clase ActionEvent, que en conjunto con el método equals de la clase Object nos permitirá saber quién generó el evento. El siguiente método a tratar es generarClaves, que simplemente invocará los métodos generaPrimos y generaClaves de la clase RSA.java, y nos los mostrará:
private void generarClaves() {
if(cajaTamPrimo.getText().equals(""))
JOptionPane.showMessageDialog(null,
"No haz introducido el tamaño del primo",
"Tenemos problemas", JOptionPane.ERROR_MESSAGE);
else {
rsa = new RSA(Integer.parseInt(cajaTamPrimo.getText()));
rsa.generaPrimos();
rsa.generaClaves();
JTextArea area = new JTextArea(20,50);
area.setEditable(false);
area.setLineWrap(true);
area.append("Tam clave: "+cajaTamPrimo.getText()+"\n\n");
area.append("p:["+rsa.damep()+"]\n\nq:["+rsa.dameq()+"]\n\n");
area.append("Clave publica (n,e):\n\nn:["+
rsa.damen()+"]\n\ne:["+rsa.damee()+"]\n\n");
area.append("Clave publica (n,d):\n\nn:["+rsa.damen()+"]\n\nd:["
+rsa.damed()+"]");
JOptionPane.showMessageDialog(null, new JscrollPane(area),
"Primos generados", JOptionPane.INFORMATION_MESSAGE);
btnEncriptar.setEnabled(true);
btnDesEncriptar.setEnabled(true);
}
}
De aquí podemos tomar algunas cosas interesantes...
- Comencemos con la clase JOptionPane, la cual nos permitirá usar cuadros de diálogo básicos. En este caso solo usamos su método estático showMessageDialog, en donde podremos introducir un texto a mostrar (o un objeto, como veremos ,más adelante), el título del cuadro de diálogo, y un icono a mostrar. En cuanto al icono, podemos escoger entre imágenes referentes a error, información, alarma, pregunta y creo que no más... por ejemplo, para usar el de error podemos hacer JoptionPane.ERROR_MESSAGE
- Otro punto interesante de la clase JOptionPane, es que en vez de texto simple podemos colocar un objeto gráfico... por ejemplo podemos colocar un objeto JTextArea como en el código de arriba: JOptionPane.showMessageDialog(null, new JscrollPane(area),"Titulo")
Pasemos entonces a ver cómo está construida la clase interna , que en nuestro caso maneja los eventos de los botones de radio:
private class ManejadorBotonOpcion implements ActionListener {
// manejar eventos de botón de opción
public void actionPerformed( ActionEvent evento ) {
if(evento.getSource().equals(btnEncriptar)) {
if(areaOrigen.getText().equals(""))
JoptionPane.showMessageDialog(null,
"No haz introducido datos para cifrar",
"Tenemos problemas", JOptionPane.ERROR_MESSAGE);
else {
textoCifrado = rsa.encripta(areaOrigen.getText());
areaDestino.setText("");
for(int i=0; i<textoCifrado.length; i++)
areaDestino.append(textoCifrado[i].toString());
}
} else if(evento.getSource().equals(btnDesEncriptar)) {
if(areaOrigen.getText().equals(""))
JoptionPane.showMessageDialog(null,
"No haz introducido datos para decifrar",
"Tenemos problemas", JOptionPane.ERROR_MESSAGE);
else {
areaDestino.setText("");
String recuperarTextoPlano = rsa.desencripta(textoCifrado);
areaDestino.setText(recuperarTextoPlano);
}
}
}
}
Como puedes ver implementa la interfaz ActionListener, por lo que más abajo tiene que declarar el método actionPerformed. Allí adentro se cifran/decifran lo que se haya puesto en una de las areas de texto. La verdad no hay mucho que explicar aquí así que vamos directamente al código del método main:
public static void main(String args[]) {
JFrame.setDefaultLookAndFeelDecorated(true);
VentanaRSA ventana = new VentanaRSA();
ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
Aquí solo hay dos cosas por resaltar:
- JFrame.setDefaultLookAndFeelDecorated(true): esto lo que hace es activar el Look&Feel, y logramos que nuestra ventana se vea bonita y además que se muestre igual en cualquier sistema operativo.
- ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE): esto nos evita tener que poner a la escucha algún método para cerrar la ventana, es decir, con esto le decimos que cuando el usuario de clic en la X de la esquina superior derecha, se cierre el programa. De otro modo hay que usar otros métodos mucho más complicados.
Bien, eso es todo... probemos entonces el programa!
Si no colocamos ningún texto para cifrar/decifrar:
Ummm, ahora que miro la anterior imágen... ¿se escribe “has” o “haz”? En fin... pasemos a la encriptación:
Y la desencriptación (¿o desencripción?)
Como puedes ver... hubo un problema con las tildes y los símbolos netamente castellanos, pero culpa de Java lo juro:(
Descargas y enlaces
- Código fuente del programa
- Implementación del algoritmo de encriptación RSA en Java versión PDF
- Implementación del algoritmo de encriptación RSA en Java versión OpenDocument para editar
- Implementación del algoritmo de encriptación RSA en Java (parte 1)














Nacho dice:
Enero 25th, 2008 a las 1:22 pm
Hola.
Homero….utilizas directamente BigInteger.toString(), de cada uno de los elmentos del vector de BigInteger[] y los vas concatenando en un string con espacios en blanco entre ellos? O los pasas a byte y despúes a string?
Te agradecería la ayuda.
Gracias
Homero dice:
Enero 25th, 2008 a las 1:44 pm
Lo que hago es guardar la representación en String de cada BigInteger del Array, en un StringBuffer, y separados por espacios en blanco. Luego a partir de este String puedes recuperar cada BigInteger haciendo new BigInteger(strFragmento) donde strFragmento es cada uno de los BigInteger pasado a string. Prueba con distintas longitudes de clave! y cosas así. La solución funciona pero seguramente debe existir otra manera que no sean los espacios en blanco para separar los BigInteger’s
Homero dice:
Enero 25th, 2008 a las 1:46 pm
Perdon, Si simplemente hago ((BigInteger)myBigIntegers[i]).toString()
erick dice:
Abril 21st, 2008 a las 5:56 pm
que chido esta el foro y sus temas gracias por la ayuda que proporcionan
roberto dice:
Agosto 3rd, 2008 a las 10:15 pm
hola a todos como estan? tengo una pregunta sobre RSA deseo crea una pagina para una empresa de manera que ellos se puedan inscribir desde internet y que al inscribirse se genere una clave, la cuestion esta en asociar el email con un numero de identificacion, que programa puedo utilizar para simular dicha accion?? pgp?? .
saludos y gracias
Casidiablo dice:
Agosto 4th, 2008 a las 10:28 am
Sí, lo mejor es PGP.
Sarahi dice:
Octubre 3rd, 2008 a las 11:25 am
oye cuando encripto una palabra al momento de cambiar la clave de encriptación y copiar el código generado y desencriptarlo con otra clave me da la misma palabra que con la clave anterior
Cristian dice:
Octubre 3rd, 2008 a las 5:01 pm
No te entiendo… pónle un par de comas y verás como se te entiende. Ahora, por otro lado, si es lo que creo pues no debería pasar. Si cambias la clave es imposible que decifres algo con otra clave
Un saludo