Esta es una clase que permite generar juego de claves, guardarlas y por supuesto cifrar y descifrar, esta diseñada para Java SE ya que las funciones para guardar y recuperar las claves desde el disco no son compatibles con Android, si  necesitan esta clase para Android pueden ir al articulo Encriptar y Desencriptar con RSA en ANDROID sino pueden seguir viendo Encriptar y Desencriptar con RSA en JAVA

Primero como usamos la clase:

public class main {
	
	public static void main(String[] args) throws Exception {
		
		
		//Definimos un texto a cifrar
		String str = "Este es el texto a cifrar";
		
		System.out.println("\nTexto a cifrar:");
		System.out.println(str);
		
		//Instanciamos la clase
		RSA rsa = new RSA();
		
		//Generamos un par de claves
		//Admite claves de 512, 1024, 2048 y 4096 bits
		rsa.genKeyPair(512);
		
		
		String file_private = "/tmp/rsa.pri";
		String file_public = "/tmp/rsa.pub";
		
		//Las guardamos asi podemos usarlas despues
		//a lo largo del tiempo
		rsa.saveToDiskPrivateKey("/tmp/rsa.pri");
		rsa.saveToDiskPublicKey("/tmp/rsa.pub");
		
		//Ciframos y e imprimimos, el texto cifrado
		//es devuelto en la variable secure
		String secure = rsa.Encrypt(str);
		
		System.out.println("\nCifrado:");
		System.out.println(secure);
		
				
		
		//A modo de ejemplo creamos otra clase rsa
		RSA rsa2 = new RSA();
		
		//A diferencia de la anterior aca no creamos
		//un nuevo par de claves, sino que cargamos
		//el juego de claves que habiamos guadado
		rsa2.openFromDiskPrivateKey("/tmp/rsa.pri");	
		rsa2.openFromDiskPublicKey("/tmp/rsa.pub");
		
		//Le pasamos el texto cifrado (secure) y nos 
		//es devuelto el texto ya descifrado (unsecure) 
		String unsecure = rsa2.Decrypt(secure);
		
		//Imprimimos
		System.out.println("\nDescifrado:");
		System.out.println(unsecure);	
		

		
	}
	
	

}

La salida que produce el codigo anterior:


Texto a cifrar:
Este es el texto a cifrar

Cifrado:
19q1c3rn9p9rfnquycflpfqu0kv1rggv1wbtbvdkye2lwdi4lkoof2v3spgp768lbchd0g2qexl7t5csif8d8s5w574u79126pbz

Descifrado:
Este es el texto a cifrar


Por ultimo la clase RSA.java que hace todo:


import java.io.BufferedReader;
import java.io.BufferedWriter;

import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;

import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

public class RSA {
	
	public PrivateKey PrivateKey = null;
	public PublicKey PublicKey = null;

	public RSA()
	{
		

	}
	
	public void setPrivateKeyString(String key) throws NoSuchAlgorithmException, InvalidKeySpecException{
		byte[] encodedPrivateKey = stringToBytes(key);
		
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
		PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
		this.PrivateKey = privateKey;
	}

	public void setPublicKeyString(String key) throws NoSuchAlgorithmException, InvalidKeySpecException{
		
		byte[] encodedPublicKey = stringToBytes(key);
		
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedPublicKey);
		PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
		this.PublicKey = publicKey;
	}

	public String getPrivateKeyString(){
		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(this.PrivateKey.getEncoded());
	    return bytesToString(pkcs8EncodedKeySpec.getEncoded());
	}

	public String getPublicKeyString(){
		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(this.PublicKey.getEncoded());
		return bytesToString(x509EncodedKeySpec.getEncoded());
	}
	
	
	public void genKeyPair(int size) throws NoSuchAlgorithmException,NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException  {
		
	    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
	    kpg.initialize(size);
	    KeyPair kp = kpg.genKeyPair();
	    
	    PublicKey publicKey = kp.getPublic();
	    PrivateKey privateKey = kp.getPrivate();
	    
	    this.PrivateKey = privateKey;
	    this.PublicKey = publicKey;
	}

	public String Encrypt(String plain) throws NoSuchAlgorithmException,NoSuchPaddingException, InvalidKeyException,IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException, UnsupportedEncodingException, NoSuchProviderException {

	    byte[] encryptedBytes; 
  
	    Cipher cipher = Cipher.getInstance("RSA");
	    cipher.init(Cipher.ENCRYPT_MODE, this.PublicKey);
	    encryptedBytes = cipher.doFinal(plain.getBytes());

	    return bytesToString(encryptedBytes);

	}

	public String Decrypt(String result) throws NoSuchAlgorithmException,NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {

	    byte[] decryptedBytes;

	    Cipher cipher = Cipher.getInstance("RSA");
	    cipher.init(Cipher.DECRYPT_MODE, this.PrivateKey);
	    decryptedBytes = cipher.doFinal(stringToBytes(result));
	    return new String(decryptedBytes);
	}

	public String bytesToString(byte[] b) {
	    byte[] b2 = new byte[b.length + 1];
	    b2[0] = 1;
	    System.arraycopy(b, 0, b2, 1, b.length);
	    return new BigInteger(b2).toString(36);
	}

	public byte[] stringToBytes(String s) {
	    byte[] b2 = new BigInteger(s, 36).toByteArray();
	    return Arrays.copyOfRange(b2, 1, b2.length);
	}


	public void saveToDiskPrivateKey(String path) throws IOException {
		try {
			Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path), "UTF-8"));
			out.write(this.getPrivateKeyString());
			out.close();
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
	
	public void saveToDiskPublicKey(String path) {
		try {
			Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path), "UTF-8"));
			out.write(this.getPublicKeyString());
			out.close();
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

	public void openFromDiskPublicKey(String path) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
		String content = this.readFileAsString(path);
		this.setPublicKeyString(content);
	}
	
	public void openFromDiskPrivateKey(String path) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
		String content = this.readFileAsString(path);
		this.setPrivateKeyString(content);
	}
	
	
	private String readFileAsString(String filePath) throws IOException {
        StringBuffer fileData = new StringBuffer();
        BufferedReader reader = new BufferedReader(
                new FileReader(filePath));
        char[] buf = new char[1024];
        int numRead=0;
        while((numRead=reader.read(buf)) != -1){
            String readData = String.valueOf(buf, 0, numRead);
            fileData.append(readData);
        }
        reader.close();
        return fileData.toString();
    }
		
}


10 comentarios

  1. Hola:

    Después de casi dos años … una pregunta.
    Modifiqué main() para separar encripción y desencripción en dos clases distintas que se «comunican» a través de files en disco.

    Se supone que debería poder desencriptar con clave pública, pero no es así.
    Copio los dos main modificados que no funcionan (bueno, el de desenciptar solo). En el main de desencripción está comentada la parte de clave privada y habilitada la parte de clave pública y no funciona.
    Espero que se entienda la duda.
    ———————————————————————————
    Main para encriptar
    ******************
    public static void main(String[] args) throws Exception {

    //Definimos un texto a cifrar
    String str = «Este es el texto a cifrar, OK ??»;
    System.out.println(str);

    //Instanciamos la clase
    RsaEncripta rsa = new RsaEncripta();

    //Generamos un par de claves
    //Admite claves de 512, 1024, 2048 y 4096 bits
    rsa.genKeyPair(512);

    //Las guardamos asi podemos usarlas despues
    //a lo largo del tiempo
    // rsa.saveToDiskPrivateKey(«C:/tmp/rsa.pri»);
    rsa.saveToDiskPublicKey(«C:/tmp/rsa1.pub»);

    //Ciframos y e imprimimos, el texto cifrado
    //es devuelto en la variable secure
    String secure = rsa.Encrypt(str);

    System.out.println(«\nCifrado:»);
    System.out.println(secure);
    RandomAccessFile encriptado = new RandomAccessFile («c:\\tmp\\encriptado.txt», «rw»);
    encriptado.writeBytes(secure);
    encriptado.close();
    }
    —————————————————————————————-
    Main para desencriptar
    ******************
    public static void main(String[] args) throws Exception {
    String secure = «»;
    String str = «»;

    RandomAccessFile encriptado = new RandomAccessFile («c:\\tmp\\encriptado.txt», «rw»);
    secure = encriptado.readLine();
    encriptado.close();
    System.out.println(secure);

    //A modo de ejemplo creamos otra clase rsa
    RsaDesEncripta rsa2 = new RsaDesEncripta();

    //A diferencia de la anterior aca no creamos
    //un nuevo par de claves, sino que cargamos
    //el juego de claves que habiamos guadado
    // rsa2.openFromDiskPrivateKey(«C:/tmp/rsa.pri»);
    rsa2.openFromDiskPublicKey(«C:/tmp/rsa.pub»);

    //Le pasamos el texto cifrado (secure) y nos
    //es devuelto el texto ya descifrado (unsecure)
    String unsecure = rsa2.Decrypt(secure);

    //Imprimimos
    System.out.println(«Descifrado:»);
    System.out.println(unsecure);
    }

    1. Hola Ignacio, en realidad es al revés, cifras con la pública y descifras con la privada, si descifraras con la pública cualquiera podría hacerlo ya que es pública, lo que garantiza la seguridad de los datos es que la clave para descifrar es que realmente sea privada para que sólo tu lo puedas decifrar el mensaje codificado, prueba con la privada en la segunda clase, espero que sea de ayuda y cualquier consulta me avisas quedo a las órdenes.

  2. Muchas gracias por tu respuesta Alvaro.

    Efectivamente, ahora si funciona con el cambio de roles.

    Yo estaba pensando en un escenario distinto, donde yo envío un texto encriptado con clave privada y el destino desencripta con una pública y por lo tanto conocida. De esta manera saben que el originario sin duda soy yo, el conocedor de la clave privada.

    Saludos
    Ignacio

    1. Hola Ignacio, te comprendo, lo que puedes hacer es renombrar las funciones, que la llamemos publica o privada es un tanto arbitrario, en realidad una cifra y la otra descifra, si intercambias los nombres de pública por privada y privada por pública la función de cada una no cambia en realidad y se ajustaria mejor a lo que necesitas, Saludos y a la ordenes

  3. Voy a probar ese cambio.
    Alvaro:

    Creo que entiendo el concepto… La pública finalmente es la que hago conocer (o hago pública, claro).

    Sin embargo (perdon por mi ignorancia) veo que la clave privada que genera la clase es bastante mas larga que la pública. Tiene sentido hacer el cambio de roles y usar una clave privada mas corta que la pública ? Tendría que hacer un cambio en los métodos, no ?.

    Voy a probar de intercambiar las funciones y veo …

    Slds
    Ignacio

    1. Hola Ignacio, el largo de la clave es debido a la función que cumple cada una.

      Originalmente esta pensado para que alguien cifre datos con la pública y que solo sean descifrados por la clave privada, en tu caso por lo que entendí lo que quieres hacer es al revés, simplemente estarías invirtiendo los roles, solo tienes que invertír el nombre de los métodos.

      Aunque la verdad no se que tan seguro será, en el código original teniendo la clave pública nunca puedes averiguar la privada, lo que no se, si teniendo la privada puedes obtener la pública, si esto fuera así y si tu estubieras distribuyendo la privada y podrian averiguar la pública y cualquiera podría cifrar por ti, es un tema complejo, entiendo los conceptos, el funcionamiento y su implementacion pero la verdad no soy criptografo para comprender el algoritmo matemático de fondo y la conveniencia de invertir las claves.

      Hay un libro sobre este tema que te puedo ayudar, yo lo tengo pero aviso que no es muy sencillo de leer
      https://0xword.com/es/libros/36-libro-cifrado-comunicaciones-rsa.html

  4. Hola Alvaro!

    Puedo cifrar un documento XML usando AES256 y la llave que valla cifrada dentro de este mismo documento pero usando RSA SHA256

  5. Hola Alvaro, la clase RSA tu la creaste? Podrias explicarla un poco por favor, para entender mejor este algoritmo RSA ya wue ko lo alcanzo a comprender.

    Saludos y gracias pr tu apoyo

    1. Hola.

      La clase la cree yo si, igualmente poco para explicar sobre RSA en mismo ya que la clase simplemente encapsula ciertas operación sobre las librerías java para cifrado, que son la que realmente hacen el trabajo.

      La clase que yo escribí lo único que hace es usar las librerías para generar las claves y luego usarlas para cifrar y descifrar los datos de forma más sencilla, como funciona RSA mismo está dentro de estas librerías y no en la clase que yo escribí.

      Si quieres aprender sobre como funciona RSA hay un libro sobre este tema que te puedo ayudar:
      https://0xword.com/es/libros/36-libro-cifrado-comunicaciones-rsa.html

      Saludos

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *