Domani connects your domain to whatever you want to host on it, all with no account needed
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.
 
 
 
 
domani/src/domain/tls/certificate.rs

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);
}
}