Reputation: 49
I am new to Rust and I trying to extract RSA public key from a X509 Certificate. Basically, I have a cert string <-----BEGIN CERTIFICATE-----MII......-----END CERTIFICATE----->. I want to extract - <----BEGIN RSA PUBLIC KEY----MIII......----END RSA PUBLIC KEY> from the cert string.
I tried using the pem crate and x509_certificate crate but the key is coming out in an invalid format.
let mut certStr: String = "-----BEGIN CERTIFICATE-----\n".to_owned();
certStr.push_str(&test.certificate);
certStr.push_str("\n-----END CERTIFICATE-----");
let rsa_cert = &X509Certificate::from_pem(certStr.as_bytes()).unwrap();
let pKey = &X509Certificate::public_key_data(&rsa_cert); // Invalid format Key
let pem = parse(&certStr).unwrap(); // This has 2 properties -> tag and contents. Not sure how to extract p
--Edit-- Thanks everyone for your help. I am trying to avoid taking dependency on openssl if possible. All I need is to get the public key out of the certificate to validate a JWT token which is encrypted by a corresponding private key. What would be the secure and straightforward way of doing that in Rust?
Upvotes: 4
Views: 2520
Reputation: 49276
In the code the wrong method is used when exporting the key: The key returned by public_key_data()
is a DER encoded public key in PKCS#1 format. To convert this to a PEM encoded key, the encode()
method must be applied instead of the parse()
method:
use x509_certificate::certificate::X509Certificate;
use pem::{Pem, encode};
use base64::{engine::general_purpose, Engine as _};
fn main() {
let cert_b64_der = "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";
// Convert certificate to PEM, import PEM certifciate
let mut cert_pem: String = "-----BEGIN CERTIFICATE-----\n".to_owned();
cert_pem.push_str(&cert_b64_der);
cert_pem.push_str("\n-----END CERTIFICATE-----");
let cert = &X509Certificate::from_pem(cert_pem.as_bytes()).unwrap();
// Pick public key
let public_key = &X509Certificate::public_key_data(&cert);
let public_pkcs1_der = general_purpose::STANDARD.encode(&public_key);
// Export public key as PEM encoded public key in PKCS#1 format
let pem = Pem {
tag: String::from("RSA PUBLIC KEY"),
contents: public_key.to_vec(),
};
let public_pkcs1_pem = encode(&pem);
println!("Base64 encoded DER encoded public PKCS#1 key:\n{}", public_pkcs1_der);
println!();
println!("PEM encoded public PKCS#1 key:\n{}", public_pkcs1_pem);
}
The output of this code is:
Base64 encoded DER encoded public PKCS#1 key:
MIICCgKCAgEAvWYJ7W5XBA5lwDCbgQX2kbZZaHSuMD+50ln7lRhuhu5Zzn7C1Iz3SmntF7vzrrtYHt8v5Cr7S4uh8GWwLr5k3Bq9xowHKnMM91DFUVCW1U0x2JavRtU79QLzLZDdh/bVcv0VSA3ZiM2yPgwY2HzJmgiLu8OZB40Lk80mf3VPfdBMkD35IlQzJC1/KV8fVP+eWOXPq8fTX/w3mNpANVnIAYGe7WsoygUMXuivi9DXAgUzDK5y5gdOmhta9EK+4wMrdFF7tk7vEL8r/cy+6rA/mRHNKRhxRKzoRQZfchpPvFk1+hJrt214/78Uf65E3NJlZKXWl2JwpmOmqpnUrGiGnotqzNX/RHgZVqwUgZqHuHS+m6wivfDMxjJg1861xOHGFMk2wpJhWReaQxPAwyatJrbCo7BsgXLlGGa4wdLq2/IMBONCw7W6xfC4QzW3pmCV5E0z6PGT1+s4YAE32qKjbCO6Xff5DOmKp8qQKykM++Rd7MHTgOiYdoWt4X+Us2YJMWZ1YG1coYapFVLW9TuO3R+Mn0MxuwomqChd3bsvEtTxG/cI06uBLrz5DthwtbE35NlDKJhoeDnMHRc1Z+knrCFFIM4U3GSS/CxfugMD7kgb1vvrMnquP23EEFtWfZndSsKkCh6VyqOTLYEIYEKeE+MEkWL3AXFUvinfb1gpBD8CAwEAAQ==
PEM encoded public PKCS#1 key:
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEAvWYJ7W5XBA5lwDCbgQX2kbZZaHSuMD+50ln7lRhuhu5Zzn7C1Iz3
SmntF7vzrrtYHt8v5Cr7S4uh8GWwLr5k3Bq9xowHKnMM91DFUVCW1U0x2JavRtU7
9QLzLZDdh/bVcv0VSA3ZiM2yPgwY2HzJmgiLu8OZB40Lk80mf3VPfdBMkD35IlQz
JC1/KV8fVP+eWOXPq8fTX/w3mNpANVnIAYGe7WsoygUMXuivi9DXAgUzDK5y5gdO
mhta9EK+4wMrdFF7tk7vEL8r/cy+6rA/mRHNKRhxRKzoRQZfchpPvFk1+hJrt214
/78Uf65E3NJlZKXWl2JwpmOmqpnUrGiGnotqzNX/RHgZVqwUgZqHuHS+m6wivfDM
xjJg1861xOHGFMk2wpJhWReaQxPAwyatJrbCo7BsgXLlGGa4wdLq2/IMBONCw7W6
xfC4QzW3pmCV5E0z6PGT1+s4YAE32qKjbCO6Xff5DOmKp8qQKykM++Rd7MHTgOiY
doWt4X+Us2YJMWZ1YG1coYapFVLW9TuO3R+Mn0MxuwomqChd3bsvEtTxG/cI06uB
Lrz5DthwtbE35NlDKJhoeDnMHRc1Z+knrCFFIM4U3GSS/CxfugMD7kgb1vvrMnqu
P23EEFtWfZndSsKkCh6VyqOTLYEIYEKeE+MEkWL3AXFUvinfb1gpBD8CAwEAAQ==
-----END RSA PUBLIC KEY-----
That the exported public key really corresponds to the key from the certificate and that the key is correctly formatted can be checked most easily with an ASN.1 parser, e.g. https://lapo.it/asn1js/.
As it seems that your certificate is available as a Base64 encoded ASN.1/DER encoded certificate, it is probably more convenient to import this directly as DER using from_der()
and spare the detour via the PEM encoding. For this, simply replace the Convert certificate to PEM
code block with:
// Convert certificate to DER, import DER certificate
let cert_der = general_purpose::STANDARD.decode(&cert_b64_der).unwrap();
let cert = &X509Certificate::from_der(cert_der.as_slice()).unwrap();
You can alternatively use the openssl crate that supports all the tasks you need in a single crate: The import of the PEM certificate, the extraction of the public key and the export as PEM encoded public key in PKCS#1 format (as well as in various other formats and encodings):
use openssl::x509::X509;
use std::str;
fn main() {
let cert_b64_der = "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";
// Convert certificate to PEM, import PEM certificate
let mut cert_pem: String = "-----BEGIN CERTIFICATE-----\n".to_owned();
cert_pem.push_str(&cert_b64_der);
cert_pem.push_str("\n-----END CERTIFICATE-----");
let cert = &X509::from_pem(cert_pem.as_bytes()).unwrap();
// Pick public key
let public_pkey = cert.public_key().unwrap();
// Export public key as PEM encoded public key in PKCS#1 format
let public_pkcs1_pem = public_pkey.rsa().unwrap().public_key_to_pem_pkcs1().unwrap();
println!("PEM encoded public PKCS#1 key:\n{}", str::from_utf8(public_pkcs1_pem.as_slice()).unwrap());
}
Also the OpenSSL library allows to import the ASN.1/DER encoded certificate directly (here) and to export the key DER encoded (here).
Upvotes: 3