Verifying an OpenSSL signature

Hi there!
I have a very specific problem and I haven't been able to make it work, thus I'm here. I want to port the following NodeJS code to Rust:

const crypto = require('crypto');

const payload = "{\"ziplist\":{\"reqid\":\"CreatorId_RequestNumber\",\"reqtime\":\"2015-02-22T12:20:55.123Z\",\"name\":\"FileName.zip\",\"files\":[{\"url\":\"https://media3.gaggenau.com/Images/600x/MCIM03407410_gaggenau-ovens-landing-400-series-teaser.jpg\",\"path\":\"Directory1/Backofen.jpg\"}]}}";
const signature = "5ae0221981e1a3e66c5c7b990dd66c823d1c879fe8feb667c292c103115fa651c9f8496d8c5132e47527b91efc1a4fa1e7d0010326529db09197a881913d4fc3e5e04ac079a353e81e60ceda6fe76df11a5e7035a9da3a030a99ef5a8d96d0e2381634006c64713fa488d232f2b5d910a1a68b38e4affac90e6500ff2261a501aa0c8b460d817f76370abaeed36f4531bf4358d604f9f9738f987f3c9b858e8526cd4604840e7e1f73ebb4cfee412ec952ae7a48ed904d72302f360836a42ae47e43c27ade88871605b52acd9cbe02154d523ef64252e34d1e9798fe4cd452cf88442cdcebf74954b001ebeb83df28caab34583e543cc33729e3b4193c0e1542";
	    
const verifier = crypto.createVerify('RSA-SHA512');
const public_key = fs.readFileSync('./public.pem', 'utf8');

verifier.update(payload, 'utf8');
console.log(verifier.verify(public_key, signature, 'hex'));

As you can see, I need to verify the validity of a signed JSON string using a public key (PKCS#8 RSA) and SHA512 as digest function. The formats of everything are non-negotiable.

I've looked at several crates decided to go with rsa and landed on the following:

use rsa::{pkcs8::DecodePublicKey, sha2::{Digest, Sha512}, PublicKey, Pkcs1v15Sign};

fn main() {
    let payload = "{\"ziplist\":{\"reqid\":\"CreatorId_RequestNumber\",\"reqtime\":\"2015-02-22T12:20:55.123Z\",\"name\":\"FileName.zip\",\"files\":[{\"url\":\"https://media3.gaggenau.com/Images/600x/MCIM03407410_gaggenau-ovens-landing-400-series-teaser.jpg\",\"path\":\"Directory1/Backofen.jpg\"}]}}";
    let signature = "5ae0221981e1a3e66c5c7b990dd66c823d1c879fe8feb667c292c103115fa651c9f8496d8c5132e47527b91efc1a4fa1e7d0010326529db09197a881913d4fc3e5e04ac079a353e81e60ceda6fe76df11a5e7035a9da3a030a99ef5a8d96d0e2381634006c64713fa488d232f2b5d910a1a68b38e4affac90e6500ff2261a501aa0c8b460d817f76370abaeed36f4531bf4358d604f9f9738f987f3c9b858e8526cd4604840e7e1f73ebb4cfee412ec952ae7a48ed904d72302f360836a42ae47e43c27ade88871605b52acd9cbe02154d523ef64252e34d1e9798fe4cd452cf88442cdcebf74954b001ebeb83df28caab34583e543cc33729e3b4193c0e1542";

    let scheme = Pkcs1v15Sign::new::<Sha512>();
    let hash = Sha512::digest(payload);
    let key = rsa::RsaPublicKey::read_public_key_pem_file("./public.pem").unwrap();
    println!("{}", key.verify(scheme, &hash, signature.as_bytes()).is_ok());
}

Unfortunately the verification is not successful. I noted that the NodeJS algorithm takes in the message as utf-8 encoded string whereas rsa requires me to provide a hash thereof as bytes. Also NodeJS takes the signature as utf-8 encoded hex string whereas rsa just takes bytes. I'm assuming there's a screwup somewhere in the encoding or formatting but I'm not sure where to start looking.

Any help would be greatly appreciated! Also, I'm not married to this particular crate so if anyone can suggest a different one that solves my issue, I'd be just as thankful.

Just a wild guess, but should signature be this:

let signature = &[0x5a,0xe0,0x22,0x19,0x81,0xe1,0xa3,0xe6,0x6c,0x5c,0x7b,0x99,0x0d,0xd6,0x6c,0x82,0x3d,0x1c,0x87,0x9f,0xe8,0xfe,0xb6,0x67,0xc2,0x92,0xc1,0x03,0x11,0x5f,0xa6,0x51,0xc9,0xf8,0x49,0x6d,0x8c,0x51,0x32,0xe4,0x75,0x27,0xb9,0x1e,0xfc,0x1a,0x4f,0xa1,0xe7,0xd0,0x01,0x03,0x26,0x52,0x9d,0xb0,0x91,0x97,0xa8,0x81,0x91,0x3d,0x4f,0xc3,0xe5,0xe0,0x4a,0xc0,0x79,0xa3,0x53,0xe8,0x1e,0x60,0xce,0xda,0x6f,0xe7,0x6d,0xf1,0x1a,0x5e,0x70,0x35,0xa9,0xda,0x3a,0x03,0x0a,0x99,0xef,0x5a,0x8d,0x96,0xd0,0xe2,0x38,0x16,0x34,0x00,0x6c,0x64,0x71,0x3f,0xa4,0x88,0xd2,0x32,0xf2,0xb5,0xd9,0x10,0xa1,0xa6,0x8b,0x38,0xe4,0xaf,0xfa,0xc9,0x0e,0x65,0x00,0xff,0x22,0x61,0xa5,0x01,0xaa,0x0c,0x8b,0x46,0x0d,0x81,0x7f,0x76,0x37,0x0a,0xba,0xee,0xd3,0x6f,0x45,0x31,0xbf,0x43,0x58,0xd6,0x04,0xf9,0xf9,0x73,0x8f,0x98,0x7f,0x3c,0x9b,0x85,0x8e,0x85,0x26,0xcd,0x46,0x04,0x84,0x0e,0x7e,0x1f,0x73,0xeb,0xb4,0xcf,0xee,0x41,0x2e,0xc9,0x52,0xae,0x7a,0x48,0xed,0x90,0x4d,0x72,0x30,0x2f,0x36,0x08,0x36,0xa4,0x2a,0xe4,0x7e,0x43,0xc2,0x7a,0xde,0x88,0x87,0x16,0x05,0xb5,0x2a,0xcd,0x9c,0xbe,0x02,0x15,0x4d,0x52,0x3e,0xf6,0x42,0x52,0xe3,0x4d,0x1e,0x97,0x98,0xfe,0x4c,0xd4,0x52,0xcf,0x88,0x44,0x2c,0xdc,0xeb,0xf7,0x49,0x54,0xb0,0x01,0xeb,0xeb,0x83,0xdf,0x28,0xca,0xab,0x34,0x58,0x3e,0x54,0x3c,0xc3,0x37,0x29,0xe3,0xb4,0x19,0x3c,0x0e,0x15,0x42];

because str::as_bytes gets you the right type, but it's actually converting each digit into its byte value.

1 Like

Wow, that's exactly right. Thanks so much, you just saved my week!

Do you happen to know if there's a way to have Rust do this conversion automatically? For testing I did that manually in my editor.

Nevermind, this is a simple task using the hex crate. Thanks again!

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.