Keats / jsonwebtoken

JWT lib in rust
MIT License
1.64k stars 260 forks source link

RSA256 signature verification OK from file KO from modulus and exponent #284

Closed JT117 closed 1 year ago

JT117 commented 1 year ago

I want to validate the RS256 signature of a JWT Token with the public key modulus and exponent. The signature verification always fails with InvalidSignature with the modulus and exponent, but is valid if I load the public key file.

Sample code to reproduce:

fn main() {
    let token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rlc3QiLCJhdWQiOiJodHRwczovL2F1ZGllbmNlIiwiaWF0IjoxNjczNTA5NzI3LCJleHAiOjE2NzM1OTYxMjcsInRlc3QiOiJ2YWx1ZSJ9.KmDgmNmk-lL7pf4BlazqN7X_sAaZCL634A8EmN1WhZPEzpqLRzbg1LUZ4bC-E0uBG22NxAyAN82CbJu2mdQrzC1avxNF8Y9mj3bWp73AlMyOm0d8dEPLlwFX71HEtH3A3OQ27kGup7-C1VNlAhXBUllD9Xv3vIwOUVzhuxPq3oj379HAdWD8-mJdDNcqQ8FNCTCIxpic1k7l6_L315pjepm4DA00WsgYPvkWJxitFuseIqVeLzinwseyosoXiIxbkLwxH_66na4jIbPt5MD7mcqbtDVPpaLlfYyA98la9tZ1LGkrwN2Q97j7eFQlRineYAXHI4VeWFtrDqAups_RSQ";

    let validation = Validation::new(Algorithm::RS256);

    let exponent ="65537".as_bytes();
    let modulus = "20259033849828414475893211043967070472894580853087712819747337741071632294805455887210161770150629024823148194329002783781333263988222597458376478071167863098265171744358502308283360588110824310602336303043505617535265410394998779186804880184203890831979487403611203350526447835702657730299871310918476298218088500602197454828600332208014487159347827463358076031422941996169484645244240612086684424616385431417912636954921975734118374466209861116801336798568016960128713065526964551786891429804794941505450027018264479476289937096394362264622164392707922168800662006708472649854924895811165634993361333830216945714863".as_bytes();

    let public_key_from_raw_component = DecodingKey::from_rsa_raw_components(&modulus, &exponent);
    let public_key_from_file = DecodingKey::from_rsa_pem(include_bytes!("../public_key.pem")).unwrap();

    dbg!(decode::<Claims>(token, &public_key_from_raw_component, &validation));
    dbg!(decode::<Claims>(token, &public_key_from_file, &validation));
}

The public key content:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoHuCj+5YZPDqqlusqzXC
o38AzfohcahURe03QFANLcoCIolraF3oFDZSsq5yUpr69dG7d4S9//Gle+nWTQwt
nfgUsjgvosz+QF2T77Vh+joFo/R9zR180W4yauRPwB954Q7UcGo+33Uxipzhfo4Z
xpQGjahzp1xsIoikLjLoCIPZNcTpSj1ulX1DWjSgjOZ8N9gCcUGshUnJplOef4Xi
gG4uNaxDuHdexsLo5thjSIf/I88anxL/3ZAQ0lvQHpCn6uMLz21V3hX0lfaHGdVV
6eTJ9qIoXNYyRruTTTAGQPvfnYBuKNcngUf9I8LYLnwKub2CjfE6BgNqsoo84PtG
rwIDAQAB
-----END PUBLIC KEY-----

The execution result :

[src\main.rs:20] decode::<Claims>(token, &public_key_from_raw_component, &validation) = Err(
    Error(
        InvalidSignature,
    ),
)
[src\main.rs:22] decode::<Claims>(token, &public_key_from_file, &validation) = Ok(
    TokenData {
        header: Header {
            typ: None,
            alg: RS256,
            cty: None,
            jku: None,
            jwk: None,
            kid: None,
            x5u: None,
            x5c: None,
            x5t: None,
            x5t_s256: None,
        },
        claims: Claims {
            aud: "https://audience",
            exp: 1673596127,
            iat: 1673509727,
            iss: "https://test",
        },
    },
)

In debug mode I can see the content of the DecodingKey structure is the same, but the kind differs. From file it is a DecodingKeyKind::SecretOrDer but from raw part it is DecodingKeyKind::RsaModulusExponent.

For what I can follow it all goes down to https://github.com/Keats/jsonwebtoken/blob/af684c146f288d993c632e15fb390f29ebe46683/src/crypto/mod.rs#L99-L103

The verify_ring seems to be fine but the rsa::verify_from_components return the InvalidSignature.

I will try to compare the code with the RFC to find out the bug but I don't know enough about RSA and signature verification. Any help finding/understanding the problem is appreciated 😄

JT117 commented 1 year ago

My bad, I fooled myself with the asbytes() method that return the char code and not the decimal value >< Using the crate num-bigint solves my issue.

let exponent = BigInt::parse_bytes( exponent.as_bytes(), 10).unwrap().to_bytes_be();
let modulus = BigInt::parse_bytes( modulus.as_bytes(), 10).unwrap().to_bytes_be();