Skip to content

Commit

Permalink
Decode PKCS#8 public keys
Browse files Browse the repository at this point in the history
  • Loading branch information
honzasp committed Aug 28, 2022
1 parent 369f897 commit f17b01a
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 2 deletions.
4 changes: 3 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,10 @@ pub enum Error {
Pkcs8(pkcs8::Error), // rustc mysteriously complains when we use `#[source]` here
#[error("could not parse file in PKCS#8 format (RSA)")]
Pkcs8Rsa(#[source] rsa::pkcs8::Error),
#[error("could not parse file in PKCS#8 format (spki): {0}")]
#[error("could not parse file in PKCS#8 format (SPKI): {0}")]
Pkcs8Spki(pkcs8::spki::Error), // rustc mysteriously complains when we use `#[source]` here
#[error("could not parse file in PKCS#8 format (RSA/SPKI)")]
Pkcs8RsaSpki(#[source] rsa::pkcs8::spki::Error),
#[error("could not parse file in PKCS#8 format (Ed25519)")]
Pkcs8Ed25519(#[source] ed25519_dalek::SignatureError),
#[error("unknown algorithm OID {0:?} in PKCS#8 file")]
Expand Down
1 change: 1 addition & 0 deletions src/keys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub use self::pkcs1::{
};
pub use self::pkcs8::{
decode_pkcs8_pem_privkey, decode_pkcs8_der_privkey, decode_pkcs8_encrypted_der_privkey,
decode_pkcs8_pem_pubkey, decode_pkcs8_der_pubkey,
};

mod openssh;
Expand Down
42 changes: 41 additions & 1 deletion src/keys/pkcs8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use ecdsa::elliptic_curve;
use ed25519_dalek::ed25519;
use pkcs8::AssociatedOid as _;
use crate::error::{Result, Error};
use crate::pubkey::Privkey;
use crate::pubkey::{Privkey, Pubkey};

/// Decode a private key from PKCS#8 PEM format.
///
Expand Down Expand Up @@ -67,3 +67,43 @@ pub fn decode_pkcs8_encrypted_der_privkey(der_data: &[u8], passphrase: &[u8]) ->
let document = info.decrypt(passphrase).map_err(Error::Pkcs8)?;
decode_pkcs8_der_privkey(document.as_bytes())
}

/// Decode a public key from PKCS#8 PEM format.
///
/// Files in this format start with `-----BEGIN PUBLIC KEY-----`.
pub fn decode_pkcs8_pem_pubkey(pem_data: &[u8]) -> Result<Pubkey> {
let data = super::decode_pem(pem_data, "PUBLIC KEY")?;
decode_pkcs8_der_pubkey(&data)
}

/// Decode a public key from PKCS#8 DER (binary) format.
pub fn decode_pkcs8_der_pubkey(der_data: &[u8]) -> Result<Pubkey> {
let info = pkcs8::SubjectPublicKeyInfo::try_from(der_data).map_err(Error::Pkcs8Spki)?;
let algo_oid = info.algorithm.oid;
if algo_oid == RSA_OID {
// unfortunately, `rsa` uses an older version of `pkcs8`, so we must re-parse the DER
let info = rsa::pkcs8::SubjectPublicKeyInfo::try_from(der_data).map_err(Error::Pkcs8RsaSpki)?;
let pubkey = rsa::RsaPublicKey::try_from(info).map_err(Error::Pkcs8RsaSpki)?;
Ok(Pubkey::Rsa(pubkey.into()))
} else if algo_oid == EC_OID {
let curve_oid = info.algorithm.parameters_oid().map_err(Error::Pkcs8Spki)?;
if curve_oid == p256::NistP256::OID {
let pubkey = elliptic_curve::PublicKey::<p256::NistP256>::try_from(info)
.map_err(Error::Pkcs8Spki)?;
Ok(Pubkey::EcdsaP256(pubkey.into()))
} else if curve_oid == p384::NistP384::OID {
let pubkey = elliptic_curve::PublicKey::<p384::NistP384>::try_from(info)
.map_err(Error::Pkcs8Spki)?;
Ok(Pubkey::EcdsaP384(pubkey.into()))
} else {
Err(Error::Pkcs8BadCurveOid(curve_oid.to_string()))
}
} else if algo_oid == ED25519_OID {
let public_bytes = ed25519::pkcs8::PublicKeyBytes::try_from(info).map_err(Error::Pkcs8Spki)?;
let pubkey = ed25519_dalek::PublicKey::from_bytes(&public_bytes.to_bytes())
.map_err(Error::Pkcs8Ed25519)?;
Ok(Pubkey::Ed25519(pubkey.into()))
} else {
Err(Error::Pkcs8BadAlgorithmOid(algo_oid.to_string()))
}
}
10 changes: 10 additions & 0 deletions tests/keys/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,20 +131,30 @@ fn check_pkcs8_privkey(expected_privkey: makiko::Privkey, pem_data: &str, passwo
assert_privkeys_eq!(&decoded_privkey, &expected_privkey);
}

fn check_pkcs8_pubkey(expected_privkey: makiko::Privkey, pem_data: &str) {
let decoded_pubkey = makiko::keys::decode_pkcs8_pem_pubkey(pem_data.as_bytes())
.expect("could not decode pubkey");
assert_eq!(decoded_pubkey, expected_privkey.pubkey());
}

#[test] fn test_decode_pkcs8_rsa() {
check_pkcs8_privkey(keys::pkcs8_rsa(), keys::PKCS8_RSA_PRIVKEY_FILE, "");
check_pkcs8_pubkey(keys::pkcs8_rsa(), keys::PKCS8_RSA_PUBKEY_FILE);
}

#[test] fn test_decode_pkcs8_ecdsa_p256() {
check_pkcs8_privkey(keys::pkcs8_ecdsa_p256(), keys::PKCS8_ECDSA_P256_PRIVKEY_FILE, "");
check_pkcs8_pubkey(keys::pkcs8_ecdsa_p256(), keys::PKCS8_ECDSA_P256_PUBKEY_FILE);
}

#[test] fn test_decode_pkcs8_ecdsa_p384() {
check_pkcs8_privkey(keys::pkcs8_ecdsa_p384(), keys::PKCS8_ECDSA_P384_PRIVKEY_FILE, "");
check_pkcs8_pubkey(keys::pkcs8_ecdsa_p384(), keys::PKCS8_ECDSA_P384_PUBKEY_FILE);
}

#[test] fn test_decode_pkcs8_ed25519() {
check_pkcs8_privkey(keys::pkcs8_ed25519(), keys::PKCS8_ED25519_PRIVKEY_FILE, "");
check_pkcs8_pubkey(keys::pkcs8_ed25519(), keys::PKCS8_ED25519_PUBKEY_FILE);
}

#[test] fn test_decode_pkcs8v2_ed25519() {
Expand Down

0 comments on commit f17b01a

Please sign in to comment.