use {
crate::{
asn1time::Time, rfc3280::Name, rfc5280, InMemorySigningKeyPair, KeyAlgorithm,
SignatureAlgorithm, X509CertificateError as Error,
},
bcder::{
decode::Constructed,
encode::Values,
int::Integer,
string::{BitString, OctetString},
ConstOid, Mode, Oid,
},
bytes::Bytes,
chrono::{Duration, Utc},
ring::signature,
std::{
cmp::Ordering,
collections::HashSet,
convert::TryFrom,
fmt::{Debug, Formatter},
hash::{Hash, Hasher},
io::Write,
ops::{Deref, DerefMut},
},
};
const OID_EXTENSION_KEY_USAGE: ConstOid = Oid(&[85, 29, 15]);
const OID_EXTENSION_BASIC_CONSTRAINTS: ConstOid = Oid(&[85, 29, 19]);
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct X509Certificate(rfc5280::Certificate);
impl X509Certificate {
pub fn from_der(data: impl AsRef<[u8]>) -> Result<Self, Error> {
let cert = Constructed::decode(data.as_ref(), Mode::Der, |cons| {
rfc5280::Certificate::take_from(cons)
})?;
Ok(Self(cert))
}
pub fn from_ber(data: impl AsRef<[u8]>) -> Result<Self, Error> {
let cert = Constructed::decode(data.as_ref(), Mode::Ber, |cons| {
rfc5280::Certificate::take_from(cons)
})?;
Ok(Self(cert))
}
pub fn from_pem(data: impl AsRef<[u8]>) -> Result<Self, Error> {
let data = pem::parse(data.as_ref()).map_err(Error::PemDecode)?;
Self::from_der(&data.contents)
}
pub fn from_pem_multiple(data: impl AsRef<[u8]>) -> Result<Vec<Self>, Error> {
Self::from_pem_multiple_tags(data, &["CERTIFICATE"])
}
pub fn from_pem_multiple_tags(
data: impl AsRef<[u8]>,
tags: &[&str],
) -> Result<Vec<Self>, Error> {
let pem = pem::parse_many(data.as_ref());
pem.into_iter()
.filter(|pem| tags.contains(&pem.tag.as_str()))
.map(|pem| Self::from_der(&pem.contents))
.collect::<Result<_, _>>()
}
pub fn serial_number_asn1(&self) -> &Integer {
&self.0.tbs_certificate.serial_number
}
pub fn subject_name(&self) -> &Name {
&self.0.tbs_certificate.subject
}
pub fn subject_common_name(&self) -> Option<String> {
self.0
.tbs_certificate
.subject
.iter_common_name()
.next()
.and_then(|cn| cn.to_string().ok())
}
pub fn issuer_name(&self) -> &Name {
&self.0.tbs_certificate.issuer
}
pub fn encode_der_to(&self, fh: &mut impl Write) -> Result<(), std::io::Error> {
self.0.encode_ref().write_encoded(Mode::Der, fh)
}
pub fn encode_ber_to(&self, fh: &mut impl Write) -> Result<(), std::io::Error> {
self.0.encode_ref().write_encoded(Mode::Ber, fh)
}
pub fn encode_der(&self) -> Result<Vec<u8>, std::io::Error> {
let mut buffer = Vec::<u8>::new();
self.encode_der_to(&mut buffer)?;
Ok(buffer)
}
pub fn encode_ber(&self) -> Result<Vec<u8>, std::io::Error> {
let mut buffer = Vec::<u8>::new();
self.encode_ber_to(&mut buffer)?;
Ok(buffer)
}
pub fn write_pem(&self, fh: &mut impl Write) -> Result<(), std::io::Error> {
let encoded = pem::encode(&pem::Pem {
tag: "CERTIFICATE".to_string(),
contents: self.encode_der()?,
});
fh.write_all(encoded.as_bytes())
}
pub fn encode_pem(&self) -> Result<String, std::io::Error> {
Ok(pem::encode(&pem::Pem {
tag: "CERTIFICATE".to_string(),
contents: self.encode_der()?,
}))
}
pub fn key_algorithm(&self) -> Option<KeyAlgorithm> {
KeyAlgorithm::try_from(&self.0.tbs_certificate.subject_public_key_info.algorithm).ok()
}
pub fn key_algorithm_oid(&self) -> &Oid {
&self
.0
.tbs_certificate
.subject_public_key_info
.algorithm
.algorithm
}
pub fn signature_algorithm(&self) -> Option<SignatureAlgorithm> {
SignatureAlgorithm::try_from(&self.0.signature_algorithm).ok()
}
pub fn signature_algorithm_oid(&self) -> &Oid {
&self.0.tbs_certificate.signature.algorithm
}
pub fn public_key_data(&self) -> Bytes {
self.0
.tbs_certificate
.subject_public_key_info
.subject_public_key
.octet_bytes()
}
pub fn compare_issuer(&self, other: &Self) -> Ordering {
if self.0.tbs_certificate.subject == self.0.tbs_certificate.issuer {
Ordering::Equal
} else if self.0.tbs_certificate.issuer == other.0.tbs_certificate.subject {
Ordering::Greater
} else if self.0.tbs_certificate.subject == other.0.tbs_certificate.issuer {
Ordering::Less
} else {
Ordering::Equal
}
}
pub fn subject_is_issuer(&self) -> bool {
self.0.tbs_certificate.subject == self.0.tbs_certificate.issuer
}
}
impl From<rfc5280::Certificate> for X509Certificate {
fn from(v: rfc5280::Certificate) -> Self {
Self(v)
}
}
impl From<X509Certificate> for rfc5280::Certificate {
fn from(v: X509Certificate) -> Self {
v.0
}
}
impl AsRef<rfc5280::Certificate> for X509Certificate {
fn as_ref(&self) -> &rfc5280::Certificate {
&self.0
}
}
impl AsMut<rfc5280::Certificate> for X509Certificate {
fn as_mut(&mut self) -> &mut rfc5280::Certificate {
&mut self.0
}
}
#[derive(Clone, Eq, PartialEq)]
enum OriginalData {
Ber(Vec<u8>),
Der(Vec<u8>),
}
impl Debug for OriginalData {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"{}({})",
match self {
Self::Ber(_) => "Ber",
Self::Der(_) => "Der",
},
match self {
Self::Ber(data) => hex::encode(data),
Self::Der(data) => hex::encode(data),
}
))
}
}
#[derive(Clone, Debug)]
pub struct CapturedX509Certificate {
original: OriginalData,
inner: X509Certificate,
}
impl CapturedX509Certificate {
pub fn from_der(data: impl Into<Vec<u8>>) -> Result<Self, Error> {
let der_data = data.into();
let inner = X509Certificate::from_der(&der_data)?;
Ok(Self {
original: OriginalData::Der(der_data),
inner,
})
}
pub fn from_ber(data: impl Into<Vec<u8>>) -> Result<Self, Error> {
let data = data.into();
let inner = X509Certificate::from_ber(&data)?;
Ok(Self {
original: OriginalData::Ber(data),
inner,
})
}
pub fn from_pem(data: impl AsRef<[u8]>) -> Result<Self, Error> {
let data = pem::parse(data.as_ref()).map_err(Error::PemDecode)?;
Self::from_der(data.contents)
}
pub fn from_pem_multiple(data: impl AsRef<[u8]>) -> Result<Vec<Self>, Error> {
Self::from_pem_multiple_tags(data, &["CERTIFICATE"])
}
pub fn from_pem_multiple_tags(
data: impl AsRef<[u8]>,
tags: &[&str],
) -> Result<Vec<Self>, Error> {
let pem = pem::parse_many(data.as_ref());
pem.into_iter()
.filter(|pem| tags.contains(&pem.tag.as_str()))
.map(|pem| Self::from_der(pem.contents))
.collect::<Result<_, _>>()
}
pub fn constructed_data(&self) -> &[u8] {
match &self.original {
OriginalData::Ber(data) => data,
OriginalData::Der(data) => data,
}
}
pub fn encode_pem(&self) -> String {
pem::encode(&pem::Pem {
tag: "CERTIFICATE".to_string(),
contents: self.constructed_data().to_vec(),
})
}
pub fn verify_signed_by_certificate(
&self,
other: impl AsRef<X509Certificate>,
) -> Result<(), Error> {
let public_key = other
.as_ref()
.0
.tbs_certificate
.subject_public_key_info
.subject_public_key
.octet_bytes();
self.verify_signed_by_public_key(public_key)
}
pub fn verify_signed_by_public_key(
&self,
public_key_data: impl AsRef<[u8]>,
) -> Result<(), Error> {
let this_cert = match &self.original {
OriginalData::Ber(data) => X509Certificate::from_ber(data),
OriginalData::Der(data) => X509Certificate::from_der(data),
}
.expect("certificate re-parse should never fail");
let signed_data = this_cert
.0
.tbs_certificate
.raw_data
.as_ref()
.expect("original certificate data should have persisted as part of re-parse");
let signature = this_cert.0.signature.octet_bytes();
let key_algorithm = KeyAlgorithm::try_from(
&this_cert
.0
.tbs_certificate
.subject_public_key_info
.algorithm,
)?;
let signature_algorithm = SignatureAlgorithm::try_from(&this_cert.0.signature_algorithm)?;
let verify_algorithm = signature_algorithm.resolve_verification_algorithm(key_algorithm)?;
let public_key = signature::UnparsedPublicKey::new(verify_algorithm, public_key_data);
public_key
.verify(&signed_data, &signature)
.map_err(|_| Error::CertificateSignatureVerificationFailed)
}
pub fn find_signing_certificate<'a>(
&self,
mut certs: impl Iterator<Item = &'a Self>,
) -> Option<&'a Self> {
certs.find(|candidate| self.verify_signed_by_certificate(candidate).is_ok())
}
pub fn resolve_signing_chain<'a>(
&self,
certs: impl Iterator<Item = &'a Self>,
) -> Vec<&'a Self> {
#[allow(clippy::mutable_key_type)]
let mut seen = HashSet::new();
let mut remaining = vec![];
for cert in certs {
if cert == self || seen.contains(cert) {
continue;
} else {
remaining.push(cert);
seen.insert(cert);
}
}
drop(seen);
let mut chain = vec![];
let mut last_cert = self;
while let Some(issuer) = last_cert.find_signing_certificate(remaining.iter().copied()) {
chain.push(issuer);
last_cert = issuer;
remaining = remaining
.drain(..)
.filter(|cert| *cert != issuer)
.collect::<Vec<_>>();
}
chain
}
}
impl PartialEq for CapturedX509Certificate {
fn eq(&self, other: &Self) -> bool {
self.constructed_data() == other.constructed_data()
}
}
impl Eq for CapturedX509Certificate {}
impl Hash for CapturedX509Certificate {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(self.constructed_data());
}
}
impl Deref for CapturedX509Certificate {
type Target = X509Certificate;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl AsRef<X509Certificate> for CapturedX509Certificate {
fn as_ref(&self) -> &X509Certificate {
&self.inner
}
}
impl AsRef<rfc5280::Certificate> for CapturedX509Certificate {
fn as_ref(&self) -> &rfc5280::Certificate {
self.inner.as_ref()
}
}
impl TryFrom<&X509Certificate> for CapturedX509Certificate {
type Error = Error;
fn try_from(cert: &X509Certificate) -> Result<Self, Self::Error> {
let mut buffer = Vec::<u8>::new();
cert.encode_der_to(&mut buffer)?;
Self::from_der(buffer)
}
}
impl TryFrom<X509Certificate> for CapturedX509Certificate {
type Error = Error;
fn try_from(cert: X509Certificate) -> Result<Self, Self::Error> {
let mut buffer = Vec::<u8>::new();
cert.encode_der_to(&mut buffer)?;
Self::from_der(buffer)
}
}
impl From<CapturedX509Certificate> for rfc5280::Certificate {
fn from(cert: CapturedX509Certificate) -> Self {
cert.inner.0
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MutableX509Certificate(CapturedX509Certificate);
impl Deref for MutableX509Certificate {
type Target = X509Certificate;
fn deref(&self) -> &Self::Target {
&self.0.inner
}
}
impl DerefMut for MutableX509Certificate {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0.inner
}
}
impl From<CapturedX509Certificate> for MutableX509Certificate {
fn from(cert: CapturedX509Certificate) -> Self {
Self(cert)
}
}
pub fn certificate_is_subset_of(
a_serial: &Integer,
a_name: &Name,
b_serial: &Integer,
b_name: &Name,
) -> bool {
if a_serial != b_serial {
return false;
}
let Name::RdnSequence(a_sequence) = &a_name;
let Name::RdnSequence(b_sequence) = &b_name;
a_sequence.iter().all(|rdn| b_sequence.contains(rdn))
}
pub enum KeyUsage {
DigitalSignature,
NonRepudiation,
KeyEncipherment,
DataEncipherment,
KeyAgreement,
KeyCertSign,
CrlSign,
}
impl From<KeyUsage> for u8 {
fn from(ku: KeyUsage) -> Self {
match ku {
KeyUsage::DigitalSignature => 0,
KeyUsage::NonRepudiation => 1,
KeyUsage::KeyEncipherment => 2,
KeyUsage::DataEncipherment => 3,
KeyUsage::KeyAgreement => 4,
KeyUsage::KeyCertSign => 5,
KeyUsage::CrlSign => 6,
}
}
}
pub struct X509CertificateBuilder {
key_algorithm: KeyAlgorithm,
subject: Name,
issuer: Option<Name>,
extensions: rfc5280::Extensions,
serial_number: i64,
not_before: chrono::DateTime<Utc>,
not_after: chrono::DateTime<Utc>,
}
impl X509CertificateBuilder {
pub fn new(alg: KeyAlgorithm) -> Self {
let not_before = Utc::now();
let not_after = not_before + Duration::hours(1);
Self {
key_algorithm: alg,
subject: Name::default(),
issuer: None,
extensions: rfc5280::Extensions::default(),
serial_number: 1,
not_before,
not_after,
}
}
pub fn subject(&mut self) -> &mut Name {
&mut self.subject
}
pub fn issuer(&mut self) -> &mut Name {
self.issuer.get_or_insert_with(Name::default)
}
pub fn serial_number(&mut self, value: i64) {
self.serial_number = value;
}
pub fn extensions(&self) -> &rfc5280::Extensions {
&self.extensions
}
pub fn extensions_mut(&mut self) -> &mut rfc5280::Extensions {
&mut self.extensions
}
pub fn add_extension_der_data(&mut self, oid: Oid, critical: bool, data: impl AsRef<[u8]>) {
self.extensions.push(rfc5280::Extension {
id: oid,
critical: Some(critical),
value: OctetString::new(Bytes::copy_from_slice(data.as_ref())),
});
}
pub fn validity_duration(&mut self, duration: Duration) {
self.not_after = self.not_before + duration;
}
pub fn constraint_not_ca(&mut self) {
self.extensions.push(rfc5280::Extension {
id: Oid(OID_EXTENSION_BASIC_CONSTRAINTS.as_ref().into()),
critical: Some(true),
value: OctetString::new(Bytes::copy_from_slice(&[0x30, 00])),
});
}
pub fn key_usage(&mut self, key_usage: KeyUsage) {
let value: u8 = key_usage.into();
self.extensions.push(rfc5280::Extension {
id: Oid(OID_EXTENSION_KEY_USAGE.as_ref().into()),
critical: Some(true),
value: OctetString::new(Bytes::copy_from_slice(&[3, 2, 7, 128 | value])),
});
}
pub fn create_with_random_keypair(
&self,
) -> Result<
(
CapturedX509Certificate,
InMemorySigningKeyPair,
ring::pkcs8::Document,
),
Error,
> {
let (key_pair, document) = InMemorySigningKeyPair::generate_random(self.key_algorithm)?;
let key_pair_signature_algorithm = key_pair.signature_algorithm();
let issuer = if let Some(issuer) = &self.issuer {
issuer
} else {
&self.subject
};
let tbs_certificate = rfc5280::TbsCertificate {
version: rfc5280::Version::V3,
serial_number: self.serial_number.into(),
signature: key_pair_signature_algorithm.into(),
issuer: issuer.clone(),
validity: rfc5280::Validity {
not_before: Time::from(self.not_before),
not_after: Time::from(self.not_after),
},
subject: self.subject.clone(),
subject_public_key_info: rfc5280::SubjectPublicKeyInfo {
algorithm: key_pair.key_algorithm().into(),
subject_public_key: BitString::new(
0,
Bytes::copy_from_slice(key_pair.public_key_data()),
),
},
issuer_unique_id: None,
subject_unique_id: None,
extensions: if self.extensions.is_empty() {
None
} else {
Some(self.extensions.clone())
},
raw_data: None,
};
let mut tbs_der = Vec::<u8>::new();
tbs_certificate
.encode_ref()
.write_encoded(Mode::Der, &mut tbs_der)?;
let (signature, signature_algorithm) = key_pair.sign(&tbs_der)?;
let cert = rfc5280::Certificate {
tbs_certificate,
signature_algorithm: signature_algorithm.into(),
signature: BitString::new(0, Bytes::copy_from_slice(signature.as_ref())),
};
let cert = X509Certificate::from(cert);
let cert_der = cert.encode_der()?;
let cert = CapturedX509Certificate::from_der(cert_der)?;
Ok((cert, key_pair, document))
}
}
#[cfg(test)]
mod test {
use {super::*, crate::EcdsaCurve};
#[test]
fn builder_ed25519_default() {
let builder = X509CertificateBuilder::new(KeyAlgorithm::Ed25519);
builder.create_with_random_keypair().unwrap();
}
#[test]
fn build_ecdsa_default() {
for curve in EcdsaCurve::all() {
let key_algorithm = KeyAlgorithm::Ecdsa(*curve);
let builder = X509CertificateBuilder::new(key_algorithm);
builder.create_with_random_keypair().unwrap();
}
}
#[test]
fn build_subject_populate() {
let mut builder = X509CertificateBuilder::new(KeyAlgorithm::Ed25519);
builder
.subject()
.append_common_name_utf8_string("My Name")
.unwrap();
builder
.subject()
.append_country_utf8_string("Wakanda")
.unwrap();
builder.create_with_random_keypair().unwrap();
}
#[test]
fn ecdsa_p256_sha256_self_signed() {
let der = include_bytes!("testdata/ecdsa-p256-sha256-self-signed.cer");
let cert = CapturedX509Certificate::from_der(der.to_vec()).unwrap();
cert.verify_signed_by_certificate(&cert).unwrap();
}
#[test]
fn ecdsa_p384_sha256_self_signed() {
let der = include_bytes!("testdata/ecdsa-p384-sha256-self-signed.cer");
let cert = CapturedX509Certificate::from_der(der.to_vec()).unwrap();
cert.verify_signed_by_certificate(&cert).unwrap();
}
#[test]
fn ecdsa_p512_sha256_self_signed() {
let der = include_bytes!("testdata/ecdsa-p512-sha256-self-signed.cer");
let cert = CapturedX509Certificate::from_der(der.to_vec()).unwrap();
assert!(matches!(
cert.verify_signed_by_certificate(&cert),
Err(Error::UnknownEllipticCurve(_))
));
}
}