Skip to content
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
2 changes: 1 addition & 1 deletion 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 rcgen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rcgen"
version = "0.12.0"
version = "0.13.0"
documentation = "https://docs.rs/rcgen"
description.workspace = true
repository.workspace = true
Expand Down
12 changes: 7 additions & 5 deletions rcgen/examples/rsa-irc-openssl.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use rcgen::CertifiedKey;

fn main() -> Result<(), Box<dyn std::error::Error>> {
use rcgen::{date_time_ymd, Certificate, CertificateParams, DistinguishedName};
use std::fmt::Write;
Expand All @@ -15,8 +17,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let key_pair = rcgen::KeyPair::from_pem(&key_pair_pem)?;
params.key_pair = Some(key_pair);

let cert = Certificate::from_params(params)?;
let pem_serialized = cert.serialize_pem()?;
let CertifiedKey { cert, key_pair } = Certificate::generate_self_signed(params)?;
let pem_serialized = cert.pem();
let pem = pem::parse(&pem_serialized)?;
let der_serialized = pem.contents();
let hash = ring::digest::digest(&ring::digest::SHA512, der_serialized);
Expand All @@ -26,11 +28,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
});
println!("sha-512 fingerprint: {hash_hex}");
println!("{pem_serialized}");
println!("{}", cert.serialize_private_key_pem());
println!("{}", key_pair.serialize_pem());
std::fs::create_dir_all("certs/")?;
fs::write("certs/cert.pem", pem_serialized.as_bytes())?;
fs::write("certs/cert.der", der_serialized)?;
fs::write("certs/key.pem", cert.serialize_private_key_pem().as_bytes())?;
fs::write("certs/key.der", cert.serialize_private_key_der())?;
fs::write("certs/key.pem", key_pair.serialize_pem().as_bytes())?;
fs::write("certs/key.der", key_pair.serialize_der())?;
Ok(())
}
12 changes: 7 additions & 5 deletions rcgen/examples/rsa-irc.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use rcgen::CertifiedKey;

fn main() -> Result<(), Box<dyn std::error::Error>> {
use rand::rngs::OsRng;
use rsa::pkcs8::EncodePrivateKey;
Expand All @@ -21,8 +23,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let key_pair = rcgen::KeyPair::try_from(private_key_der.as_bytes()).unwrap();
params.key_pair = Some(key_pair);

let cert = Certificate::from_params(params)?;
let pem_serialized = cert.serialize_pem()?;
let CertifiedKey { cert, key_pair } = Certificate::generate_self_signed(params)?;
let pem_serialized = cert.pem();
let pem = pem::parse(&pem_serialized)?;
let der_serialized = pem.contents();
let hash = ring::digest::digest(&ring::digest::SHA512, der_serialized);
Expand All @@ -32,11 +34,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
});
println!("sha-512 fingerprint: {hash_hex}");
println!("{pem_serialized}");
println!("{}", cert.serialize_private_key_pem());
println!("{}", key_pair.serialize_pem());
std::fs::create_dir_all("certs/")?;
fs::write("certs/cert.pem", pem_serialized.as_bytes())?;
fs::write("certs/cert.der", der_serialized)?;
fs::write("certs/key.pem", cert.serialize_private_key_pem().as_bytes())?;
fs::write("certs/key.der", cert.serialize_private_key_der())?;
fs::write("certs/key.pem", key_pair.serialize_pem().as_bytes())?;
fs::write("certs/key.der", key_pair.serialize_der())?;
Ok(())
}
16 changes: 8 additions & 8 deletions rcgen/examples/sign-leaf-with-ca.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
use rcgen::{
BasicConstraints, Certificate, CertificateParams, DnType, DnValue::PrintableString,
ExtendedKeyUsagePurpose, IsCa, KeyUsagePurpose,
BasicConstraints, Certificate, CertificateParams, CertifiedKey, DnType,
DnValue::PrintableString, ExtendedKeyUsagePurpose, IsCa, KeyUsagePurpose,
};
use time::{Duration, OffsetDateTime};

/// Example demonstrating signing end-endity certificate with ca
fn main() {
let ca = new_ca();
let ca = new_ca().cert;
let end_entity = new_end_entity();

let end_entity_pem = end_entity.serialize_pem_with_signer(&ca).unwrap();
let end_entity_pem = end_entity.pem();
println!("directly signed end-entity certificate: {end_entity_pem}");

let ca_cert_pem = ca.serialize_pem().unwrap();
let ca_cert_pem = ca.pem();
println!("ca certificate: {ca_cert_pem}",);
}

fn new_ca() -> Certificate {
fn new_ca() -> CertifiedKey {
let mut params = CertificateParams::new(Vec::default());
let (yesterday, tomorrow) = validity_period();
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
Expand All @@ -32,7 +32,7 @@ fn new_ca() -> Certificate {

params.not_before = yesterday;
params.not_after = tomorrow;
Certificate::from_params(params).unwrap()
Certificate::generate_self_signed(params).unwrap()
}

fn new_end_entity() -> Certificate {
Expand All @@ -47,7 +47,7 @@ fn new_end_entity() -> Certificate {
.push(ExtendedKeyUsagePurpose::ServerAuth);
params.not_before = yesterday;
params.not_after = tomorrow;
Certificate::from_params(params).unwrap()
Certificate::generate_self_signed(params).unwrap().cert
}

fn validity_period() -> (OffsetDateTime, OffsetDateTime) {
Expand Down
14 changes: 8 additions & 6 deletions rcgen/examples/simple.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use rcgen::{date_time_ymd, Certificate, CertificateParams, DistinguishedName, DnType, SanType};
use rcgen::{
date_time_ymd, Certificate, CertificateParams, CertifiedKey, DistinguishedName, DnType, SanType,
};
use std::fs;

fn main() -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -17,17 +19,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
SanType::DnsName("localhost".to_string()),
];

let cert = Certificate::from_params(params)?;
let CertifiedKey { cert, key_pair } = Certificate::generate_self_signed(params)?;

let pem_serialized = cert.serialize_pem()?;
let pem_serialized = cert.pem();
let pem = pem::parse(&pem_serialized)?;
let der_serialized = pem.contents();
println!("{pem_serialized}");
println!("{}", cert.serialize_private_key_pem());
println!("{}", key_pair.serialize_pem());
fs::create_dir_all("certs/")?;
fs::write("certs/cert.pem", pem_serialized.as_bytes())?;
fs::write("certs/cert.der", der_serialized)?;
fs::write("certs/key.pem", cert.serialize_private_key_pem().as_bytes())?;
fs::write("certs/key.der", cert.serialize_private_key_der())?;
fs::write("certs/key.pem", key_pair.serialize_pem().as_bytes())?;
fs::write("certs/key.der", key_pair.serialize_der())?;
Ok(())
}
54 changes: 40 additions & 14 deletions rcgen/src/crl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::oid::*;
use crate::ENCODE_CONFIG;
use crate::{
write_distinguished_name, write_dt_utc_or_generalized, write_x509_authority_key_identifier,
write_x509_extension,
write_x509_extension, DistinguishedName, KeyPair,
};
use crate::{Certificate, Error, KeyIdMethod, KeyUsagePurpose, SerialNumber, SignatureAlgorithm};

Expand All @@ -26,7 +26,7 @@ use crate::{Certificate, Error, KeyIdMethod, KeyUsagePurpose, SerialNumber, Sign
/// let mut issuer_params = CertificateParams::new(vec!["crl.issuer.example.com".to_string()]);
/// issuer_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
/// issuer_params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::DigitalSignature, KeyUsagePurpose::CrlSign];
/// let issuer = Certificate::from_params(issuer_params).unwrap();
/// let issuer = Certificate::generate_self_signed(issuer_params).unwrap();
/// // Describe a revoked certificate.
/// let revoked_cert = RevokedCertParams{
/// serial_number: SerialNumber::from(9999),
Expand Down Expand Up @@ -64,19 +64,31 @@ impl CertificateRevocationList {
}
/// Serializes the certificate revocation list (CRL) in binary DER format, signed with
/// the issuing certificate authority's key.
pub fn serialize_der_with_signer(&self, ca: &Certificate) -> Result<Vec<u8>, Error> {
pub fn serialize_der_with_signer(
&self,
ca: &Certificate,
ca_key: &KeyPair,
) -> Result<Vec<u8>, Error> {
if !ca.params.key_usages.is_empty()
&& !ca.params.key_usages.contains(&KeyUsagePurpose::CrlSign)
{
return Err(Error::IssuerNotCrlSigner);
}
self.params.serialize_der_with_signer(ca)
self.params.serialize_der_with_signer(
self.params.alg,
ca_key,
&ca.params.distinguished_name,
)
}
/// Serializes the certificate revocation list (CRL) in ASCII PEM format, signed with
/// the issuing certificate authority's key.
#[cfg(feature = "pem")]
pub fn serialize_pem_with_signer(&self, ca: &Certificate) -> Result<String, Error> {
let contents = self.serialize_der_with_signer(ca)?;
pub fn serialize_pem_with_signer(
&self,
ca: &Certificate,
ca_key: &KeyPair,
) -> Result<String, Error> {
let contents = self.serialize_der_with_signer(ca, ca_key)?;
let p = Pem::new("X509 CRL", contents);
Ok(pem::encode_config(&p, ENCODE_CONFIG))
}
Expand Down Expand Up @@ -172,29 +184,40 @@ pub struct CertificateRevocationListParams {
}

impl CertificateRevocationListParams {
fn serialize_der_with_signer(&self, ca: &Certificate) -> Result<Vec<u8>, Error> {
fn serialize_der_with_signer(
&self,
sig_alg: &SignatureAlgorithm,
issuer: &KeyPair,
issuer_name: &DistinguishedName,
) -> Result<Vec<u8>, Error> {
yasna::try_construct_der(|writer| {
// https://www.rfc-editor.org/rfc/rfc5280#section-5.1
writer.write_sequence(|writer| {
let tbs_cert_list_serialized = yasna::try_construct_der(|writer| {
self.write_crl(writer, ca)?;
self.write_crl(writer, sig_alg, issuer, issuer_name)?;
Ok::<(), Error>(())
})?;

// Write tbsCertList
writer.next().write_der(&tbs_cert_list_serialized);

// Write signatureAlgorithm
ca.params.alg.write_alg_ident(writer.next());
sig_alg.write_alg_ident(writer.next());

// Write signature
ca.key_pair.sign(&tbs_cert_list_serialized, writer.next())?;
issuer.sign(&tbs_cert_list_serialized, writer.next())?;

Ok(())
})
})
}
fn write_crl(&self, writer: DERWriter, ca: &Certificate) -> Result<(), Error> {
fn write_crl(
&self,
writer: DERWriter,
sig_alg: &SignatureAlgorithm,
issuer: &KeyPair,
issuer_name: &DistinguishedName,
) -> Result<(), Error> {
writer.write_sequence(|writer| {
// Write CRL version.
// RFC 5280 §5.1.2.1:
Expand All @@ -211,12 +234,12 @@ impl CertificateRevocationListParams {
// RFC 5280 §5.1.2.2:
// This field MUST contain the same algorithm identifier as the
// signatureAlgorithm field in the sequence CertificateList
ca.params.alg.write_alg_ident(writer.next());
sig_alg.write_alg_ident(writer.next());

// Write issuer.
// RFC 5280 §5.1.2.3:
// The issuer field MUST contain a non-empty X.500 distinguished name (DN).
write_distinguished_name(writer.next(), &ca.params.distinguished_name);
write_distinguished_name(writer.next(), issuer_name);

// Write thisUpdate date.
// RFC 5280 §5.1.2.4:
Expand Down Expand Up @@ -252,7 +275,10 @@ impl CertificateRevocationListParams {
writer.next().write_tagged(Tag::context(0), |writer| {
writer.write_sequence(|writer| {
// Write authority key identifier.
write_x509_authority_key_identifier(writer.next(), ca);
write_x509_authority_key_identifier(
writer.next(),
self.key_identifier_method.derive(issuer.public_key_der()),
);

// Write CRL number.
write_x509_extension(writer.next(), OID_CRL_NUMBER, false, |writer| {
Expand Down
25 changes: 5 additions & 20 deletions rcgen/src/csr.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
#[cfg(feature = "x509-parser")]
use crate::{DistinguishedName, SanType};
#[cfg(feature = "pem")]
use pem::Pem;
use crate::{DistinguishedName, Error, SanType};
use std::hash::Hash;

use crate::{Certificate, CertificateParams, Error, PublicKeyData, SignatureAlgorithm};
use crate::{CertificateParams, PublicKeyData, SignatureAlgorithm};

/// A public key, extracted from a CSR
#[derive(Debug, PartialEq, Eq, Hash)]
Expand All @@ -23,15 +21,15 @@ impl PublicKeyData for PublicKey {
}
}

/// Data for a certificate signing request
pub struct CertificateSigningRequest {
/// Parameters for a certificate signing request
pub struct CertificateSigningRequestParams {
/// Parameters for the certificate to be signed.
pub params: CertificateParams,
/// Public key to include in the certificate signing request.
pub public_key: PublicKey,
}

impl CertificateSigningRequest {
impl CertificateSigningRequestParams {
/// Parse a certificate signing request from the ASCII PEM format
///
/// See [`from_der`](Self::from_der) for more details.
Expand Down Expand Up @@ -92,17 +90,4 @@ impl CertificateSigningRequest {
public_key: PublicKey { alg, raw },
})
}
/// Serializes the requested certificate, signed with another certificate's key, in binary DER format
pub fn serialize_der_with_signer(&self, ca: &Certificate) -> Result<Vec<u8>, Error> {
self.params.serialize_der_with_signer(&self.public_key, ca)
}
/// Serializes the requested certificate, signed with another certificate's key, to the ASCII PEM format
#[cfg(feature = "pem")]
pub fn serialize_pem_with_signer(&self, ca: &Certificate) -> Result<String, Error> {
let contents = self
.params
.serialize_der_with_signer(&self.public_key, ca)?;
let p = Pem::new("CERTIFICATE", contents);
Ok(pem::encode_config(&p, crate::ENCODE_CONFIG))
}
}
22 changes: 21 additions & 1 deletion rcgen/src/key_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::sign_algo::SignAlgo;
use crate::ENCODE_CONFIG;
use crate::{Error, SignatureAlgorithm};

/// A key pair vairant
/// A key pair variant
#[allow(clippy::large_enum_variant)]
pub(crate) enum KeyPairKind {
/// A Ecdsa key pair
Expand Down Expand Up @@ -211,6 +211,26 @@ impl KeyPair {
}
}

/// Validate a provided key pair's compatibility with `sig_alg` or generate a new one.
///
/// If a provided `existing_key_pair` is not compatible with the `sig_alg` an error is
/// returned.
///
/// If `None` is provided for `existing_key_pair` a new key pair compatible with `sig_alg`
/// is generated from scratch.
pub(crate) fn validate_or_generate(
existing_key_pair: &mut Option<KeyPair>,
sig_alg: &'static SignatureAlgorithm,
) -> Result<Self, Error> {
match existing_key_pair.take() {
Some(kp) if !kp.is_compatible(sig_alg) => {
return Err(Error::CertificateKeyPairMismatch)
},
Some(kp) => Ok(kp),
None => KeyPair::generate(sig_alg),
}
}

/// Get the raw public key of this key pair
///
/// The key is in raw format, as how [`ring::signature::KeyPair::public_key`]
Expand Down
Loading