Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read Public Keys from a pemfile #51

Merged
merged 2 commits into from
Aug 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rustls-pemfile"
version = "2.1.2"
version = "2.1.3"
edition = "2018"
license = "Apache-2.0 OR ISC OR MIT"
readme = "README.md"
Expand All @@ -11,7 +11,7 @@ categories = ["network-programming", "cryptography"]

djc marked this conversation as resolved.
Show resolved Hide resolved
[dependencies]
base64 = { version = "0.22", default-features = false, features = ["alloc"] }
pki-types = { package = "rustls-pki-types", version = "1.3" }
pki-types = { package = "rustls-pki-types", version = "1.7" }

[dev-dependencies]
bencher = "0.1.5"
Expand Down
23 changes: 21 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ use pki_types::PrivateKeyDer;
#[cfg(feature = "std")]
use pki_types::{
CertificateDer, CertificateRevocationListDer, CertificateSigningRequestDer, PrivatePkcs1KeyDer,
PrivatePkcs8KeyDer, PrivateSec1KeyDer,
PrivatePkcs8KeyDer, PrivateSec1KeyDer, SubjectPublicKeyInfoDer,
};

#[cfg(feature = "std")]
Expand Down Expand Up @@ -104,7 +104,10 @@ pub fn private_key(rd: &mut dyn io::BufRead) -> Result<Option<PrivateKeyDer<'sta
Item::Pkcs1Key(key) => return Ok(Some(key.into())),
Item::Pkcs8Key(key) => return Ok(Some(key.into())),
Item::Sec1Key(key) => return Ok(Some(key.into())),
Item::X509Certificate(_) | Item::Crl(_) | Item::Csr(_) => continue,
Item::X509Certificate(_)
| Item::SubjectPublicKeyInfo(_)
| Item::Crl(_)
| Item::Csr(_) => continue,
}
}

Expand All @@ -126,6 +129,7 @@ pub fn csr(
| Item::Pkcs8Key(_)
| Item::Sec1Key(_)
| Item::X509Certificate(_)
| Item::SubjectPublicKeyInfo(_)
| Item::Crl(_) => continue,
}
}
Expand Down Expand Up @@ -192,3 +196,18 @@ pub fn ec_private_keys(
_ => None,
})
}

/// Return an iterator over SPKI-encoded keys from `rd`.
///
/// Filters out any PEM sections that are not SPKI-encoded public keys and yields errors if a
/// problem occurs while trying to extract a SPKI-encoded public key.
#[cfg(feature = "std")]
pub fn public_keys(
rd: &mut dyn io::BufRead,
) -> impl Iterator<Item = Result<SubjectPublicKeyInfoDer<'static>, io::Error>> + '_ {
iter::from_fn(move || read_one(rd).transpose()).filter_map(|item| match item {
Ok(Item::SubjectPublicKeyInfo(key)) => Some(Ok(key)),
Err(err) => Some(Err(err)),
_ => None,
})
}
8 changes: 7 additions & 1 deletion src/pemfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::io::{self, ErrorKind};

use pki_types::{
CertificateDer, CertificateRevocationListDer, CertificateSigningRequestDer, PrivatePkcs1KeyDer,
PrivatePkcs8KeyDer, PrivateSec1KeyDer,
PrivatePkcs8KeyDer, PrivateSec1KeyDer, SubjectPublicKeyInfoDer,
};

/// The contents of a single recognised block in a PEM file.
Expand All @@ -22,6 +22,11 @@ pub enum Item {
/// Appears as "CERTIFICATE" in PEM files.
X509Certificate(CertificateDer<'static>),

/// A DER-encoded Subject Public Key Info; as specified in RFC 7468.
///
/// Appears as "PUBLIC KEY" in PEM files.
cpu marked this conversation as resolved.
Show resolved Hide resolved
SubjectPublicKeyInfo(SubjectPublicKeyInfoDer<'static>),

/// A DER-encoded plaintext RSA private key; as specified in PKCS #1/RFC 3447
///
/// Appears as "RSA PRIVATE KEY" in PEM files.
Expand Down Expand Up @@ -196,6 +201,7 @@ fn read_one_impl(

let item = match section_type.as_slice() {
b"CERTIFICATE" => Some(Item::X509Certificate(der.into())),
b"PUBLIC KEY" => Some(Item::SubjectPublicKeyInfo(der.into())),
b"RSA PRIVATE KEY" => Some(Item::Pkcs1Key(der.into())),
b"PRIVATE KEY" => Some(Item::Pkcs8Key(der.into())),
b"EC PRIVATE KEY" => Some(Item::Sec1Key(der.into())),
Expand Down
14 changes: 14 additions & 0 deletions tests/data/spki.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-----BEGIN PUBLIC KEY-----
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, how was this generated?

Copy link
Contributor Author

@holodorum holodorum Aug 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using openssl rsa -in private_key.pem -pubout -out public_key.pem

MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqIh8FTj9DIgI8DAoCBh+
6UXOfaWkvNaGZx2GwXl4WDAa/ZSE5/8ofg/6V59bmk9yry57UR4F+blscBvE4g3U
dTvWJOBRD900l21vwpDLKzZguyGOCmKwJu3vCnAQKzBRXW5sDgvO67GeU6kpaic9
LYPYnYaoxCRTYTZu0wy72rW5G0Fe8Gg/duJmUH7vqGIZupTTVzIBMbFVPBMJqprT
MStDhaUL0JiAz0ZgTeNLRIBZWV9mY4PG3rZtbV0BZGR1ipAq9xfgqJcURCcKl/ZT
UMtzvgk8s5hYkIJX0ZL3qsfdM4BMgIFhHq/GisQKbbu9kWldBrxQylOwa6r0m3Jv
KJX2ViDSORndaCz2sppmVx5HDHnj+Bw381yawphnpumP3BJK4iof//uYKvfdc4RC
y2EXL8PYPsT5DMB0jaBt92ytR5sLhn8Sl9Hk0buN4IjrYPISrdhS45xQXUqxcp9O
9hcU+rSaQyZ45cj+VlWhKq8MDvGvaAONBFSEh01mnUwoJObsAZNVFVtuOkwAli0F
kGouMycQY1BGscpdC516Nya361Hk/ICyby2Y0BJrrVGaSM6poXH9yEjglzAdtSDb
Cvhn/zlAI5ltm4Nv2qTgYBDns5JRGVhBym6RbbZ1C/KfCgn0hOxiw3N7AN4d0K5n
LI6p7U9RnNVbWgbqsuoxBtkCAwEAAQ==
-----END PUBLIC KEY-----
28 changes: 28 additions & 0 deletions tests/data/zen2.pem
Original file line number Diff line number Diff line change
Expand Up @@ -205,4 +205,32 @@ fxfwqBTecp4MBmRbH76PLKjekhL/LTWJnvVTzBLFo9PLIUIyTiZjAkEAuODuDzIP
LUuD0YV85m8BXM+OoPbyNmH9UmwPP0iFigbPz2IdYeSWU6Kxs6b5J7KdULwKgVeY
k9zbpTrkZyMjXw==
-----END PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqIh8FTj9DIgI8DAoCBh+
6UXOfaWkvNaGZx2GwXl4WDAa/ZSE5/8ofg/6V59bmk9yry57UR4F+blscBvE4g3U
dTvWJOBRD900l21vwpDLKzZguyGOCmKwJu3vCnAQKzBRXW5sDgvO67GeU6kpaic9
LYPYnYaoxCRTYTZu0wy72rW5G0Fe8Gg/duJmUH7vqGIZupTTVzIBMbFVPBMJqprT
MStDhaUL0JiAz0ZgTeNLRIBZWV9mY4PG3rZtbV0BZGR1ipAq9xfgqJcURCcKl/ZT
UMtzvgk8s5hYkIJX0ZL3qsfdM4BMgIFhHq/GisQKbbu9kWldBrxQylOwa6r0m3Jv
KJX2ViDSORndaCz2sppmVx5HDHnj+Bw381yawphnpumP3BJK4iof//uYKvfdc4RC
y2EXL8PYPsT5DMB0jaBt92ytR5sLhn8Sl9Hk0buN4IjrYPISrdhS45xQXUqxcp9O
9hcU+rSaQyZ45cj+VlWhKq8MDvGvaAONBFSEh01mnUwoJObsAZNVFVtuOkwAli0F
kGouMycQY1BGscpdC516Nya361Hk/ICyby2Y0BJrrVGaSM6poXH9yEjglzAdtSDb
Cvhn/zlAI5ltm4Nv2qTgYBDns5JRGVhBym6RbbZ1C/KfCgn0hOxiw3N7AN4d0K5n
LI6p7U9RnNVbWgbqsuoxBtkCAwEAAQ==
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqIh8FTj9DIgI8DAoCBh+
6UXOfaWkvNaGZx2GwXl4WDAa/ZSE5/8ofg/6V59bmk9yry57UR4F+blscBvE4g3U
dTvWJOBRD900l21vwpDLKzZguyGOCmKwJu3vCnAQKzBRXW5sDgvO67GeU6kpaic9
LYPYnYaoxCRTYTZu0wy72rW5G0Fe8Gg/duJmUH7vqGIZupTTVzIBMbFVPBMJqprT
MStDhaUL0JiAz0ZgTeNLRIBZWV9mY4PG3rZtbV0BZGR1ipAq9xfgqJcURCcKl/ZT
UMtzvgk8s5hYkIJX0ZL3qsfdM4BMgIFhHq/GisQKbbu9kWldBrxQylOwa6r0m3Jv
KJX2ViDSORndaCz2sppmVx5HDHnj+Bw381yawphnpumP3BJK4iof//uYKvfdc4RC
y2EXL8PYPsT5DMB0jaBt92ytR5sLhn8Sl9Hk0buN4IjrYPISrdhS45xQXUqxcp9O
9hcU+rSaQyZ45cj+VlWhKq8MDvGvaAONBFSEh01mnUwoJObsAZNVFVtuOkwAli0F
kGouMycQY1BGscpdC516Nya361Hk/ICyby2Y0BJrrVGaSM6poXH9yEjglzAdtSDb
Cvhn/zlAI5ltm4Nv2qTgYBDns5JRGVhBym6RbbZ1C/KfCgn0hOxiw3N7AN4d0K5n
LI6p7U9RnNVbWgbqsuoxBtkCAwEAAQ==
-----END PUBLIC KEY-----
... that's all folks!
35 changes: 34 additions & 1 deletion tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,39 @@ fn private_key() {
assert!(rustls_pemfile::private_key(&mut reader).unwrap().is_none());
}

#[test]
fn public_keys() {
let data = include_bytes!("data/spki.pem");
let mut reader = BufReader::new(&data[..]);
assert_eq!(
rustls_pemfile::public_keys(&mut reader)
.collect::<Result<Vec<_>, _>>()
.unwrap()
.len(),
1
);

let data = include_bytes!("data/zen2.pem");
let mut reader = BufReader::new(&data[..]);
assert_eq!(
rustls_pemfile::public_keys(&mut reader)
.collect::<Result<Vec<_>, _>>()
.unwrap()
.len(),
2
);

let data = include_bytes!("data/certificate.chain.pem");
let mut reader = BufReader::new(&data[..]);
assert_eq!(
rustls_pemfile::public_keys(&mut reader)
.collect::<Result<Vec<_>, _>>()
.unwrap()
.len(),
0
);
}

#[test]
fn test_csr() {
let data = include_bytes!("data/csr.pem");
Expand Down Expand Up @@ -115,7 +148,7 @@ fn smoketest_iterate() {
count += 1;
}

assert_eq!(count, 16);
assert_eq!(count, 18);
}

#[test]
Expand Down