Fix parsing PKCS8 public keys
This commit is contained in:
parent
6c47c3fc37
commit
8dc04bd060
|
@ -10,14 +10,18 @@ edition = "2018"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bit-vec = "0.6"
|
||||||
|
log = "0.4"
|
||||||
num-bigint = "0.2"
|
num-bigint = "0.2"
|
||||||
num-bigint-dig = "0.6"
|
num-bigint-dig = "0.6"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
pem = "0.7"
|
pem = "0.7"
|
||||||
rsa = "0.2.0"
|
rsa = "0.2.0"
|
||||||
thiserror = "1.0.9"
|
thiserror = "1.0.9"
|
||||||
yasna = { version = "0.3", features = ["num-bigint"] }
|
yasna = { version = "0.3", features = ["num-bigint", "bit-vec"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
pretty_env_logger = "0.4"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
|
sha2 = "0.8"
|
||||||
|
|
20
examples/parse.rs
Normal file
20
examples/parse.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use anyhow::Error;
|
||||||
|
use rsa::RSAPublicKey;
|
||||||
|
use rsa_pem::KeyExt;
|
||||||
|
|
||||||
|
static ASONIX_PEM: &str = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwEvEsHqM3twoC2F3KYMQ\n9YOialfVQX4StkLvhLUwFv8qpmY7ZSHHl2TWpnzlo5iWS5Pi2vC41HUGYz9XT5G7\n4IUOyuIkjTL1FIcPJDcUFCzQjN3QZcHLPJPJVNOOOEiOk8//paOyrqJTq9ikcJDM\nJ8KTWQgk1leOxUVEN5uaQ+p9IBFbXC76+RqabfEoqLZagVMDSOfeC2uR9xZ1q5Hk\nFveRTGs84QLR7FJVvx078nszx4UQGnmP0M+0sOeRJGK17IoJmhaok1XBpP6XFQ45\nvYeIRiaFj0Pc9GNISCW70dVXKMhv+K07orQJm6PwP8USyhq4tLkq6tcPbGRqEk3Z\nXwIDAQAB\n-----END PUBLIC KEY-----\n";
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, thiserror::Error)]
|
||||||
|
#[error("Error generating key")]
|
||||||
|
pub struct MyError;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Error> {
|
||||||
|
std::env::set_var("RUST_LOG", "debug");
|
||||||
|
pretty_env_logger::init();
|
||||||
|
log::info!("Parsing:\n{}", ASONIX_PEM);
|
||||||
|
RSAPublicKey::from_pem_pkcs8(ASONIX_PEM).map_err(|e| {
|
||||||
|
eprintln!("Error, {}, {:?}", e, e);
|
||||||
|
MyError
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
33
src/lib.rs
33
src/lib.rs
|
@ -59,53 +59,72 @@ fn sign_from_dig(sign: Sign) -> num_bigint::Sign {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::KeyExt;
|
use crate::KeyExt;
|
||||||
use rsa::{RSAPrivateKey, RSAPublicKey};
|
use rsa::{hash::Hashes, padding::PaddingScheme, PublicKey, RSAPrivateKey, RSAPublicKey};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
|
static SIGNING_STRING: &[u8] = b"Hewwo, Mr Obama";
|
||||||
|
static HASH: Option<&Hashes> = Some(&Hashes::SHA2_256);
|
||||||
|
static PADDING: PaddingScheme = PaddingScheme::PKCS1v15;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn priv_can_complete_cycle_pkcs1() {
|
fn priv_can_complete_cycle_pkcs1() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
||||||
|
let hashed = Sha256::digest(SIGNING_STRING);
|
||||||
|
let sig = rsa.sign(PADDING, HASH, &hashed).unwrap();
|
||||||
|
|
||||||
let string = rsa.to_pem_pkcs1().unwrap();
|
let string = rsa.to_pem_pkcs1().unwrap();
|
||||||
|
|
||||||
let res = RSAPrivateKey::from_pem_pkcs1(&string);
|
let res = RSAPrivateKey::from_pem_pkcs1(&string);
|
||||||
|
|
||||||
res.unwrap();
|
let pubkey = res.unwrap().to_public_key();
|
||||||
|
pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pub_can_complete_cycle_pkcs1() {
|
fn pub_can_complete_cycle_pkcs1() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap().to_public_key();
|
let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
||||||
|
let hashed = Sha256::digest(SIGNING_STRING);
|
||||||
|
let sig = rsa.sign(PADDING, HASH, &hashed).unwrap();
|
||||||
|
|
||||||
|
let rsa = rsa.to_public_key();
|
||||||
let string = rsa.to_pem_pkcs1().unwrap();
|
let string = rsa.to_pem_pkcs1().unwrap();
|
||||||
|
|
||||||
let res = RSAPublicKey::from_pem_pkcs1(&string);
|
let res = RSAPublicKey::from_pem_pkcs1(&string);
|
||||||
|
|
||||||
res.unwrap();
|
let pubkey = res.unwrap();
|
||||||
|
pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn priv_can_complete_cycle_pkcs8() {
|
fn priv_can_complete_cycle_pkcs8() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
||||||
|
let hashed = Sha256::digest(SIGNING_STRING);
|
||||||
|
let sig = rsa.sign(PADDING, HASH, &hashed).unwrap();
|
||||||
|
|
||||||
let string = rsa.to_pem_pkcs8().unwrap();
|
let string = rsa.to_pem_pkcs8().unwrap();
|
||||||
|
|
||||||
let res = RSAPrivateKey::from_pem_pkcs8(&string);
|
let res = RSAPrivateKey::from_pem_pkcs8(&string);
|
||||||
|
|
||||||
res.unwrap();
|
let pubkey = res.unwrap().to_public_key();
|
||||||
|
pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pub_can_complete_cycle_pkcs8() {
|
fn pub_can_complete_cycle_pkcs8() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap().to_public_key();
|
let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
||||||
|
let hashed = Sha256::digest(SIGNING_STRING);
|
||||||
|
let sig = rsa.sign(PADDING, HASH, &hashed).unwrap();
|
||||||
|
|
||||||
|
let rsa = rsa.to_public_key();
|
||||||
let string = rsa.to_pem_pkcs8().unwrap();
|
let string = rsa.to_pem_pkcs8().unwrap();
|
||||||
|
|
||||||
let res = RSAPublicKey::from_pem_pkcs8(&string);
|
let res = RSAPublicKey::from_pem_pkcs8(&string);
|
||||||
|
|
||||||
res.unwrap();
|
let pubkey = res.unwrap();
|
||||||
|
pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,35 @@
|
||||||
|
//! ```ignore
|
||||||
|
//! // Private-key information shall have ASN.1 type PrivateKeyInfo
|
||||||
|
//! PrivateKeyInfo ::= SEQUENCE {
|
||||||
|
//! version Version,
|
||||||
|
//! privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
|
||||||
|
//! privateKey PrivateKey,
|
||||||
|
//! attributes [0] IMPLICIT Attributes OPTIONAL
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // version is the syntax version number, for compatibility with future revisions of the spec.
|
||||||
|
//! // It shall be 0 for this version.
|
||||||
|
//! Version ::= INTEGER
|
||||||
|
//!
|
||||||
|
//! // privateKeyAlgorithm identifies the private-key algorithm. One example of a private-key
|
||||||
|
//! // algorithm is PKCS #1's rsaEncryption [PKCS#1]
|
||||||
|
//! //
|
||||||
|
//! // A type that identifies an algorithm (by object identifier) and any associated parameters.
|
||||||
|
//! // This type is defined in [X.509](https://tools.ietf.org/html/rfc5208#ref-X.509)
|
||||||
|
//! PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
|
||||||
|
//!
|
||||||
|
//! // privateKey is an octet string whose contents are the value of the private key. The
|
||||||
|
//! // interpretation of the contents is defined in the registration of the private-key algorithm.
|
||||||
|
//! // For an RSA private key, for example, the ocntents are a BER encodeing of a value of type
|
||||||
|
//! // RSAPrivateKey.
|
||||||
|
//! PrivateKey ::= OCTET STRING
|
||||||
|
//!
|
||||||
|
//! // attributes is a set of attributes. These are the extended information that is encrypted
|
||||||
|
//! // along with the private-key information.
|
||||||
|
//! Attributes ::= SET OF Attribute
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use log::debug;
|
||||||
use num_bigint_dig::{traits::ModInverse, BigUint};
|
use num_bigint_dig::{traits::ModInverse, BigUint};
|
||||||
use num_traits::identities::One;
|
use num_traits::identities::One;
|
||||||
use rsa::{PublicKey, RSAPrivateKey};
|
use rsa::{PublicKey, RSAPrivateKey};
|
||||||
|
@ -39,6 +71,8 @@ impl KeyExt for RSAPrivateKey {
|
||||||
let pkey = yasna::parse_der(&data.contents, |reader| {
|
let pkey = yasna::parse_der(&data.contents, |reader| {
|
||||||
// Read the outer DER-encoded value
|
// Read the outer DER-encoded value
|
||||||
reader.read_sequence(|reader| {
|
reader.read_sequence(|reader| {
|
||||||
|
// Read Version ::= INTEGER
|
||||||
|
debug!("Parsing algorithm ID");
|
||||||
let version = reader.next().read_i64()?;
|
let version = reader.next().read_i64()?;
|
||||||
|
|
||||||
// PKCS#8 defines version == 0
|
// PKCS#8 defines version == 0
|
||||||
|
@ -46,6 +80,8 @@ impl KeyExt for RSAPrivateKey {
|
||||||
return Err(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid));
|
return Err(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read AlgorithmIdentifier ::= AlgorithmIdentifier
|
||||||
|
debug!("Parsing oid");
|
||||||
let oid = reader
|
let oid = reader
|
||||||
.next()
|
.next()
|
||||||
.read_sequence(|reader| reader.next().read_oid())?;
|
.read_sequence(|reader| reader.next().read_oid())?;
|
||||||
|
@ -55,8 +91,12 @@ impl KeyExt for RSAPrivateKey {
|
||||||
return Err(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid));
|
return Err(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read PrivateKey ::= OCTET STRING
|
||||||
|
debug!("Parsing bytes");
|
||||||
let bytes = reader.next().read_bytes()?;
|
let bytes = reader.next().read_bytes()?;
|
||||||
|
|
||||||
|
// TODO: Read Attributes ::= SET OF Attribute
|
||||||
|
|
||||||
parse_pkcs1(&bytes)
|
parse_pkcs1(&bytes)
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use log::debug;
|
||||||
use rsa::{PublicKey, RSAPublicKey};
|
use rsa::{PublicKey, RSAPublicKey};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -11,8 +12,11 @@ impl KeyExt for RSAPublicKey {
|
||||||
writer.write_sequence(|writer| {
|
writer.write_sequence(|writer| {
|
||||||
writer.next().write_sequence(|writer| {
|
writer.next().write_sequence(|writer| {
|
||||||
writer.next().write_oid(&oid);
|
writer.next().write_oid(&oid);
|
||||||
|
writer.next().write_null();
|
||||||
});
|
});
|
||||||
writer.next().write_bytes(&bytes);
|
writer
|
||||||
|
.next()
|
||||||
|
.write_bitvec(&bit_vec::BitVec::from_bytes(&bytes));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -34,19 +38,27 @@ impl KeyExt for RSAPublicKey {
|
||||||
let expected_oid = yasna::models::ObjectIdentifier::from_slice(&RSA_OID);
|
let expected_oid = yasna::models::ObjectIdentifier::from_slice(&RSA_OID);
|
||||||
|
|
||||||
let pkey = yasna::parse_der(&data.contents, |reader| {
|
let pkey = yasna::parse_der(&data.contents, |reader| {
|
||||||
reader.read_sequence(|reader| {
|
let o = reader.read_sequence(|reader| {
|
||||||
let oid = reader
|
debug!("Parse OID");
|
||||||
.next()
|
let oid = reader.next().read_sequence(|reader| {
|
||||||
.read_sequence(|reader| reader.next().read_oid())?;
|
// TODO: parse more in here
|
||||||
|
let oid = reader.next().read_oid()?;
|
||||||
|
reader.next().read_null()?;
|
||||||
|
Ok(oid)
|
||||||
|
})?;
|
||||||
|
|
||||||
if oid != expected_oid {
|
if oid != expected_oid {
|
||||||
|
debug!("OID was unexpected, {}", oid);
|
||||||
return Err(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid));
|
return Err(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid));
|
||||||
}
|
}
|
||||||
|
|
||||||
let bytes = reader.next().read_bytes()?;
|
debug!("Parse bytes");
|
||||||
|
let bitvec = reader.next().read_bitvec()?;
|
||||||
|
|
||||||
parse_pkcs1(&bytes)
|
parse_pkcs1(&bitvec.to_bytes())
|
||||||
})
|
})?;
|
||||||
|
debug!("Parsed sequence");
|
||||||
|
Ok(o)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(pkey)
|
Ok(pkey)
|
||||||
|
|
Loading…
Reference in a new issue