use std::fmt;
use x509_cert::der::{
asn1::{Ia5StringRef, SetOfVec},
Encode, FixedTag,
};
use x509_cert::ext::pkix;
use x509_cert::ext::AsExtension;
use x509_cert::spki::SignatureBitStringEncoding;
const PEM_TAG_CSR: &str = "CERTIFICATE REQUEST";
#[derive(Debug)]
pub enum RequestedSubjectName {
Dns(String),
}
impl fmt::Display for RequestedSubjectName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
RequestedSubjectName::Dns(dns) => write!(f, "DNS:{dns}"),
}
}
}
impl From<String> for RequestedSubjectName {
fn from(value: String) -> Self {
RequestedSubjectName::Dns(value)
}
}
impl From<&str> for RequestedSubjectName {
fn from(value: &str) -> Self {
Self::Dns(value.to_owned())
}
}
pub trait DynSubjectPublicKeyInfoOwned {
fn subject_public_key_info(&self) -> x509_cert::spki::SubjectPublicKeyInfoOwned;
}
impl<S> DynSubjectPublicKeyInfoOwned for S
where
S: x509_cert::spki::EncodePublicKey,
{
fn subject_public_key_info(&self) -> x509_cert::spki::SubjectPublicKeyInfoOwned {
self.to_public_key_der().unwrap().decode_msg().unwrap()
}
}
#[derive(Debug, Default)]
pub struct CertificateSigningRequest {
names: Vec<RequestedSubjectName>,
}
impl CertificateSigningRequest {
pub fn new() -> Self {
CertificateSigningRequest { names: Vec::new() }
}
pub fn len(&self) -> usize {
self.names.len()
}
pub fn is_empty(&self) -> bool {
self.names.is_empty()
}
pub fn push<N>(&mut self, name: N)
where
N: Into<RequestedSubjectName>,
{
self.names.push(name.into())
}
pub fn sign<K, S>(self, key: &K) -> SignedCertificateRequest
where
K: signature::Keypair
+ signature::Signer<S>
+ x509_cert::spki::DynSignatureAlgorithmIdentifier,
K::VerifyingKey: DynSubjectPublicKeyInfoOwned,
S: SignatureBitStringEncoding,
{
let verifying_key = key.verifying_key();
let spki = verifying_key.subject_public_key_info();
let san_names: Vec<_> = self
.names
.iter()
.map(|san| match san {
RequestedSubjectName::Dns(dns) => pkix::name::GeneralName::DnsName(
Ia5StringRef::new(dns.as_bytes())
.expect("ia-5 DNS valid names")
.into(),
),
})
.collect();
let san = pkix::SubjectAltName::from(san_names);
let name: x509_cert::name::Name = Default::default();
let extension = san.to_extension(&name, &[]).expect("valid exetnsions");
let extension_der = extension.to_der().unwrap();
let encoded_extensions =
der::asn1::Any::new(x509_cert::ext::Extension::TAG, extension_der).unwrap();
let mut values = SetOfVec::new();
values.insert(encoded_extensions).unwrap();
let attr = x509_cert::attr::Attribute {
oid: const_oid::db::rfc5912::ID_EXTENSION_REQ,
values,
};
let mut attributes = SetOfVec::new();
attributes.insert(attr).unwrap();
let csr_info = x509_cert::request::CertReqInfo {
version: x509_cert::request::Version::V1,
subject: Default::default(),
public_key: spki,
attributes,
};
let csr_target = csr_info.to_der().expect("Valid encoding");
let signature = key.sign(&csr_target);
let csr = x509_cert::request::CertReq {
info: csr_info,
algorithm: key.signature_algorithm_identifier().unwrap(),
signature: signature.to_bitstring().unwrap(),
};
let mut buf = Vec::new();
csr.encode_to_vec(&mut buf).expect("successful encoding");
SignedCertificateRequest(buf.into())
}
}
#[derive(Debug, Clone)]
pub struct SignedCertificateRequest(Box<[u8]>);
impl SignedCertificateRequest {
pub fn to_pem(&self) -> String {
pem_rfc7468::encode_string(PEM_TAG_CSR, base64ct::LineEnding::default(), &self.0)
.expect("valid PEM")
}
}
impl From<Box<[u8]>> for SignedCertificateRequest {
fn from(value: Box<[u8]>) -> Self {
SignedCertificateRequest(value)
}
}
impl From<Vec<u8>> for SignedCertificateRequest {
fn from(value: Vec<u8>) -> Self {
SignedCertificateRequest(value.into_boxed_slice())
}
}
impl From<SignedCertificateRequest> for Box<[u8]> {
fn from(value: SignedCertificateRequest) -> Self {
value.0
}
}
impl<'a> From<&'a [u8]> for SignedCertificateRequest {
fn from(value: &'a [u8]) -> Self {
SignedCertificateRequest(value.into())
}
}
impl AsRef<[u8]> for SignedCertificateRequest {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[cfg(test)]
mod test {
use der::Decode;
use signature::rand_core::OsRng;
use super::*;
#[test]
fn csr_signature_is_der() {
let key: ecdsa::SigningKey<p256::NistP256> = ecdsa::SigningKey::random(&mut OsRng);
let mut csr = CertificateSigningRequest::new();
csr.push("example.com");
let signed = csr
.sign::<::ecdsa::SigningKey<p256::NistP256>, ::ecdsa::der::Signature<p256::NistP256>>(
&key,
);
let csr = x509_cert::request::CertReq::from_der(signed.as_ref()).expect("valid CSR");
let doc = csr.signature.as_bytes().unwrap();
let _ = der::Document::from_der(doc).expect("valid DER");
}
}