use rcgen::{ExtendedKeyUsagePurpose, KeyUsagePurpose};
use crate::signing::SigningKey;
pub struct CertificateRequest {
inner: rcgen::CertificateSigningRequest,
}
impl CertificateRequest {
#[must_use]
pub fn builder() -> CertificateRequestBuilder {
CertificateRequestBuilder::new_with_default_subject()
}
#[must_use]
pub fn as_der(&self) -> &[u8] {
self.inner.der().as_ref()
}
pub fn to_pem(&self) -> Result<String, CertificateRequestPemError> {
Ok(self.inner.pem()?)
}
}
impl std::fmt::Debug for CertificateRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CertificateRequest").finish()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CertificateRequestBuilder {
params: rcgen::CertificateParams,
custom_attributes: Vec<rcgen::Attribute>,
}
impl CertificateRequestBuilder {
pub const DEFAULT_CSR_SUBJECT_CN: &str = "MS-WCCE certificate signing request";
fn new_with_default_subject() -> Self {
let mut params = rcgen::CertificateParams::default();
params.distinguished_name = rcgen::DistinguishedName::new();
params
.distinguished_name
.push(rcgen::DnType::CommonName, Self::DEFAULT_CSR_SUBJECT_CN);
Self {
params,
custom_attributes: Vec::new(),
}
}
pub fn build(
self,
key: &SigningKey,
) -> Result<CertificateRequest, CertificateRequestBuildError> {
let csr = self
.params
.serialize_request_with_attributes(&key.key_rcgen, self.custom_attributes)?;
Ok(CertificateRequest { inner: csr })
}
#[must_use]
pub fn add_key_usage(mut self, key_usage: KeyUsage) -> Self {
self.params.key_usages.push(key_usage.into());
self
}
#[must_use]
pub fn add_extended_key_usage(mut self, extended_key_usage: ExtendedKeyUsage) -> Self {
self.params
.extended_key_usages
.push(extended_key_usage.into());
self
}
#[must_use]
pub fn add_extension(mut self, extension: &impl Extension) -> Self {
let mut ext = rcgen::CustomExtension::from_oid_content(extension.oid(), extension.value());
ext.set_criticality(extension.critical());
self.params.custom_extensions.push(ext);
self
}
#[must_use]
pub fn add_attribute(mut self, attribute: &impl Attribute) -> Self {
self.custom_attributes.push(rcgen::Attribute {
oid: attribute.oid(),
values: attribute.values(),
});
self
}
}
pub trait Attribute {
fn oid(&self) -> &'static [u64];
fn values(&self) -> Vec<u8>;
}
pub trait Extension {
fn oid(&self) -> &[u64];
fn critical(&self) -> bool;
fn value(&self) -> Vec<u8>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyUsage {
DigitalSignature,
ContentCommitment,
KeyEncipherment,
DataEncipherment,
KeyAgreement,
KeyCertSign,
CrlSign,
EncipherOnly,
DecipherOnly,
}
impl From<KeyUsage> for KeyUsagePurpose {
fn from(value: KeyUsage) -> Self {
match value {
KeyUsage::DigitalSignature => Self::DigitalSignature,
KeyUsage::ContentCommitment => Self::ContentCommitment,
KeyUsage::KeyEncipherment => Self::KeyEncipherment,
KeyUsage::DataEncipherment => Self::DataEncipherment,
KeyUsage::KeyAgreement => Self::KeyAgreement,
KeyUsage::KeyCertSign => Self::KeyCertSign,
KeyUsage::CrlSign => Self::CrlSign,
KeyUsage::EncipherOnly => Self::EncipherOnly,
KeyUsage::DecipherOnly => Self::DecipherOnly,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExtendedKeyUsage {
Any,
ServerAuth,
ClientAuth,
CodeSigning,
EmailProtection,
TimeStamping,
OcspSigning,
SmartCardLogon,
Other(Vec<u64>),
}
impl ExtendedKeyUsage {
const SMARTCARD_LOGON_OID: &[u64] = &[1, 3, 6, 1, 4, 1, 311, 20, 2, 2];
}
impl From<ExtendedKeyUsage> for ExtendedKeyUsagePurpose {
fn from(value: ExtendedKeyUsage) -> Self {
match value {
ExtendedKeyUsage::Any => Self::Any,
ExtendedKeyUsage::ServerAuth => Self::ServerAuth,
ExtendedKeyUsage::ClientAuth => Self::ClientAuth,
ExtendedKeyUsage::CodeSigning => Self::CodeSigning,
ExtendedKeyUsage::EmailProtection => Self::EmailProtection,
ExtendedKeyUsage::TimeStamping => Self::TimeStamping,
ExtendedKeyUsage::OcspSigning => Self::OcspSigning,
ExtendedKeyUsage::SmartCardLogon => {
Self::Other(ExtendedKeyUsage::SMARTCARD_LOGON_OID.into())
}
ExtendedKeyUsage::Other(vec) => Self::Other(vec),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct CertificateRequestBuildError(rcgen::Error);
impl std::fmt::Display for CertificateRequestBuildError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"failed to build and sign certificate request: {}",
self.0
)
}
}
impl std::error::Error for CertificateRequestBuildError {}
impl From<rcgen::Error> for CertificateRequestBuildError {
fn from(value: rcgen::Error) -> Self {
Self(value)
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct CertificateRequestPemError(rcgen::Error);
impl std::fmt::Display for CertificateRequestPemError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "failed to serialize PEM certificate request: {}", self.0)
}
}
impl std::error::Error for CertificateRequestPemError {}
impl From<rcgen::Error> for CertificateRequestPemError {
fn from(value: rcgen::Error) -> Self {
Self(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_certificate_request_builder_build() {
assert!(CertificateRequest::builder()
.build(&SigningKey::new_testing_key())
.is_ok());
assert!(CertificateRequest::builder()
.add_key_usage(KeyUsage::DigitalSignature)
.add_key_usage(KeyUsage::KeyEncipherment)
.add_extended_key_usage(ExtendedKeyUsage::ClientAuth)
.build(&SigningKey::new_testing_key())
.is_ok());
}
}