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
7 changes: 1 addition & 6 deletions rcgen/examples/rsa-irc-openssl.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
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 @@ -10,14 +8,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
params.not_after = date_time_ymd(4096, 1, 1);
params.distinguished_name = DistinguishedName::new();

params.alg = &rcgen::PKCS_RSA_SHA256;

let pkey: openssl::pkey::PKey<_> = openssl::rsa::Rsa::generate(2048)?.try_into()?;
let key_pair_pem = String::from_utf8(pkey.private_key_to_pem_pkcs8()?)?;
let key_pair = rcgen::KeyPair::from_pem(&key_pair_pem)?;
params.key_pair = Some(key_pair);

let CertifiedKey { cert, key_pair } = Certificate::generate_self_signed(params)?;
let cert = Certificate::generate_self_signed(params, &key_pair)?;
let pem_serialized = cert.pem();
let pem = pem::parse(&pem_serialized)?;
let der_serialized = pem.contents();
Expand Down
7 changes: 1 addition & 6 deletions rcgen/examples/rsa-irc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use rcgen::CertifiedKey;

fn main() -> Result<(), Box<dyn std::error::Error>> {
use rand::rngs::OsRng;
use rsa::pkcs8::EncodePrivateKey;
Expand All @@ -14,16 +12,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
params.not_after = date_time_ymd(4096, 1, 1);
params.distinguished_name = DistinguishedName::new();

params.alg = &rcgen::PKCS_RSA_SHA256;

let mut rng = OsRng;
let bits = 2048;
let private_key = RsaPrivateKey::new(&mut rng, bits)?;
let private_key_der = private_key.to_pkcs8_der()?;
let key_pair = rcgen::KeyPair::try_from(private_key_der.as_bytes()).unwrap();
params.key_pair = Some(key_pair);

let CertifiedKey { cert, key_pair } = Certificate::generate_self_signed(params)?;
let cert = Certificate::generate_self_signed(params, &key_pair)?;
let pem_serialized = cert.pem();
let pem = pem::parse(&pem_serialized)?;
let der_serialized = pem.contents();
Expand Down
18 changes: 11 additions & 7 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, CertifiedKey, DnType,
DnValue::PrintableString, ExtendedKeyUsagePurpose, IsCa, KeyUsagePurpose,
BasicConstraints, Certificate, CertificateParams, DnType, DnValue::PrintableString,
ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose,
};
use time::{Duration, OffsetDateTime};

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

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

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

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

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

let key_pair = KeyPair::generate().unwrap();
Certificate::generate_self_signed(params, &key_pair).unwrap()
}

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

let key_pair = KeyPair::generate().unwrap();
Certificate::generate_self_signed(params, &key_pair).unwrap()
}

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

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

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

let pem_serialized = cert.pem();
let pem = pem::parse(&pem_serialized)?;
Expand Down
23 changes: 8 additions & 15 deletions rcgen/src/crl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
write_distinguished_name, write_dt_utc_or_generalized, write_x509_authority_key_identifier,
write_x509_extension, DistinguishedName, KeyPair,
};
use crate::{Certificate, Error, KeyIdMethod, KeyUsagePurpose, SerialNumber, SignatureAlgorithm};
use crate::{Certificate, Error, KeyIdMethod, KeyUsagePurpose, SerialNumber};

/// A certificate revocation list (CRL)
///
Expand All @@ -26,7 +26,8 @@ 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::generate_self_signed(issuer_params).unwrap();
/// let key_pair = KeyPair::generate().unwrap();
/// let issuer = Certificate::generate_self_signed(issuer_params, &key_pair).unwrap();
/// // Describe a revoked certificate.
/// let revoked_cert = RevokedCertParams{
/// serial_number: SerialNumber::from(9999),
Expand All @@ -41,7 +42,6 @@ use crate::{Certificate, Error, KeyIdMethod, KeyUsagePurpose, SerialNumber, Sign
/// crl_number: SerialNumber::from(1234),
/// issuing_distribution_point: None,
/// revoked_certs: vec![revoked_cert],
/// alg: &PKCS_ECDSA_P256_SHA256,
/// key_identifier_method: KeyIdMethod::Sha256,
/// };
/// let crl = CertificateRevocationList::from_params(crl).unwrap();
Expand Down Expand Up @@ -74,11 +74,8 @@ impl CertificateRevocationList {
{
return Err(Error::IssuerNotCrlSigner);
}
self.params.serialize_der_with_signer(
self.params.alg,
ca_key,
&ca.params.distinguished_name,
)
self.params
.serialize_der_with_signer(ca_key, &ca.params.distinguished_name)
}
/// Serializes the certificate revocation list (CRL) in ASCII PEM format, signed with
/// the issuing certificate authority's key.
Expand Down Expand Up @@ -175,8 +172,6 @@ pub struct CertificateRevocationListParams {
pub issuing_distribution_point: Option<CrlIssuingDistributionPoint>,
/// A list of zero or more parameters describing revoked certificates included in the CRL.
pub revoked_certs: Vec<RevokedCertParams>,
/// Signature algorithm to use when signing the serialized CRL.
pub alg: &'static SignatureAlgorithm,
/// Method to generate key identifiers from public keys
///
/// Defaults to SHA-256.
Expand All @@ -186,23 +181,22 @@ pub struct CertificateRevocationListParams {
impl CertificateRevocationListParams {
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, sig_alg, issuer, issuer_name)?;
self.write_crl(writer, issuer, issuer_name)?;
Ok::<(), Error>(())
})?;

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

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

// Write signature
issuer.sign(&tbs_cert_list_serialized, writer.next())?;
Expand All @@ -214,7 +208,6 @@ impl CertificateRevocationListParams {
fn write_crl(
&self,
writer: DERWriter,
sig_alg: &SignatureAlgorithm,
issuer: &KeyPair,
issuer_name: &DistinguishedName,
) -> Result<(), Error> {
Expand All @@ -234,7 +227,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
sig_alg.write_alg_ident(writer.next());
issuer.alg.write_alg_ident(writer.next());

// Write issuer.
// RFC 5280 §5.1.2.3:
Expand Down
1 change: 0 additions & 1 deletion rcgen/src/csr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ impl CertificateSigningRequestParams {

let info = &csr.certification_request_info;
let mut params = CertificateParams::default();
params.alg = alg;
params.distinguished_name = DistinguishedName::from_name(&info.subject)?;
let raw = info.subject_pki.subject_public_key.data.to_vec();

Expand Down
8 changes: 0 additions & 8 deletions rcgen/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ pub enum Error {
RingUnspecified,
/// The `ring` library rejected the key upon loading
RingKeyRejected(String),
/// The provided certificate's signature algorithm
/// is incompatible with the given key pair
CertificateKeyPairMismatch,
/// Time conversion related errors
Time,
#[cfg(feature = "pem")]
Expand Down Expand Up @@ -75,11 +72,6 @@ impl fmt::Display for Error {
UnsupportedExtension => write!(f, "Unsupported extension requested in CSR")?,
RingUnspecified => write!(f, "Unspecified ring error")?,
RingKeyRejected(e) => write!(f, "Key rejected by ring: {}", e)?,
CertificateKeyPairMismatch => write!(
f,
"The provided certificate's signature \
algorithm is incompatible with the given key pair"
)?,

Time => write!(f, "Time error")?,
RemoteKeyError => write!(f, "Remote key error")?,
Expand Down
97 changes: 43 additions & 54 deletions rcgen/src/key_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,49 @@ pub struct KeyPair {
}

impl KeyPair {
/// Generate a new random [`PKCS_ECDSA_P256_SHA256`] key pair
pub fn generate() -> Result<Self, Error> {
Self::generate_for(&PKCS_ECDSA_P256_SHA256)
}

/// Generate a new random key pair for the specified signature algorithm
///
/// If you're not sure which algorithm to use, [`PKCS_ECDSA_P256_SHA256`] is a good choice.
pub fn generate_for(alg: &'static SignatureAlgorithm) -> Result<Self, Error> {
let rng = &SystemRandom::new();

match alg.sign_alg {
SignAlgo::EcDsa(sign_alg) => {
let key_pair_doc = EcdsaKeyPair::generate_pkcs8(sign_alg, rng)._err()?;
let key_pair_serialized = key_pair_doc.as_ref().to_vec();

let key_pair = ecdsa_from_pkcs8(&sign_alg, &&key_pair_doc.as_ref(), rng).unwrap();
Ok(KeyPair {
kind: KeyPairKind::Ec(key_pair),
alg,
serialized_der: key_pair_serialized,
})
},
SignAlgo::EdDsa(_sign_alg) => {
let key_pair_doc = Ed25519KeyPair::generate_pkcs8(rng)._err()?;
let key_pair_serialized = key_pair_doc.as_ref().to_vec();

let key_pair = Ed25519KeyPair::from_pkcs8(&&key_pair_doc.as_ref()).unwrap();
Ok(KeyPair {
kind: KeyPairKind::Ed(key_pair),
alg,
serialized_der: key_pair_serialized,
})
},
// Ring doesn't have RSA key generation yet:
// https://github.com/briansmith/ring/issues/219
// https://github.com/briansmith/ring/pull/733
// Nor does aws-lc-rs:
// https://github.com/aws/aws-lc-rs/issues/296
SignAlgo::Rsa() => Err(Error::KeyGenerationUnavailable),
}
}

/// Parses the key pair from the DER format
///
/// Equivalent to using the [`TryFrom`] implementation.
Expand Down Expand Up @@ -177,60 +220,6 @@ impl KeyPair {
Ok((kind, alg))
}

/// Generate a new random key pair for the specified signature algorithm
pub fn generate(alg: &'static SignatureAlgorithm) -> Result<Self, Error> {
let rng = &SystemRandom::new();

match alg.sign_alg {
SignAlgo::EcDsa(sign_alg) => {
let key_pair_doc = EcdsaKeyPair::generate_pkcs8(sign_alg, rng)._err()?;
let key_pair_serialized = key_pair_doc.as_ref().to_vec();

let key_pair = ecdsa_from_pkcs8(&sign_alg, &&key_pair_doc.as_ref(), rng).unwrap();
Ok(KeyPair {
kind: KeyPairKind::Ec(key_pair),
alg,
serialized_der: key_pair_serialized,
})
},
SignAlgo::EdDsa(_sign_alg) => {
let key_pair_doc = Ed25519KeyPair::generate_pkcs8(rng)._err()?;
let key_pair_serialized = key_pair_doc.as_ref().to_vec();

let key_pair = Ed25519KeyPair::from_pkcs8(&&key_pair_doc.as_ref()).unwrap();
Ok(KeyPair {
kind: KeyPairKind::Ed(key_pair),
alg,
serialized_der: key_pair_serialized,
})
},
// Ring doesn't have RSA key generation yet:
// https://github.com/briansmith/ring/issues/219
// https://github.com/briansmith/ring/pull/733
SignAlgo::Rsa() => Err(Error::KeyGenerationUnavailable),
}
}

/// 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