You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
174 lines
4.8 KiB
174 lines
4.8 KiB
use crate::domain;
|
|
use crate::domain::tls::PrivateKey;
|
|
use crate::error::unexpected::{self, Mappable};
|
|
|
|
use std::convert::{From, TryFrom};
|
|
use std::fmt;
|
|
use std::str::FromStr;
|
|
|
|
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
|
|
|
#[derive(Debug, Clone, PartialEq, DeserializeFromStr, SerializeDisplay)]
|
|
/// DER-encoded X.509, like rustls::Certificate.
|
|
pub struct Certificate(Vec<u8>);
|
|
|
|
impl Certificate {
|
|
pub fn new_self_signed(
|
|
pkey: &PrivateKey,
|
|
domain: &domain::Name,
|
|
) -> unexpected::Result<Certificate> {
|
|
use openssl::asn1::*;
|
|
use openssl::pkey::*;
|
|
use openssl::x509::extension::SubjectAlternativeName;
|
|
use openssl::x509::*;
|
|
|
|
let name = {
|
|
let mut builder = X509Name::builder().expect("initializing x509 name builder");
|
|
builder
|
|
.append_entry_by_text("CN", domain.as_str())
|
|
.or_unexpected_while("adding CN")?;
|
|
builder.build()
|
|
};
|
|
|
|
let mut builder = X509::builder().expect("initializing x509 builder");
|
|
builder
|
|
.set_subject_name(&name)
|
|
.or_unexpected_while("setting subject name")?;
|
|
|
|
// 1970/01/01
|
|
let not_before = Asn1Time::from_unix(0).expect("initializing not_before");
|
|
builder
|
|
.set_not_before(not_before.as_ref())
|
|
.or_unexpected_while("setting not_before")?;
|
|
|
|
// 9999/07/23
|
|
let not_after = Asn1Time::from_str("99990723000000Z").expect("initializing not_after");
|
|
builder
|
|
.set_not_after(not_after.as_ref())
|
|
.or_unexpected_while("setting not_after")?;
|
|
|
|
// Add domain as SAN
|
|
let san_extension = {
|
|
let mut san = SubjectAlternativeName::new();
|
|
san.dns(domain.as_str());
|
|
san.build(&builder.x509v3_context(None, None))
|
|
.or_unexpected_while("building SAN")?
|
|
};
|
|
|
|
builder
|
|
.append_extension(san_extension)
|
|
.or_unexpected_while("appending SAN")?;
|
|
|
|
let pkey: PKey<Private> = pkey.try_into().or_unexpected_while("converting PKey")?;
|
|
|
|
builder
|
|
.set_pubkey(pkey.as_ref())
|
|
.or_unexpected_while("setting pubkey")?;
|
|
|
|
builder
|
|
.sign(pkey.as_ref(), openssl::hash::MessageDigest::sha256())
|
|
.or_unexpected_while("signing with private key")?;
|
|
|
|
let cert = builder.build();
|
|
|
|
cert.as_ref()
|
|
.try_into()
|
|
.or_unexpected_while("converting to cert")
|
|
}
|
|
}
|
|
|
|
impl FromStr for Certificate {
|
|
type Err = pem::PemError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(Certificate(pem::parse(s)?.into_contents()))
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Certificate {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
pem::Pem::new("CERTIFICATE", self.0.clone()).fmt(f)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&openssl::x509::X509Ref> for Certificate {
|
|
type Error = openssl::error::ErrorStack;
|
|
|
|
fn try_from(c: &openssl::x509::X509Ref) -> Result<Self, Self::Error> {
|
|
Ok(Certificate(c.to_der()?))
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&Certificate> for openssl::x509::X509 {
|
|
type Error = openssl::error::ErrorStack;
|
|
|
|
fn try_from(c: &Certificate) -> Result<Self, Self::Error> {
|
|
openssl::x509::X509::from_der(&c.0)
|
|
}
|
|
}
|
|
|
|
impl From<Certificate> for rustls::Certificate {
|
|
fn from(c: Certificate) -> Self {
|
|
rustls::Certificate(c.0)
|
|
}
|
|
}
|
|
|
|
pub struct CertificateChain(Vec<Certificate>);
|
|
|
|
impl FromStr for CertificateChain {
|
|
type Err = pem::PemError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(CertificateChain(
|
|
pem::parse_many(s)?
|
|
.into_iter()
|
|
.map(|s| Certificate(s.into_contents()))
|
|
.collect(),
|
|
))
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for CertificateChain {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
for cert in &self.0 {
|
|
pem::Pem::new("CERTIFICATE", cert.0.clone()).fmt(f)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl From<CertificateChain> for Vec<Certificate> {
|
|
fn from(c: CertificateChain) -> Self {
|
|
c.0
|
|
}
|
|
}
|
|
|
|
impl From<Vec<Certificate>> for CertificateChain {
|
|
fn from(v: Vec<Certificate>) -> Self {
|
|
Self(v)
|
|
}
|
|
}
|
|
|
|
impl std::iter::IntoIterator for CertificateChain {
|
|
type Item = Certificate;
|
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
self.0.into_iter()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::domain;
|
|
use crate::domain::tls::PrivateKey;
|
|
use std::str::FromStr;
|
|
|
|
#[test]
|
|
fn make_cert() {
|
|
let pkey = PrivateKey::new();
|
|
let domain = domain::Name::from_str("foo.com").expect("domain name parsed");
|
|
let cert = Certificate::new_self_signed(&pkey, &domain).expect("cert created");
|
|
println!("{}", cert);
|
|
}
|
|
}
|
|
|