Skip to content

bump rcgen to 0.14.2 #3733

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

Merged
merged 1 commit into from
Jul 16, 2025
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
5 changes: 3 additions & 2 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ quote = "1"
rand = "0.8"
rasn = "0.18" # Not using the latest version which requires rust 1.85
rasn-cms = "0.18" # Not using the latest version which requires rust 1.85
rcgen = { version = "0.12", features = ["pem", "zeroize"] }
rcgen = { version = "0.14", features = ["pem", "zeroize"] }
regex = "1.4"
reqwest = { version = "0.12", default-features = false }
ron = "0.8"
Expand Down
23 changes: 9 additions & 14 deletions crates/common/axum_tls/src/acceptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,7 @@ mod tests {
let permitted_certificate =
rcgen::generate_simple_self_signed(vec!["not-my-client".into()]).unwrap();
let mut roots = RootCertStore::empty();
roots
.add(permitted_certificate.serialize_der().unwrap().into())
.unwrap();
roots.add(permitted_certificate.cert.der().clone()).unwrap();
let server = Server::with_trusted_roots(roots);
let client = Client::builder()
.add_root_certificate(server.certificate.clone())
Expand All @@ -198,9 +196,7 @@ mod tests {
let permitted_certificate =
rcgen::generate_simple_self_signed(vec!["not-my-client".into()]).unwrap();
let mut roots = RootCertStore::empty();
roots
.add(permitted_certificate.serialize_der().unwrap().into())
.unwrap();
roots.add(permitted_certificate.cert.der().clone()).unwrap();
let server = Server::with_trusted_roots(roots);
let client = Client::builder()
.add_root_certificate(server.certificate.clone())
Expand All @@ -223,9 +219,8 @@ mod tests {
let client_cert = rcgen::generate_simple_self_signed(["my-client".into()]).unwrap();
let identity = identity_from(&client_cert);
let mut cert_store = RootCertStore::empty();
cert_store.add_parsable_certificates([CertificateDer::from(
client_cert.serialize_der().unwrap(),
)]);
cert_store
.add_parsable_certificates([CertificateDer::from(client_cert.cert.der().as_ref())]);

let server = Server::with_trusted_roots(cert_store);
let client = Client::builder()
Expand Down Expand Up @@ -253,9 +248,9 @@ mod tests {
identity_from(&client_cert)
}

fn identity_from(cert: &rcgen::Certificate) -> Identity {
let mut pem = cert.serialize_private_key_pem().into_bytes();
pem.append(&mut cert.serialize_pem().unwrap().into_bytes());
fn identity_from(cert: &rcgen::CertifiedKey<rcgen::KeyPair>) -> Identity {
let mut pem = cert.signing_key.serialize_pem().into_bytes();
pem.append(&mut cert.cert.pem().into_bytes());
Identity::from_pem(&pem).unwrap()
}

Expand Down Expand Up @@ -293,9 +288,9 @@ mod tests {
port += 1;
};
let certificate = rcgen::generate_simple_self_signed(["localhost".to_owned()]).unwrap();
let certificate_der = CertificateDer::from(certificate.serialize_der().unwrap());
let certificate_der = certificate.cert.der().clone();
let private_key_der =
PrivateKeyDer::from_pem_slice(certificate.serialize_private_key_pem().as_bytes())
PrivateKeyDer::from_pem_slice(certificate.signing_key.serialize_pem().as_bytes())
.unwrap();
let certificate = reqwest::Certificate::from_der(&certificate_der).unwrap();
let config = ssl_config(vec![certificate_der], private_key_der, trusted_roots).unwrap();
Expand Down
6 changes: 3 additions & 3 deletions crates/common/axum_tls/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ use yansi::Paint;
/// use tedge_config::{OptionalConfig, TEdgeConfig};
///
/// let cert = rcgen::generate_simple_self_signed(["localhost".to_owned()]).unwrap();
/// let cert_pem = cert.serialize_pem().unwrap();
/// let key_pem = cert.serialize_private_key_pem();
/// let cert_pem = cert.cert.pem();
/// let key_pem = cert.signing_key.serialize_pem();
///
/// let config = load_ssl_config(
/// OptionalConfig::present(InjectedValue(cert_pem), "http.cert_path"),
Expand Down Expand Up @@ -147,7 +147,7 @@ pub trait TrustStoreLoader {
/// use axum_tls::config::InjectedValue;
/// use axum_tls::load_cert;
/// let cert = rcgen::generate_simple_self_signed(["localhost".to_owned()]).unwrap();
/// let pem_data = cert.serialize_pem().unwrap();
/// let pem_data = cert.cert.pem();
///
/// let loaded_chain = load_cert(&InjectedValue(pem_data)).unwrap();
///
Expand Down
136 changes: 88 additions & 48 deletions crates/common/certificate/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use anyhow::Context;
use camino::Utf8Path;
use device_id::DeviceIdError;
use rcgen::Certificate;
use rcgen::CertificateParams;
use rcgen::KeyPair;
use sha1::Digest;
Expand Down Expand Up @@ -224,17 +223,17 @@ pub struct RemoteKeyPair {
algorithm: &'static rcgen::SignatureAlgorithm,
}

impl RemoteKeyPair {
pub fn to_key_pair(&self) -> Result<KeyPair, CertificateError> {
Ok(KeyPair::from_remote(Box::new(self.clone()))?)
impl rcgen::PublicKeyData for RemoteKeyPair {
fn der_bytes(&self) -> &[u8] {
&self.public_key_raw
}
}

impl rcgen::RemoteKeyPair for RemoteKeyPair {
fn public_key(&self) -> &[u8] {
&self.public_key_raw
fn algorithm(&self) -> &'static rcgen::SignatureAlgorithm {
self.algorithm
}
}

impl rcgen::SigningKey for RemoteKeyPair {
fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, rcgen::Error> {
// the error here is not PEM-related, but we need to return a foreign error type, and there
// are no other better variants that could let us return context, so we'll have to use this
Expand All @@ -245,14 +244,43 @@ impl rcgen::RemoteKeyPair for RemoteKeyPair {
.sign(msg)
.map_err(|e| rcgen::Error::PemError(e.to_string()))
}
}

pub struct KeyCertPair {
certificate: rcgen::Certificate,
// in rcgen 0.14 params are necessary to generate the CSR
params: rcgen::CertificateParams,
signing_key: SigningKeyWrapper,
}
Comment on lines +249 to +254
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to confirm.

Previously, the certificate was protected under Zeroizing. Now this protection has been narrowed under the signing_key in the case there is actually a private key in memory. This makes sense. However, can you confirm that, with version 0.14, rcgen::Certificate no more contains data to zeroize on drop?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, wrapping rcgen::Certificate with Zeroizing<> generates the following error:

error[E0277]: the trait bound `rcgen::Certificate: zeroize::DefaultIsZeroes` is not satisfied
   --> crates/common/certificate/src/lib.rs:250:18
    |
250 |     certificate: Zeroizing<rcgen::Certificate>,
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `zeroize::DefaultIsZeroes` is not implemented for `rcgen::Certificate`
    |

And in 0.14 only rcgen::KeyPair implements Zeroize, which makes sense because it's the only thing holding sensitive data.


enum SigningKeyWrapper {
Local(Zeroizing<rcgen::KeyPair>),
Remote(RemoteKeyPair),
}

impl rcgen::PublicKeyData for SigningKeyWrapper {
fn der_bytes(&self) -> &[u8] {
match self {
Self::Local(k) => k.der_bytes(),
Self::Remote(k) => k.der_bytes(),
}
}

fn algorithm(&self) -> &'static rcgen::SignatureAlgorithm {
self.algorithm
match self {
Self::Local(k) => k.algorithm(),
Self::Remote(k) => k.algorithm(),
}
}
}

pub struct KeyCertPair {
certificate: Zeroizing<rcgen::Certificate>,
impl rcgen::SigningKey for SigningKeyWrapper {
fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, rcgen::Error> {
match self {
Self::Local(k) => k.sign(msg),
Self::Remote(k) => k.sign(msg),
}
}
}

impl KeyCertPair {
Expand All @@ -263,11 +291,13 @@ impl KeyCertPair {
) -> Result<KeyCertPair, CertificateError> {
let today = OffsetDateTime::now_utc();
let not_before = today - Duration::days(1); // Ensure the certificate is valid today
let params =
let (params, signing_key) =
Self::create_selfsigned_certificate_parameters(config, id, key_kind, not_before)?;

Ok(KeyCertPair {
certificate: Zeroizing::new(Certificate::from_params(params)?),
certificate: params.self_signed(&signing_key)?,
signing_key,
params,
})
}

Expand All @@ -278,11 +308,14 @@ impl KeyCertPair {
) -> Result<KeyCertPair, CertificateError> {
// Create Certificate without `not_before` and `not_after` fields
// as rcgen library will not parse it for certificate signing request
let params = Self::create_csr_parameters(config, id, key_kind)?;
let (params, signing_key) = Self::create_csr_parameters(config, id, key_kind)?;
let issuer = rcgen::Issuer::from_params(&params, &signing_key);
Ok(KeyCertPair {
certificate: Zeroizing::new(
Certificate::from_params(params).context("Failed to create CSR")?,
),
certificate: params
.signed_by(&signing_key, &issuer)
.context("Failed to create CSR")?,
signing_key,
params,
})
}

Expand All @@ -291,8 +324,8 @@ impl KeyCertPair {
id: &str,
key_kind: &KeyKind,
not_before: OffsetDateTime,
) -> Result<CertificateParams, CertificateError> {
let mut params = Self::create_csr_parameters(config, id, key_kind)?;
) -> Result<(CertificateParams, SigningKeyWrapper), CertificateError> {
let (mut params, signing_key) = Self::create_csr_parameters(config, id, key_kind)?;

let not_after = not_before + Duration::days(config.validity_period_days.into());
params.not_before = not_before;
Expand All @@ -301,14 +334,14 @@ impl KeyCertPair {
// IsCa::SelfSignedOnly is rejected by C8Y with "422 Unprocessable Entity"
params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);

Ok(params)
Ok((params, signing_key))
}

fn create_csr_parameters(
config: &CsrTemplate,
id: &str,
key_kind: &KeyKind,
) -> Result<CertificateParams, CertificateError> {
) -> Result<(CertificateParams, SigningKeyWrapper), CertificateError> {
KeyCertPair::check_identifier(id, config.max_cn_size)?;
let mut distinguished_name = rcgen::DistinguishedName::new();
distinguished_name.push(rcgen::DnType::CommonName, id);
Expand All @@ -321,38 +354,38 @@ impl KeyCertPair {
let mut params = CertificateParams::default();
params.distinguished_name = distinguished_name;

match key_kind {
let signing_key: SigningKeyWrapper = match key_kind {
KeyKind::New => {
// ECDSA signing using the P-256 curves and SHA-256 hashing as per RFC 5758
params.alg = &rcgen::PKCS_ECDSA_P256_SHA256;
SigningKeyWrapper::Local(Zeroizing::new(KeyPair::generate_for(
&rcgen::PKCS_ECDSA_P256_SHA256,
)?))
}
KeyKind::Reuse { keypair_pem } => {
// Use the same signing algorithm as the existing key
// Failing to do so leads to an error telling the algorithm is not compatible
let key_pair = KeyPair::from_pem(keypair_pem)?;
params.alg = key_pair.algorithm();
params.key_pair = Some(key_pair);
}
KeyKind::ReuseRemote(key_pair) => {
let key_pair = key_pair.to_key_pair()?;
params.alg = key_pair.algorithm();
params.key_pair = Some(key_pair)
SigningKeyWrapper::Local(Zeroizing::new(KeyPair::from_pem(keypair_pem)?))
}
}
KeyKind::ReuseRemote(remote) => SigningKeyWrapper::Remote(remote.clone()),
};

Ok(params)
Ok((params, signing_key))
}

pub fn certificate_pem_string(&self) -> Result<String, CertificateError> {
Ok(self.certificate.serialize_pem()?)
Ok(self.certificate.pem())
}

pub fn private_key_pem_string(&self) -> Result<Zeroizing<String>, CertificateError> {
Ok(Zeroizing::new(self.certificate.serialize_private_key_pem()))
if let SigningKeyWrapper::Local(keypair) = &self.signing_key {
Ok(Zeroizing::new(keypair.serialize_pem()))
} else {
Err(anyhow::anyhow!("Can't serialize private key PEM for remote private key").into())
}
}

pub fn certificate_signing_request_string(&self) -> Result<String, CertificateError> {
Ok(self.certificate.serialize_request_pem()?)
Ok(self.params.serialize_request(&self.signing_key)?.pem()?)
}

fn check_identifier(id: &str, max_cn_size: usize) -> Result<(), CertificateError> {
Expand Down Expand Up @@ -555,7 +588,7 @@ mod tests {
let id = "some-id";
let birthdate = datetime!(2021-03-31 16:39:57 +01:00);

let params = KeyCertPair::create_selfsigned_certificate_parameters(
let (params, signing_key) = KeyCertPair::create_selfsigned_certificate_parameters(
&config,
id,
&KeyKind::New,
Expand All @@ -564,9 +597,11 @@ mod tests {
.expect("Fail to get a certificate parameters");

let keypair = KeyCertPair {
certificate: Zeroizing::new(
Certificate::from_params(params).expect("Fail to create a certificate"),
),
certificate: params
.self_signed(&signing_key)
.expect("Fail to create a certificate"),
params,
signing_key,
};

// Check the not_before date
Expand All @@ -587,7 +622,7 @@ mod tests {
let id = "some-id";
let birthdate = datetime!(2021-03-31 16:39:57 +01:00);

let params = KeyCertPair::create_selfsigned_certificate_parameters(
let (params, signing_key) = KeyCertPair::create_selfsigned_certificate_parameters(
&config,
id,
&KeyKind::New,
Expand All @@ -596,9 +631,11 @@ mod tests {
.expect("Fail to get a certificate parameters");

let keypair = KeyCertPair {
certificate: Zeroizing::new(
Certificate::from_params(params).expect("Fail to create a certificate"),
),
certificate: params
.self_signed(&signing_key)
.expect("Fail to create a certificate"),
params,
signing_key,
};

// Check the not_after date
Expand All @@ -613,13 +650,16 @@ mod tests {
let config = CsrTemplate::default();
let id = "some-id";

let params = KeyCertPair::create_csr_parameters(&config, id, &KeyKind::New)
let (params, signing_key) = KeyCertPair::create_csr_parameters(&config, id, &KeyKind::New)
.expect("Fail to get a certificate parameters");

let issuer = rcgen::Issuer::from_params(&params, &signing_key);
let keypair = KeyCertPair {
certificate: Zeroizing::new(
Certificate::from_params(params).expect("Fail to create a certificate"),
),
certificate: params
.signed_by(&signing_key, &issuer)
.expect("Fail to create a certificate"),
params,
signing_key,
};

// Check the subject
Expand Down
5 changes: 2 additions & 3 deletions crates/common/download/src/download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,6 @@ mod tests {
use axum::Router;
use hyper::header::AUTHORIZATION;
use rustls::pki_types::pem::PemObject;
use rustls::pki_types::CertificateDer;
use rustls::pki_types::PrivateKeyDer;
use rustls::RootCertStore;
use std::io::Write;
Expand Down Expand Up @@ -970,8 +969,8 @@ mod tests {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let port = listener.local_addr().unwrap().port();
let server_cert = rcgen::generate_simple_self_signed(["localhost".into()]).unwrap();
let cert = CertificateDer::from(server_cert.serialize_der().unwrap());
let key = PrivateKeyDer::from_pem_slice(server_cert.serialize_private_key_pem().as_bytes())
let cert = server_cert.cert.der().clone();
let key = PrivateKeyDer::from_pem_slice(server_cert.signing_key.serialize_pem().as_bytes())
.unwrap();
let mut accepted_certs = RootCertStore::empty();
accepted_certs.add(cert.clone()).unwrap();
Expand Down
Loading
Loading