use crate::xdsa;
use const_oid::ObjectIdentifier;
use der::Encode;
use der::asn1::{BitString, OctetString, SetOfVec, UtcTime};
use sha1::{Digest, Sha1};
use std::error::Error;
use x509_cert::attr::AttributeTypeAndValue;
use x509_cert::certificate::{CertificateInner, TbsCertificateInner, Version};
use x509_cert::ext::AsExtension;
use x509_cert::ext::pkix::{
AuthorityKeyIdentifier, BasicConstraints, KeyUsage, KeyUsages, SubjectKeyIdentifier,
};
use x509_cert::name::{Name, RdnSequence, RelativeDistinguishedName};
use x509_cert::serial_number::SerialNumber;
use x509_cert::spki::{AlgorithmIdentifierOwned, SubjectPublicKeyInfoOwned};
use x509_cert::time::{Time, Validity};
const OID_CN: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.5.4.3");
pub trait Subject {
type Bytes: AsRef<[u8]>;
fn to_bytes(&self) -> Self::Bytes;
fn algorithm_oid(&self) -> ObjectIdentifier;
}
pub struct Params<'a> {
pub subject_name: &'a str,
pub issuer_name: &'a str,
pub not_before: u64,
pub not_after: u64,
pub is_ca: bool,
pub path_len: Option<u8>,
}
fn make_cn_name(cn: &str) -> Result<Name, Box<dyn Error>> {
let cn_value = der::asn1::Any::new(der::Tag::Utf8String, cn.as_bytes())?;
let attr = AttributeTypeAndValue {
oid: OID_CN,
value: cn_value,
};
let mut rdn_set = SetOfVec::new();
rdn_set.insert(attr)?;
let rdn = RelativeDistinguishedName::from(rdn_set);
Ok(RdnSequence(vec![rdn]))
}
fn make_ski(public_key: &[u8]) -> SubjectKeyIdentifier {
let mut hasher = Sha1::new();
hasher.update(public_key);
let hash = hasher.finalize();
SubjectKeyIdentifier(OctetString::new(&hash[..]).unwrap())
}
fn make_aki(public_key: &[u8]) -> AuthorityKeyIdentifier {
let mut hasher = Sha1::new();
hasher.update(public_key);
let hash = hasher.finalize();
AuthorityKeyIdentifier {
key_identifier: Some(OctetString::new(&hash[..]).unwrap()),
authority_cert_issuer: None,
authority_cert_serial_number: None,
}
}
fn make_key_usage(is_ca: bool) -> KeyUsage {
if is_ca {
KeyUsage(KeyUsages::KeyCertSign | KeyUsages::CRLSign)
} else {
KeyUsage(KeyUsages::DigitalSignature.into())
}
}
pub fn new<S: Subject>(
subject: &S,
issuer: &xdsa::SecretKey,
params: &Params,
) -> Result<CertificateInner, Box<dyn Error>> {
let composite_alg = AlgorithmIdentifierOwned {
oid: xdsa::OID,
parameters: None,
};
let mut serial_bytes = [0u8; 16];
getrandom::fill(&mut serial_bytes).unwrap();
serial_bytes[0] &= 0x7F; let serial_number = SerialNumber::new(&serial_bytes)?;
let subject_name = make_cn_name(params.subject_name)?;
let ski = make_ski(subject.to_bytes().as_ref());
let aki = make_aki(&issuer.public_key().to_bytes());
let bc = BasicConstraints {
ca: params.is_ca,
path_len_constraint: params.path_len,
};
let ku = make_key_usage(params.is_ca);
let extensions = vec![
bc.to_extension(&subject_name, &[])?,
ku.to_extension(&subject_name, &[])?,
ski.to_extension(&subject_name, &[])?,
aki.to_extension(&subject_name, &[])?,
];
let not_before =
UtcTime::from_unix_duration(std::time::Duration::from_secs(params.not_before))?;
let not_after = UtcTime::from_unix_duration(std::time::Duration::from_secs(params.not_after))?;
let tbs_certificate = TbsCertificateInner {
version: Version::V3,
serial_number,
signature: composite_alg.clone(),
issuer: make_cn_name(params.issuer_name)?,
validity: Validity {
not_before: Time::UtcTime(not_before),
not_after: Time::UtcTime(not_after),
},
subject: subject_name,
subject_public_key_info: SubjectPublicKeyInfoOwned {
algorithm: AlgorithmIdentifierOwned {
oid: subject.algorithm_oid(),
parameters: None,
},
subject_public_key: BitString::from_bytes(subject.to_bytes().as_ref())?,
},
issuer_unique_id: None,
subject_unique_id: None,
extensions: Some(extensions),
};
let tbs_der = tbs_certificate.to_der()?;
let signature = issuer.sign(&tbs_der);
Ok(CertificateInner {
tbs_certificate,
signature_algorithm: composite_alg,
signature: BitString::from_bytes(&signature.to_bytes())?,
})
}