Fix parsing PKCS8 public keys

This commit is contained in:
asonix 2020-03-16 20:26:54 -05:00
parent 6c47c3fc37
commit 8dc04bd060
5 changed files with 111 additions and 16 deletions

View file

@ -10,14 +10,18 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bit-vec = "0.6"
log = "0.4"
num-bigint = "0.2"
num-bigint-dig = "0.6"
num-traits = "0.2"
pem = "0.7"
rsa = "0.2.0"
thiserror = "1.0.9"
yasna = { version = "0.3", features = ["num-bigint"] }
yasna = { version = "0.3", features = ["num-bigint", "bit-vec"] }
[dev-dependencies]
pretty_env_logger = "0.4"
anyhow = "1.0"
rand = "0.7"
sha2 = "0.8"

20
examples/parse.rs Normal file
View 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(())
}

View file

@ -59,53 +59,72 @@ fn sign_from_dig(sign: Sign) -> num_bigint::Sign {
#[cfg(test)]
mod tests {
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]
fn priv_can_complete_cycle_pkcs1() {
let mut rng = rand::thread_rng();
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 res = RSAPrivateKey::from_pem_pkcs1(&string);
res.unwrap();
let pubkey = res.unwrap().to_public_key();
pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap();
}
#[test]
fn pub_can_complete_cycle_pkcs1() {
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 res = RSAPublicKey::from_pem_pkcs1(&string);
res.unwrap();
let pubkey = res.unwrap();
pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap();
}
#[test]
fn priv_can_complete_cycle_pkcs8() {
let mut rng = rand::thread_rng();
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 res = RSAPrivateKey::from_pem_pkcs8(&string);
res.unwrap();
let pubkey = res.unwrap().to_public_key();
pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap();
}
#[test]
fn pub_can_complete_cycle_pkcs8() {
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 res = RSAPublicKey::from_pem_pkcs8(&string);
res.unwrap();
let pubkey = res.unwrap();
pubkey.verify(PADDING, HASH, &hashed, &sig).unwrap();
}
}

View file

@ -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_traits::identities::One;
use rsa::{PublicKey, RSAPrivateKey};
@ -39,6 +71,8 @@ impl KeyExt for RSAPrivateKey {
let pkey = yasna::parse_der(&data.contents, |reader| {
// Read the outer DER-encoded value
reader.read_sequence(|reader| {
// Read Version ::= INTEGER
debug!("Parsing algorithm ID");
let version = reader.next().read_i64()?;
// PKCS#8 defines version == 0
@ -46,6 +80,8 @@ impl KeyExt for RSAPrivateKey {
return Err(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid));
}
// Read AlgorithmIdentifier ::= AlgorithmIdentifier
debug!("Parsing oid");
let oid = reader
.next()
.read_sequence(|reader| reader.next().read_oid())?;
@ -55,8 +91,12 @@ impl KeyExt for RSAPrivateKey {
return Err(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid));
}
// Read PrivateKey ::= OCTET STRING
debug!("Parsing bytes");
let bytes = reader.next().read_bytes()?;
// TODO: Read Attributes ::= SET OF Attribute
parse_pkcs1(&bytes)
})
})?;

View file

@ -1,3 +1,4 @@
use log::debug;
use rsa::{PublicKey, RSAPublicKey};
use super::*;
@ -11,8 +12,11 @@ impl KeyExt for RSAPublicKey {
writer.write_sequence(|writer| {
writer.next().write_sequence(|writer| {
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 pkey = yasna::parse_der(&data.contents, |reader| {
reader.read_sequence(|reader| {
let oid = reader
.next()
.read_sequence(|reader| reader.next().read_oid())?;
let o = reader.read_sequence(|reader| {
debug!("Parse OID");
let oid = reader.next().read_sequence(|reader| {
// TODO: parse more in here
let oid = reader.next().read_oid()?;
reader.next().read_null()?;
Ok(oid)
})?;
if oid != expected_oid {
debug!("OID was unexpected, {}", oid);
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)