Una pasarela de pago avisa un pago con un POST cuyo cuerpo viene firmado con RSA; la firma llega en base64 en un header. Con HMAC no alcanza porque el proveedor no comparte un secreto: publica su CLAVE PÚBLICA (PEM) y uno verifica con ella. std/rsa lo hace con OpenSSL.
// Nyx by Example: verificar el aviso firmado de una pasarela de pago (RSA-SHA256). // // El caso real: la pasarela avisa un pago con un POST cuyo cuerpo viene firmado // con RSA; la firma llega en base64 en un header. Con HMAC no alcanza porque el // proveedor no comparte un secreto: publica su CLAVE PÚBLICA (PEM) y uno // verifica con ella. std/rsa lo hace con OpenSSL: // - rsa_sha256_verify_b64(pem, cuerpo, firma_base64): PKCS#1 v1.5, lo común; // - rsa_sha256_verify(pem, cuerpo, firma_cruda): la misma firma en bytes; // - rsa_pss_sha256_verify(pem, cuerpo, firma_cruda): RSASSA-PSS. // Ok(true) = firma válida; Ok(false) = el aviso NO es del proveedor (datos // alterados o clave ajena); Err = no se pudo ni intentar (PEM ilegible). // Se verifica el cuerpo EXACTO recibido, byte a byte, antes de parsearlo. // // Los datos de la receta son los fixtures de prueba del repo (openssl CLI). En un // programa real: la clave con read_file("proveedor.pem") y el cuerpo y la firma // de la petición entrante. import "std/rsa" import "std/error" fn aviso_valido(pem: String, cuerpo: String, firma_b64: String) -> bool { match rsa_sha256_verify_b64(pem, cuerpo, firma_b64) { Result.Ok(ok) => { return ok } Result.Err(e) => { print("no se pudo verificar: " + e.msg) return false } } } fn main() { let pem: String = include_bytes("tests/fixtures/rsa/publica.pem") let cuerpo: String = include_bytes("tests/fixtures/rsa/datos.txt") let firma: String = include_bytes("tests/fixtures/rsa/firma.b64") assert(aviso_valido(pem, cuerpo, firma), "el aviso genuino verifica") print("aviso genuino: aceptado") // Un atacante cambia el monto: la firma ya no corresponde. assert(aviso_valido(pem, cuerpo + " ", firma) == false, "un cuerpo alterado se rechaza") print("cuerpo alterado: rechazado") // Una clave que no se puede leer se dice, no se toma por «firma falsa». assert(aviso_valido("no soy un pem", cuerpo, firma) == false, "PEM ilegible") }
aviso genuino: aceptado cuerpo alterado: rechazado no se pudo verificar: rsa: la clave pública PEM no se pudo leer o no es RSA
Cómo funciona
Tres puntos de entrada: rsa_sha256_verify_b64(pem, cuerpo, firma_base64) para PKCS#1 v1.5, lo común; rsa_sha256_verify(pem, cuerpo, firma_cruda) para la misma firma en bytes; y rsa_pss_sha256_verify(pem, cuerpo, firma_cruda) para RSASSA-PSS. La respuesta tiene tres desenlaces: Ok(true), firma válida; Ok(false), el aviso NO es del proveedor (datos alterados o clave ajena); y Err, ni siquiera se pudo intentar, como con un PEM ilegible — las tres líneas de la salida.
Se verifica el cuerpo EXACTO recibido, byte a byte, ANTES de parsearlo. Los datos de la receta son los fixtures de prueba del repositorio, hechos con la CLI de openssl; en un programa real la clave sale de read_file("proveedor.pem") y el cuerpo y la firma, de la petición entrante.