Firma digital con RSA y Dart aplicable a Flutter

Firma digital con Dart y RSA

El día de hoy vamos a ver cómo firmar un mensaje de manera digital con Dart. Al hacerlo con Dart vamos a poder aplicar esto con Flutter más adelante, y así podremos firmar digitalmente en Android y cualquier otra plataforma soportada por Dart.

Firma digital con RSA y Dart aplicable a Flutter
Firma digital con RSA y Dart aplicable a Flutter

Recuerda que vamos a necesitar un par de claves: la privada y la pública. Ambas necesitan funcionar con la encriptación RSA.

Por cierto, toma en cuenta que vamos a firmar, no a encriptar. Lo que vamos a hacer es verificar la autenticidad de un mensaje, pero no vamos a esconder ni proteger nada.

Generando claves

Puedes generar las RSA keys de la manera que tú prefieras aunque yo recomiendo OpenSSL:

Generar par de claves RSA con OpenSSL (privada y pública)

Vamos a firmar con la privada y después verificaremos con la pública. Todo esto en Dart que más adelante, si es que quieres, se puede aplicar a Flutter.

Importando encrypt

Para firmar con RSA en Dart necesitamos el paquete encrypt.

Dependiendo si usar flutter o dart se importa de distinta manera pero en el enlace que dejé anteriormente está la guía de instalación. En mi caso solo lo usaré en dart así que ejecuto:

dart pub add encrypt

Solo como ejemplo, mi pubspec.yaml queda así:

name: newtify
description: >-
  Have you been turned into a newt?  Would you like to be?
  This package can help. It has all of the
  newt-transmogrification functionality you have been looking
  for.
version: 1.2.3
homepage: https://example-pet-store.com/newtify
documentation: https://example-pet-store.com/newtify/docs

environment:
  sdk: '>=2.10.0 <3.0.0'
dependencies:
  encrypt: ^5.0.1
  

Nota: fíjate en dependencies.

Firma digital con RSA y Dart

Comencemos viendo cómo firmar. Para esto importamos el paquete que agregamos previamente, creamos una nueva instancia de Signer pasándole un RSASigner con la clave privada y después invocamos a sign.

String firmar(String clavePrivadaParaFirmarComoCadena, String mensaje) {
  final clavePrivada = RSAKeyParser().parse(clavePrivadaParaFirmarComoCadena);
  final firmador =
      Signer(RSASigner(RSASignDigest.SHA256, privateKey: clavePrivada));
  String mensajeFirmado = firmador.sign(mensaje).base64;
  return mensajeFirmado;
}

Este método devolverá el mensaje firmado, codificado como base64 (estamos accediendo a base64 de la clase Encrypted, pero podemos acceder a bytes, base16, etcétera).

La función recibe la clave privada RSA y el mensaje original que queremos firmar.

Toma en cuenta que una cosa es la clave como String (la que definimos nosotros y generamos con OpenSSL) y otra cosa es la clave RSAAsymmetricKey que se genera al invocar a parse en la línea 2.

Por cierto, estamos codificando el mensaje firmado en base64. Al verificar el mensaje primero tendremos que decodificar.

Verificar firma con Dart

Ya hemos visto cómo firmar un mensaje, ahora falta comprobar que ese mensaje fue firmado con la clave privada usando la clave pública; es decir, que es auténtico.

El método queda así:

bool verificarFirma(
    String firmado, String plano, String clavePublicaComoCadena) {
  final clavePublica = RSAKeyParser().parse(clavePublicaComoCadena);
  final firmador =
      Signer(RSASigner(RSASignDigest.SHA256, publicKey: clavePublica));
  var coinciden = firmador.verify64(plano, firmado);
  return coinciden;
}

En la línea 3 creamos la clave pública que será de tipo RSAAsymmetricKey igual que lo hicimos al firmar, pero ahora es para la clave pública.

La verdadera comprobación está en la línea 6. Ahí invocamos a verify64 pasándole el mensaje original y el mensaje firmado.

Recuerda que el firmado está en base64 porque así lo devolvemos al firmar.

Poniendo todo junto

Veamos el ejemplo completo. Yo he dejado ahí ambas claves RSA para firmar con Dart y Flutter, pero recuerda que en la vida real solo debes distribuir la pública para verificar la firma, y mantener la privada cuando firmes.

import 'package:encrypt/encrypt.dart';

var ClavePrivada = """-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----""";
var ClavePublica = """-----BEGIN PUBLIC KEY-----
MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAoVc7pmMUBtF9q2WURsBs
KK+KhrTPj6q3F5w3IMSPQ6b2SLuloHSm/FaLDMDIXEuvnIaNAzZKNoxeL/2iKmMa
rmWtBeREph15tH8/THRYzx+JhhAS6A/V3lhrEDDpTADMAvPLTjDffNz/rfZq5gLS
46q7sG4uwgOopHxl/mv551a0GulLdKXO8Sv1HNZr1ulijYVZ1dIwlXAY+3s5NpTQ
pzczmYnHaT+zOZ4Od6MnG5vdJXXj1PwfrzAUBv/qzFU8FzJ11O0RLZtb94YJAm+F
c3J/Z/JhzWyRwfIiUggqDJepgQ4nzsOoaj8ignXXhKhF7KcRkxS/eI+aH9RzXwvV
SFd82Dz63J6rnGIbyp0pJQVbw+f1E3uP32QvFdZqpJWQBYwK6OZwTcpznri4c6PO
jHlN2OQqG+IIvkCMLsFX6OGD+t/bvfn8yAyuPFY93xYoRkQ2dy8hJeGJNa09i9GP
liY5YUskF4rhZ7yFqT2OxhJaSAHfha7cAdh2D04rlvtbAgMBAAE=
-----END PUBLIC KEY-----""";

void main(List<String> argumentos) async {
  var mensaje = "Hola mundo desde parzibyte.me";
  var firmado = firmar(ClavePrivada, mensaje);
  bool coincide = verificarFirma(firmado, mensaje, ClavePublica);
  print("Firmado: $firmado");
  print("¿El mensaje firmado es auténtico?: $coincide");
}

bool verificarFirma(
    String firmado, String plano, String clavePublicaComoCadena) {
  final clavePublica = RSAKeyParser().parse(clavePublicaComoCadena);
  final firmador =
      Signer(RSASigner(RSASignDigest.SHA256, publicKey: clavePublica));
  var coinciden = firmador.verify64(plano, firmado);
  return coinciden;
}

String firmar(String clavePrivadaParaFirmarComoCadena, String mensaje) {
  final clavePrivada = RSAKeyParser().parse(clavePrivadaParaFirmarComoCadena);
  final firmador =
      Signer(RSASigner(RSASignDigest.SHA256, privateKey: clavePrivada));
  String mensajeFirmado = firmador.sign(mensaje).base64;
  return mensajeFirmado;
}

El ejemplo de código está en el método main en la línea 54. Obviamente tú puedes mejorar y separar estos métodos que ves y usarlos como prefieras.

Nota: yo he ejecutado el programa con dart run firmar.dart. También se puede compilar y todo eso.

Te repito que esto se puede integrar directamente a Flutter y usarlo en otras plataformas. Así podrás verificar firmas digitales con Dart en varios lugares.

Esto de la firma digital con RSA también lo he hecho en Golang. Y antes de que lo olvide: nunca compartas tu clave privada. Yo lo hago porque estoy ejemplificando y no la usaré en otro lugar.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

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