Ayer mostraba una clase java para cifrado y descifrado RSA en para Java SE ahora traigo la misma clase pero modificada para funcionar en una app en Android

Si quieren ver la version para Java SE pueden ir a Encriptar y Desencriptar con RSA en JAVA sino pueden seguir leyendo: Encriptar y Desencriptar con RSA en ANDROID

Les dejo el codigo fuente del ejemplo AndroidRSATest.zip

Ejemplo de como utilizar la clase para Android:

public class MainActivity extends AppCompatActivity {


    private TextView txtOrignal;
    private TextView txtEncode;
    private TextView txtDecode;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        txtOrignal = (TextView) findViewById(R.id.txtOriginal);
        txtEncode = (TextView) findViewById(R.id.txtEncode);
        txtDecode = (TextView) findViewById(R.id.txtDecoded);

        txtOrignal.setText("Este es el texto a cifrar");


    }

    public void crypt(View view) {

        try {

           //Obtenemos el texto desde el cuadro de texto 
            String original = txtOrignal.getText().toString();
            
            RSA rsa = new RSA();

            //le asignamos el Contexto
            rsa.setContext(getBaseContext());

            //Generamos un juego de claves
            rsa.genKeyPair(1024);

            //Guardamos en la memoria las claves
            rsa.saveToDiskPrivateKey("rsa.pri");
            rsa.saveToDiskPublicKey("rsa.pub");

            //Ciframos
            String encode_text = rsa.Encrypt(original);

            //Mostramos el texto cifrado
            txtEncode.setText(encode_text);

            
            //Creamos otro objeto de nuestra clase RSA
            RSA rsa2 = new RSA();

            //Le pasamos el contexto
            rsa2.setContext(getBaseContext());

            //Cargamos las claves que creamos anteriormente
            rsa2.openFromDiskPrivateKey("rsa.pri");
            rsa2.openFromDiskPublicKey("rsa.pub");

            //Desciframos
            String decode_text = rsa2.Decrypt(encode_text);

            //Mostramos el texto ya descifrado
            txtDecode.setText(decode_text);
        } catch (Exception e) {

        }


    }
}

También tenemos la opción de no guardarlas en disco para manejaras en memoria o en una base de datos por lo cual podemos asignarlas a variables

     //Guardamos las claves
     String publicKey = rsa.getPublicKeyString();
     String privateKey = rsa.getPrivateKeyString();
     

     //Las claves guardadas anteriormente las podemos
     //Aignar a otra clase para poder cifrar o descifrar
     rsa2.setPublicKeyString(publicKey);
     rsa2.setPrivateKeyString(privateKey);

Por ultimo la clase RSA.java

package uy.com.adl.androidrsatest;

import android.content.Context;
import android.util.Log;


import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
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 Context context;

    public RSA()
    {
        

    }

    public Context getContext() {
        return context;
    }

    public void setContext(Context context) {
        this.context = context;
    }

    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, 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){
        try {
            FileOutputStream outputStream = null;
            outputStream =  this.context.openFileOutput(path, Context.MODE_PRIVATE);
            outputStream.write(this.getPrivateKeyString().getBytes());
            outputStream.close();
        } catch (Exception e) {
            Log.d("RSA:","Error write PrivateKey");
        }
    }
    
    public void saveToDiskPublicKey(String path) {
        try {
            FileOutputStream outputStream = null;
            outputStream =  this.context.openFileOutput(path, Context.MODE_PRIVATE);
            outputStream.write(this.getPublicKeyString().getBytes());
            outputStream.close();
        } catch (Exception e) {
            Log.d("RSA:","Error write Public");
        }
    }

    public void openFromDiskPublicKey(String path) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
        String content = this.readFileAsString(path);
        this.setPublicKeyString(content);
    }
    
    public void openFromDiskPrivateKey(String path) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
        String content = this.readFileAsString(path);
        this.setPrivateKeyString(content);
    }


    private String readFileAsString(String filePath) throws IOException {

        BufferedReader fin = new BufferedReader(new InputStreamReader(context.openFileInput(filePath)));
        String txt = fin.readLine();
        fin.close();
        return txt;

    }
        
}

10 comentarios

  1. Excelente aporte, se que ya hace bastante tiempo fue publicada esta entrada pero considero que nuca es tarde para dar gracias ^^

    Muchas gracias! ♥

    1. Gracias por tu comentario, me alegro que te haya servido, una de las razones por lo que lo escribí es porque nunca encontré algo sencillo y rápido de implementar y a muchos les debe pasar lo mismo porque este post y el otro de RSA en Java (no android) son de los más vistos del blog por lejos.

    1. Hola Andy

      Para descifrar lo único que tienes que hacer es desde la otra clase crear una instancia del objeto RSA nuevamente, asignarle la clave privada que tienes que haber guardado cuando la cifraste, no crear llaves nuevas sino usar el par con el que fue cifrado, luego a la función RSA.Decrypt() pasarle el texto cifrado que te devolverá el texto descifrado.

      Saludos

  2. Hola buenos días, no entiendo como manejar las claves en una Base de Datos, tengo el problema de que cuando voy a desencriptar la información desde otro dispositivo, no lo hace. me podrías ayudar, por favor.

    GRACIAS

    1. Hola Christian, para pasarle las claves que vengan de la base de datos puedes hacerlo mediante las funciones setPublicKeyString y setPrivateKeyString, lo deberías hacer en ambos dispositivos, solo te debes asegurar que las claves se estén guardando sin problemas, te debería funcionar, saludos

  3. Buenas Alvaro,
    Lo primero muchas gracias por el artículo, ya que es difícil encontrar material para poder cifrar y descifrar mensajes con clave publica y privada.
    Mi pregunta es sobre las funciones Encrypt y Decrypt de la clase RSA:
    – ¿Por qué en Encrypt usas la funcion bytesToString() creada por ti, para pasar del arrray de bytes a un string como resultado en lugar de usar new String() como si haces en Decrypt?
    – La opuesta a la anterior, ¿Por qué en Decrypt usas la funcion stringToBytes() creada por ti, para pasar de string a array de bytes en lugar de usar result.getBytes() como si haces en Encrypt?
    Lo haces únicamente para implementar tu las funciones, o en ellas hay algo que no realicen las que puedes usar por defecto en Android.

    Muchas gracias por la ayuda,
    Un saludo, Alejandro.

    1. Hola Ramon

      Honestamente me mataste con la pregunta, tendría que entrar a ver el código nuevamente, igual el código es un puzzle, lo que te puedo decir es que el código es producto de muchas horas de prueba y error para lo cual probe cuanto ejemplo encontré en la vuelta, ejemplos que en muchos casos no era para lo que yo quería y que ni siquiera eran en java y adapte a java, probablemente la función de cifrado y descifrado sea de ejemplos diferentes o de lenguajes diferentes y por eso no sea muy coherente y lo publique porque realmente yo me enfrente al mismo problema que tu de necesitar cifrar algo y no encontrar un ejemplo claro o practico.

      Saludos

  4. Que buen tutorial, tengo una pregunta, si ya tengo mis propias llaves (privada y publica) como puedo importarlas?

    1. Hola Guillermo

      Muchas gracias por tu comentario, con respecto a tu consulta puedes leer las claves con las funciones openFromDiskPrivateKey y openFromDiskPublicKey, podrás leer las claves que la propia clase genero o clases similares basadas en la misma librería nativa de java desconozco si puede funcionar con claves generadas con otros programas, quizás no porque utilicen algoritmos diferentes

Deja una respuesta

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