#![forbid(unsafe_code)]
#![deny(missing_docs)]
extern crate yasna;
extern crate ring;
extern crate pem;
extern crate untrusted;
extern crate chrono;
extern crate bit_vec;
use yasna::Tag;
use yasna::models::ObjectIdentifier;
use pem::Pem;
use ring::signature::EcdsaKeyPair;
use ring::rand::SystemRandom;
use ring::signature::KeyPair;
use untrusted::Input;
use ring::signature::ECDSA_P256_SHA256_ASN1_SIGNING as KALG;
use yasna::DERWriter;
use yasna::models::GeneralizedTime;
use chrono::{DateTime, Timelike};
use chrono::{NaiveDate, Utc};
use std::collections::HashMap;
use bit_vec::BitVec;
pub struct Certificate {
params :CertificateParams,
key_pair :EcdsaKeyPair,
key_pair_serialized :Vec<u8>,
}
pub fn generate_simple_self_signed(subject_alt_names :impl Into<Vec<String>>) -> Certificate {
let not_before = date_time_ymd(1975, 01, 01);
let not_after = date_time_ymd(4096, 01, 01);
let mut distinguished_name = DistinguishedName::new();
distinguished_name.push(DnType::CommonName, "rcgen self signed cert");
let params = CertificateParams {
alg : PKCS_WITH_SHA256_WITH_ECDSA_ENCRYPTION,
not_before,
not_after,
serial_number : None,
subject_alt_names : subject_alt_names.into(),
distinguished_name,
};
Certificate::from_params(params)
}
const OID_COUNTRY_NAME :&[u64] = &[2, 5, 4, 6];
const OID_ORG_NAME :&[u64] = &[2, 5, 4, 10];
const OID_COMMON_NAME :&[u64] = &[2, 5, 4, 3];
const OID_EC_PUBLIC_KEY :&[u64] = &[1, 2, 840, 10045, 2, 1];
const OID_EC_SECP_256_R1 :&[u64] = &[1, 2, 840, 10045, 3, 1, 7];
const OID_SUBJECT_ALT_NAME :&[u64] = &[2, 5, 29, 17];
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[allow(missing_docs)]
pub enum DnType {
CountryName,
OrganizationName,
CommonName,
#[doc(hidden)]
_Nonexhaustive,
}
impl DnType {
fn to_oid(&self) -> ObjectIdentifier {
let sl = match self {
DnType::CountryName => OID_COUNTRY_NAME,
DnType::OrganizationName => OID_ORG_NAME,
DnType::CommonName => OID_COMMON_NAME,
DnType::_Nonexhaustive => unimplemented!(),
};
ObjectIdentifier::from_slice(sl)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct DistinguishedName {
entries :HashMap<DnType, String>,
}
impl DistinguishedName {
pub fn new() -> Self {
Self {
entries : HashMap::new(),
}
}
pub fn push(&mut self, ty :DnType, s :impl Into<String>) {
self.entries.insert(ty, s.into());
}
}
#[allow(missing_docs)]
pub struct CertificateParams {
pub alg :SignatureAlgorithm,
pub not_before :DateTime<Utc>,
pub not_after :DateTime<Utc>,
pub serial_number :Option<u64>,
pub subject_alt_names :Vec<String>,
pub distinguished_name :DistinguishedName,
}
pub fn date_time_ymd(year :i32, month :u32, day :u32) -> DateTime<Utc> {
let naive_dt = NaiveDate::from_ymd(year, month, day).and_hms_milli(0, 0, 0, 0);
DateTime::<Utc>::from_utc(naive_dt, Utc)
}
fn dt_to_generalized(dt :&DateTime<Utc>) -> GeneralizedTime {
let mut date_time = *dt;
let nanos = if date_time.nanosecond() >= 1_000_000 {
1_000_000
} else {
0
};
date_time = date_time.with_nanosecond(nanos).unwrap();
GeneralizedTime::from_datetime::<Utc>(&date_time)
}
impl Certificate {
pub fn from_params(params :CertificateParams) -> Self {
let system_random = SystemRandom::new();
let key_pair_doc = EcdsaKeyPair::generate_pkcs8(&KALG, &system_random).unwrap();
let key_pair_serialized = key_pair_doc.as_ref().to_vec();
let key_pair = EcdsaKeyPair::from_pkcs8(&KALG, Input::from(&&key_pair_doc.as_ref())).unwrap();
Certificate {
params,
key_pair,
key_pair_serialized,
}
}
fn write_name(&self, writer :DERWriter) {
writer.write_sequence(|writer| {
writer.next().write_set(|writer| {
for (ty, content) in self.params.distinguished_name.entries.iter() {
writer.next().write_sequence(|writer| {
writer.next().write_oid(&ty.to_oid());
writer.next().write_utf8_string(content);
});
}
});
});
}
fn write_cert(&self, writer :DERWriter) {
writer.write_sequence(|writer| {
writer.next().write_tagged(Tag::context(0), |writer| {
writer.write_u8(2);
});
let serial = self.params.serial_number.unwrap_or(42);
writer.next().write_u64(serial);
writer.next().write_sequence(|writer| {
writer.next().write_oid(&self.params.alg.oid());
});
self.write_name(writer.next());
writer.next().write_sequence(|writer| {
let nb_gt = dt_to_generalized(&self.params.not_before);
writer.next().write_generalized_time(&nb_gt);
let na_gt = dt_to_generalized(&self.params.not_after);
writer.next().write_generalized_time(&na_gt);
});
self.write_name(writer.next());
writer.next().write_sequence(|writer| {
writer.next().write_sequence(|writer| {
let oid = ObjectIdentifier::from_slice(OID_EC_PUBLIC_KEY);
writer.next().write_oid(&oid);
let oid = ObjectIdentifier::from_slice(OID_EC_SECP_256_R1);
writer.next().write_oid(&oid);
});
let public_key = &self.key_pair.public_key().as_ref();
let pkbs = BitVec::from_bytes(&public_key);
writer.next().write_bitvec(&pkbs);
});
writer.next().write_tagged(Tag::context(3), |writer| {
writer.write_sequence(|writer| {
writer.next().write_sequence(|writer| {
let oid = ObjectIdentifier::from_slice(OID_SUBJECT_ALT_NAME);
writer.next().write_oid(&oid);
let bytes = yasna::construct_der(|writer| {
writer.write_sequence(|writer|{
for san in self.params.subject_alt_names.iter() {
writer.next().write_tagged_implicit(Tag::context(2), |writer| {
writer.write_utf8_string(san);
});
}
});
});
writer.next().write_bytes(&bytes);
});
});
});
})
}
pub fn serialize_der(&self) -> Vec<u8> {
yasna::construct_der(|writer| {
writer.write_sequence(|writer| {
let tbs_cert_list_serialized = yasna::construct_der(|writer| {
self.write_cert(writer);
});
writer.next().write_der(&tbs_cert_list_serialized);
writer.next().write_sequence(|writer| {
writer.next().write_oid(&self.params.alg.oid());
});
let cl_input = Input::from(&tbs_cert_list_serialized);
let system_random = SystemRandom::new();
let signature = self.key_pair.sign(&system_random, cl_input).unwrap();
let sig = BitVec::from_bytes(&signature.as_ref());
writer.next().write_bitvec(&sig);
})
})
}
pub fn serialize_pem(&self) -> String {
let p = Pem {
tag : "CERTIFICATE".to_string(),
contents : self.serialize_der(),
};
pem::encode(&p)
}
pub fn serialize_private_key_der(&self) -> Vec<u8> {
self.key_pair_serialized.clone()
}
pub fn serialize_private_key_pem(&self) -> String {
let p = Pem {
tag : "PRIVATE KEY".to_string(),
contents : self.serialize_private_key_der(),
};
pem::encode(&p)
}
}
pub struct SignatureAlgorithm {
oid_components : &'static [u64],
}
pub const PKCS_WITH_SHA256_WITH_ECDSA_ENCRYPTION :SignatureAlgorithm = SignatureAlgorithm {
oid_components : &[1, 2, 840, 10045, 4, 3, 2],
};
impl SignatureAlgorithm {
fn oid(&self) -> ObjectIdentifier {
ObjectIdentifier::from_slice(self.oid_components)
}
}