Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using ES256 with public key from PEM? #71

Closed
hobofan opened this issue Apr 23, 2017 · 5 comments
Closed

Using ES256 with public key from PEM? #71

hobofan opened this issue Apr 23, 2017 · 5 comments
Labels

Comments

@hobofan
Copy link
Contributor

hobofan commented Apr 23, 2017

I am trying to decode and verify a JWT that has been signed with ES256, where I have the public key in form of PEM.

With #53 still open, this seems to be outside of the currently supported scope, so I tried to get it to work with help of the pem crate. However if I try to decode the JWT I get Err(ValidationError(InvalidSignature)) as a result. The same testcase implemented in node.js correctly verifies the signature.

Does anybody know what I'm doing wrong? Any help is appreciated!

Testcase:

extern crate biscuit;
extern crate pem;
extern crate serde_json;

use biscuit::*;
use biscuit::jwa::*;
use biscuit::jws::*;
use pem::parse;
use serde_json::Value as Json;

fn main() {
    let public_key_pem = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENyf5aq1BaIfddcwuMzw9jgbc35aLYCRXlEmiuALvyJH2OMdRz2h+b/migOEbcDZYXmDKMrtGnD5XmYoonghgpg==\n-----END PUBLIC KEY-----";
    let jwt = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoic2VydmljZSIsImlhdCI6MTQ5MjkzODU4OH0.do_XppIOFthPWlTXL95CIBfgRdyAxbcIsUfM0YxMjCjqvp4ehHFA3I-JasABKzC8CAy4ndhCHsZdpAtKkqZMEA";

    let parsed_pem = parse(public_key_pem).unwrap();
    let signing_secret = Secret::PublicKey(parsed_pem.contents);

    let token = JWT::<Json, biscuit::Empty>::new_encoded(jwt);
    let decoded = token.into_decoded(&signing_secret, SignatureAlgorithm::ES256);

    println!("{:?}", decoded);
}
@lawliet89
Copy link
Owner

lawliet89 commented Apr 23, 2017

Your public key is in the SubjectPublicKeyInfo form, but ring (the underlying Crypto library we use) expects it in the SubjectPublicKey form.

If you notice, in the docs, I haven't documented a way to convert the public key for EC keys into this form using openssl because there is apparently no way to do so. See briansmith/ring#476 and https://unix.stackexchange.com/a/349682/189657

I'm waiting for briansmith/ring#378 to land to better support this.

@hobofan
Copy link
Contributor Author

hobofan commented Apr 23, 2017

Thanks, I didn't know that those two different forms existed. Those links were exactly what I needed!

I played around a bit, and got it working now. On the way to get there I wrote a DER parser with yasna to extract the public key which wasn't too hard (in case someone wants to build a more lightweight workaround). I ended up rebasing briansmith's modified version of the PR to master so I use the cargo replace feature. I also had to make a minor adjustment to biscuit to get it working (see #72).


Working example:

extern crate biscuit;
extern crate pem;
extern crate ring;
extern crate serde_json;
extern crate untrusted;

use biscuit::*;
use biscuit::jwa::*;
use biscuit::jws::*;
use pem::parse;
use ring::signature::spki::ECDSA_P256_SHA256;
use ring::signature::spki::parse_spki;
use serde_json::Value as Json;
use untrusted::Input;

fn main() {
    let public_key_pem = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENyf5aq1BaIfddcwuMzw9jgbc35aLYCRXlEmiuALvyJH2OMdRz2h+b/migOEbcDZYXmDKMrtGnD5XmYoonghgpg==\n-----END PUBLIC KEY-----";
    let jwt = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoic2VydmljZSIsImlhdCI6MTQ5MjkzODU4OH0.do_XppIOFthPWlTXL95CIBfgRdyAxbcIsUfM0YxMjCjqvp4ehHFA3I-JasABKzC8CAy4ndhCHsZdpAtKkqZMEA";

    let parsed_pem = parse_pem_ring(public_key_pem.as_bytes());
    let signing_secret = Secret::PublicKey(parsed_pem);

    let token = JWT::<Json, biscuit::Empty>::new_encoded(jwt);
    let decoded = token.into_decoded(&signing_secret, SignatureAlgorithm::ES256);

    println!("{:?}", decoded);
}

fn parse_pem_ring(in_pem: &[u8]) -> Vec<u8> {
    let pem_contents = parse(in_pem).unwrap().contents;
    let inp = Input::from(&pem_contents);
    let spki = parse_spki(&ECDSA_P256_SHA256, inp).unwrap();
    let public_key = spki.subject_public_key.as_slice_less_safe().to_vec();

    public_key
}

Inside the Cargo.toml:

[replace]
"ring:0.7.5" = { git = "https://github.com/hobofan/ring", branch = "rebased_378" }
"biscuit:0.0.2" = { git = "https://github.com/hobofan/biscuit", branch = "fix_ecdsa_verify" }

@lawliet89
Copy link
Owner

lawliet89 commented Apr 23, 2017

Thank you for digging around. I ran out of time the last time round when I was figuring this out and never got back to looking into it.

Would you mind sharing the tool you wrote with yasna? I might include it as an example for other users until ring merges in briansmith/ring#378. Or, at the least, I can direct them to your example.

@hobofan
Copy link
Contributor Author

hobofan commented Apr 23, 2017

It was just a small function, which I wrote after I saw that the ASN.1 structure of a SPKI is pretty simple with an online debugging tool. The ring PR does the same thing (while retaining the OID of the algo and with more safety), but with their own ASN.1 parser instead of yasna.

License for the code is MIT, to fit the repo, so feel free to do with it what you feel is best for other users. Thanks for all the work you have done with biscuit!

extern crate yasna;
extern crate pem;

use pem::parse;

fn parse_pem(in_pem: &[u8]) -> Vec<u8> {
    let pem_contents = parse(in_pem).unwrap().contents;
    let asn_content = yasna::parse_der(&pem_contents, |reader| {
        reader.read_sequence(|reader| {
                try!(reader.next().read_sequence(|info_reader| {
                    info_reader.next().read_oid().unwrap();
                    info_reader.next().read_oid().unwrap();

                    Ok(())
                }));
                let b = try!(reader.next().read_bitvec());
                return Ok(b);
            })
        }
    ).unwrap();

    let public_key = asn_content.to_bytes();

    public_key
}

@lawliet89
Copy link
Owner

Thanks. I've opened #73 to deal with this, in some form.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants