Skip to content

Unbundle params from output types #328

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 9 commits into from
Apr 23, 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
11 changes: 6 additions & 5 deletions rcgen/examples/sign-leaf-with-ca.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use time::{Duration, OffsetDateTime};

/// Example demonstrating signing end-entity certificate with ca
fn main() {
let (ca, ca_key) = new_ca();
let end_entity = new_end_entity(&ca, &ca_key);
let (ca_params, ca, ca_key) = new_ca();
let end_entity = new_end_entity(&ca_params, &ca_key);

let end_entity_pem = end_entity.pem();
println!("directly signed end-entity certificate: {end_entity_pem}");
Expand All @@ -16,7 +16,7 @@ fn main() {
println!("ca certificate: {ca_cert_pem}");
}

fn new_ca() -> (Certificate, KeyPair) {
fn new_ca() -> (CertificateParams, Certificate, KeyPair) {
let mut params =
CertificateParams::new(Vec::default()).expect("empty subject alt name can't produce error");
let (yesterday, tomorrow) = validity_period();
Expand All @@ -36,10 +36,11 @@ fn new_ca() -> (Certificate, KeyPair) {
params.not_after = tomorrow;

let key_pair = KeyPair::generate().unwrap();
(params.self_signed(&key_pair).unwrap(), key_pair)
let cert = params.self_signed(&key_pair).unwrap();
(params, cert, key_pair)
}

fn new_end_entity(ca: &Certificate, ca_key: &KeyPair) -> Certificate {
fn new_end_entity(ca: &CertificateParams, ca_key: &KeyPair) -> Certificate {
let name = "entity.other.host";
let mut params = CertificateParams::new(vec![name.into()]).expect("we know the name is valid");
let (yesterday, tomorrow) = validity_period();
Expand Down
85 changes: 33 additions & 52 deletions rcgen/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,44 +10,32 @@ use yasna::{DERWriter, Tag};

use crate::crl::CrlDistributionPoint;
use crate::csr::CertificateSigningRequest;
use crate::key_pair::{serialize_public_key_der, PublicKeyData};
use crate::key_pair::{serialize_public_key_der, sign_der, PublicKeyData};
#[cfg(feature = "crypto")]
use crate::ring_like::digest;
#[cfg(feature = "pem")]
use crate::ENCODE_CONFIG;
use crate::{
oid, write_distinguished_name, write_dt_utc_or_generalized,
write_x509_authority_key_identifier, write_x509_extension, DistinguishedName, Error, Issuer,
KeyIdMethod, KeyPair, KeyUsagePurpose, SanType, SerialNumber,
KeyIdMethod, KeyUsagePurpose, SanType, SerialNumber, SigningKey,
};

/// An issued certificate together with the parameters used to generate it.
/// An issued certificate
#[derive(Debug, Clone)]
pub struct Certificate {
pub(crate) params: CertificateParams,
pub(crate) subject_public_key_info: Vec<u8>,
pub(crate) der: CertificateDer<'static>,
}

impl Certificate {
/// Returns the certificate parameters
pub fn params(&self) -> &CertificateParams {
&self.params
}
/// Calculates a subject key identifier for the certificate subject's public key.
/// This key identifier is used in the SubjectKeyIdentifier X.509v3 extension.
pub fn key_identifier(&self) -> Vec<u8> {
self.params
.key_identifier_method
.derive(&self.subject_public_key_info)
}
/// Get the certificate in DER encoded format.
///
/// [`CertificateDer`] implements `Deref<Target = [u8]>` and `AsRef<[u8]>`, so you can easily
/// extract the DER bytes from the return value.
pub fn der(&self) -> &CertificateDer<'static> {
&self.der
}

/// Get the certificate in PEM encoded format.
#[cfg(feature = "pem")]
pub fn pem(&self) -> String {
Expand All @@ -61,12 +49,6 @@ impl From<Certificate> for CertificateDer<'static> {
}
}

impl AsRef<CertificateParams> for Certificate {
fn as_ref(&self) -> &CertificateParams {
&self.params
}
}

/// Parameters used for certificate generation
#[allow(missing_docs)]
#[non_exhaustive]
Expand Down Expand Up @@ -156,49 +138,47 @@ impl CertificateParams {
/// The returned [`Certificate`] may be serialized using [`Certificate::der`] and
/// [`Certificate::pem`].
pub fn signed_by(
self,
&self,
public_key: &impl PublicKeyData,
issuer: &Certificate,
issuer_key: &KeyPair,
issuer: &CertificateParams,
issuer_key: &impl SigningKey,
) -> Result<Certificate, Error> {
let issuer = Issuer {
distinguished_name: &issuer.params.distinguished_name,
key_identifier_method: &issuer.params.key_identifier_method,
key_usages: &issuer.params.key_usages,
distinguished_name: &issuer.distinguished_name,
key_identifier_method: &issuer.key_identifier_method,
key_usages: &issuer.key_usages,
key_pair: issuer_key,
};

let subject_public_key_info =
yasna::construct_der(|writer| serialize_public_key_der(public_key, writer));
let der = self.serialize_der_with_signer(public_key, issuer)?;
Ok(Certificate {
params: self,
subject_public_key_info,
der,
der: self.serialize_der_with_signer(public_key, issuer)?,
})
}

/// Generates a new self-signed certificate from the given parameters.
///
/// The returned [`Certificate`] may be serialized using [`Certificate::der`] and
/// [`Certificate::pem`].
pub fn self_signed(self, key_pair: &KeyPair) -> Result<Certificate, Error> {
pub fn self_signed(&self, key_pair: &impl SigningKey) -> Result<Certificate, Error> {
let issuer = Issuer {
distinguished_name: &self.distinguished_name,
key_identifier_method: &self.key_identifier_method,
key_usages: &self.key_usages,
key_pair,
};

let subject_public_key_info = key_pair.public_key_der();
let der = self.serialize_der_with_signer(key_pair, issuer)?;
Ok(Certificate {
params: self,
subject_public_key_info,
der,
der: self.serialize_der_with_signer(key_pair, issuer)?,
})
}

/// Calculates a subject key identifier for the certificate subject's public key.
/// This key identifier is used in the SubjectKeyIdentifier X.509v3 extension.
pub fn key_identifier(&self, key: &impl PublicKeyData) -> Vec<u8> {
self.key_identifier_method
.derive(&key.subject_public_key_info())
}

/// Parses an existing ca certificate from the ASCII PEM format.
///
/// See [`from_ca_cert_der`](Self::from_ca_cert_der) for more details.
Expand All @@ -212,7 +192,7 @@ impl CertificateParams {
///
/// This function is only of use if you have an existing CA certificate
/// you would like to use to sign a certificate generated by `rcgen`.
/// By providing the constructed [`CertificateParams`] and the [`KeyPair`]
/// By providing the constructed [`CertificateParams`] and the [`SigningKey`]
/// associated with your existing `ca_cert` you can use [`CertificateParams::signed_by()`]
/// or [`crate::CertificateSigningRequestParams::signed_by()`] to issue new certificates
/// using the CA cert.
Expand Down Expand Up @@ -543,7 +523,7 @@ impl CertificateParams {
/// same output.
pub fn serialize_request(
&self,
subject_key: &KeyPair,
subject_key: &impl SigningKey,
) -> Result<CertificateSigningRequest, Error> {
self.serialize_request_with_attributes(subject_key, Vec::new())
}
Expand All @@ -561,7 +541,7 @@ impl CertificateParams {
/// [RFC 2986]: <https://datatracker.ietf.org/doc/html/rfc2986#section-4>
pub fn serialize_request_with_attributes(
&self,
subject_key: &KeyPair,
subject_key: &impl SigningKey,
attrs: Vec<Attribute>,
) -> Result<CertificateSigningRequest, Error> {
// No .. pattern, we use this to ensure every field is used
Expand Down Expand Up @@ -609,7 +589,7 @@ impl CertificateParams {
|| !extended_key_usages.is_empty()
|| !custom_extensions.is_empty();

let der = subject_key.sign_der(|writer| {
let der = sign_der(subject_key, |writer| {
// Write version
writer.next().write_u8(0);
write_distinguished_name(writer.next(), distinguished_name);
Expand Down Expand Up @@ -645,11 +625,10 @@ impl CertificateParams {
pub(crate) fn serialize_der_with_signer<K: PublicKeyData>(
&self,
pub_key: &K,
issuer: Issuer<'_>,
issuer: Issuer<'_, impl SigningKey>,
) -> Result<CertificateDer<'static>, Error> {
let der = issuer.key_pair.sign_der(|writer| {
let pub_key_spki =
yasna::construct_der(|writer| serialize_public_key_der(pub_key, writer));
let der = sign_der(issuer.key_pair, |writer| {
let pub_key_spki = pub_key.subject_public_key_info();
// Write version
writer.next().write_tagged(Tag::context(0), |writer| {
writer.write_u8(2);
Expand All @@ -672,7 +651,7 @@ impl CertificateParams {
}
};
// Write signature algorithm
issuer.key_pair.alg.write_alg_ident(writer.next());
issuer.key_pair.algorithm().write_alg_ident(writer.next());
// Write issuer name
write_distinguished_name(writer.next(), &issuer.distinguished_name);
// Write validity
Expand Down Expand Up @@ -709,7 +688,7 @@ impl CertificateParams {
#[cfg(feature = "crypto")]
_ => issuer
.key_identifier_method
.derive(issuer.key_pair.public_key_der()),
.derive(issuer.key_pair.subject_public_key_info()),
},
);
}
Expand Down Expand Up @@ -1236,6 +1215,8 @@ pub enum BasicConstraints {
mod tests {
#[cfg(feature = "pem")]
use super::*;
#[cfg(feature = "crypto")]
use crate::KeyPair;

#[cfg(feature = "crypto")]
#[test]
Expand Down Expand Up @@ -1493,7 +1474,7 @@ PITGdT9dgN88nHPCle0B1+OY+OZ5

let ca_kp = KeyPair::from_pem(ca_key).unwrap();
let ca_cert = params.self_signed(&ca_kp).unwrap();
assert_eq!(&ca_ski, &ca_cert.key_identifier());
assert_eq!(ca_ski, params.key_identifier(&ca_kp));

let (_, x509_ca) = x509_parser::parse_x509_certificate(ca_cert.der()).unwrap();
assert_eq!(
Expand All @@ -1512,7 +1493,7 @@ PITGdT9dgN88nHPCle0B1+OY+OZ5
let ee_key = KeyPair::generate().unwrap();
let mut ee_params = CertificateParams::default();
ee_params.use_authority_key_identifier_extension = true;
let ee_cert = ee_params.signed_by(&ee_key, &ca_cert, &ee_key).unwrap();
let ee_cert = ee_params.signed_by(&ee_key, &params, &ee_key).unwrap();

let (_, x509_ee) = x509_parser::parse_x509_certificate(ee_cert.der()).unwrap();
assert_eq!(
Expand Down
45 changes: 20 additions & 25 deletions rcgen/src/crl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ use time::OffsetDateTime;
use yasna::DERWriter;
use yasna::Tag;

use crate::key_pair::sign_der;
#[cfg(feature = "pem")]
use crate::ENCODE_CONFIG;
use crate::{
oid, write_distinguished_name, write_dt_utc_or_generalized,
write_x509_authority_key_identifier, write_x509_extension, Certificate, Error, Issuer,
KeyIdMethod, KeyPair, KeyUsagePurpose, SerialNumber,
write_x509_authority_key_identifier, write_x509_extension, CertificateParams, Error, Issuer,
KeyIdMethod, KeyUsagePurpose, SerialNumber, SigningKey,
};

/// A certificate revocation list (CRL)
Expand All @@ -24,9 +25,12 @@ use crate::{
/// #[cfg(not(feature = "crypto"))]
/// struct MyKeyPair { public_key: Vec<u8> }
/// #[cfg(not(feature = "crypto"))]
/// impl RemoteKeyPair for MyKeyPair {
/// fn public_key(&self) -> &[u8] { &self.public_key }
/// impl SigningKey for MyKeyPair {
/// fn sign(&self, _: &[u8]) -> Result<Vec<u8>, rcgen::Error> { Ok(vec![]) }
/// }
/// #[cfg(not(feature = "crypto"))]
/// impl PublicKeyData for MyKeyPair {
/// fn der_bytes(&self) -> &[u8] { &self.public_key }
/// fn algorithm(&self) -> &'static SignatureAlgorithm { &PKCS_ED25519 }
/// }
/// # fn main () {
Expand All @@ -38,9 +42,7 @@ use crate::{
/// #[cfg(feature = "crypto")]
/// let key_pair = KeyPair::generate().unwrap();
/// #[cfg(not(feature = "crypto"))]
/// let remote_key_pair = MyKeyPair { public_key: vec![] };
/// #[cfg(not(feature = "crypto"))]
/// let key_pair = KeyPair::from_remote(Box::new(remote_key_pair)).unwrap();
/// let key_pair = MyKeyPair { public_key: vec![] };
/// let issuer = issuer_params.self_signed(&key_pair).unwrap();
/// // Describe a revoked certificate.
/// let revoked_cert = RevokedCertParams{
Expand All @@ -60,20 +62,14 @@ use crate::{
/// key_identifier_method: KeyIdMethod::Sha256,
/// #[cfg(not(feature = "crypto"))]
/// key_identifier_method: KeyIdMethod::PreSpecified(vec![]),
/// }.signed_by(&issuer, &key_pair).unwrap();
/// }.signed_by(&issuer_params, &key_pair).unwrap();
///# }
#[derive(Debug)]
pub struct CertificateRevocationList {
params: CertificateRevocationListParams,
der: CertificateRevocationListDer<'static>,
}

impl CertificateRevocationList {
/// Returns the certificate revocation list (CRL) parameters.
pub fn params(&self) -> &CertificateRevocationListParams {
&self.params
}

/// Get the CRL in PEM encoded format.
#[cfg(feature = "pem")]
pub fn pem(&self) -> Result<String, Error> {
Expand Down Expand Up @@ -189,18 +185,18 @@ impl CertificateRevocationListParams {
///
/// Including a signature from the issuing certificate authority's key.
pub fn signed_by(
self,
issuer: &Certificate,
issuer_key: &KeyPair,
&self,
issuer: &CertificateParams,
issuer_key: &impl SigningKey,
) -> Result<CertificateRevocationList, Error> {
if self.next_update.le(&self.this_update) {
return Err(Error::InvalidCrlNextUpdate);
}

let issuer = Issuer {
distinguished_name: &issuer.params.distinguished_name,
key_identifier_method: &issuer.params.key_identifier_method,
key_usages: &issuer.params.key_usages,
distinguished_name: &issuer.distinguished_name,
key_identifier_method: &issuer.key_identifier_method,
key_usages: &issuer.key_usages,
key_pair: issuer_key,
};

Expand All @@ -210,12 +206,11 @@ impl CertificateRevocationListParams {

Ok(CertificateRevocationList {
der: self.serialize_der(issuer)?.into(),
params: self,
})
}

fn serialize_der(&self, issuer: Issuer) -> Result<Vec<u8>, Error> {
issuer.key_pair.sign_der(|writer| {
fn serialize_der(&self, issuer: Issuer<'_, impl SigningKey>) -> Result<Vec<u8>, Error> {
sign_der(issuer.key_pair, |writer| {
// Write CRL version.
// RFC 5280 §5.1.2.1:
// This optional field describes the version of the encoded CRL. When
Expand All @@ -231,7 +226,7 @@ impl CertificateRevocationListParams {
// RFC 5280 §5.1.2.2:
// This field MUST contain the same algorithm identifier as the
// signatureAlgorithm field in the sequence CertificateList
issuer.key_pair.alg.write_alg_ident(writer.next());
issuer.key_pair.algorithm().write_alg_ident(writer.next());

// Write issuer.
// RFC 5280 §5.1.2.3:
Expand Down Expand Up @@ -275,7 +270,7 @@ impl CertificateRevocationListParams {
write_x509_authority_key_identifier(
writer.next(),
self.key_identifier_method
.derive(issuer.key_pair.public_key_der()),
.derive(issuer.key_pair.subject_public_key_info()),
);

// Write CRL number.
Expand Down
Loading
Loading