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
|
||||
|
||||
[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
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)]
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})?;
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue