1#![cfg_attr(
12 feature = "pem",
13 doc = r##"
14## Example
15
16```
17use rcgen::{generate_simple_self_signed, CertifiedKey};
18# fn main () {
19// Generate a certificate that's valid for "localhost" and "hello.world.example"
20let subject_alt_names = vec!["hello.world.example".to_string(),
21 "localhost".to_string()];
22
23let CertifiedKey { cert, key_pair } = generate_simple_self_signed(subject_alt_names).unwrap();
24println!("{}", cert.pem());
25println!("{}", key_pair.serialize_pem());
26# }
27```"##
28)]
29#![forbid(unsafe_code)]
30#![forbid(non_ascii_idents)]
31#![deny(missing_docs)]
32#![allow(clippy::complexity, clippy::style, clippy::pedantic)]
33#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
34#![warn(unreachable_pub)]
35
36use std::collections::HashMap;
37use std::fmt;
38use std::hash::Hash;
39use std::net::IpAddr;
40#[cfg(feature = "x509-parser")]
41use std::net::{Ipv4Addr, Ipv6Addr};
42
43use time::{OffsetDateTime, Time};
44use yasna::models::ObjectIdentifier;
45use yasna::models::{GeneralizedTime, UTCTime};
46use yasna::tags::{TAG_BMPSTRING, TAG_TELETEXSTRING, TAG_UNIVERSALSTRING};
47use yasna::DERWriter;
48use yasna::Tag;
49
50pub use certificate::{
51 date_time_ymd, Attribute, BasicConstraints, Certificate, CertificateParams, CidrSubnet,
52 CustomExtension, DnType, ExtendedKeyUsagePurpose, GeneralSubtree, IsCa, NameConstraints,
53};
54pub use crl::{
55 CertificateRevocationList, CertificateRevocationListParams, CrlDistributionPoint,
56 CrlIssuingDistributionPoint, CrlScope, RevocationReason, RevokedCertParams,
57};
58pub use csr::{CertificateSigningRequest, CertificateSigningRequestParams, PublicKey};
59pub use error::{Error, InvalidAsn1String};
60pub use key_pair::PublicKeyData;
61#[cfg(all(feature = "crypto", feature = "aws_lc_rs"))]
62pub use key_pair::RsaKeySize;
63pub use key_pair::{KeyPair, RemoteKeyPair, SubjectPublicKeyInfo};
64#[cfg(feature = "crypto")]
65use ring_like::digest;
66pub use sign_algo::algo::*;
67pub use sign_algo::SignatureAlgorithm;
68pub use string_types::*;
69
70mod certificate;
71mod crl;
72mod csr;
73mod error;
74mod key_pair;
75mod oid;
76mod ring_like;
77mod sign_algo;
78mod string_types;
79
80#[deprecated(
82 note = "Renamed to `Error`. We recommend to refer to it by fully-qualifying the crate: `rcgen::Error`."
83)]
84pub type RcgenError = Error;
85
86pub struct CertifiedKey {
88 pub cert: Certificate,
90 pub key_pair: KeyPair,
92}
93
94#[cfg(feature = "crypto")]
103#[cfg_attr(
104 feature = "pem",
105 doc = r##"
106## Example
107
108```
109use rcgen::{generate_simple_self_signed, CertifiedKey};
110# fn main () {
111// Generate a certificate that's valid for "localhost" and "hello.world.example"
112let subject_alt_names = vec!["hello.world.example".to_string(),
113 "localhost".to_string()];
114
115let CertifiedKey { cert, key_pair } = generate_simple_self_signed(subject_alt_names).unwrap();
116
117// The certificate is now valid for localhost and the domain "hello.world.example"
118println!("{}", cert.pem());
119println!("{}", key_pair.serialize_pem());
120# }
121```
122"##
123)]
124pub fn generate_simple_self_signed(
125 subject_alt_names: impl Into<Vec<String>>,
126) -> Result<CertifiedKey, Error> {
127 let key_pair = KeyPair::generate()?;
128 let cert = CertificateParams::new(subject_alt_names)?.self_signed(&key_pair)?;
129 Ok(CertifiedKey { cert, key_pair })
130}
131
132struct Issuer<'a> {
133 distinguished_name: &'a DistinguishedName,
134 key_identifier_method: &'a KeyIdMethod,
135 key_usages: &'a [KeyUsagePurpose],
136 key_pair: &'a KeyPair,
137}
138
139#[cfg(feature = "pem")]
145const ENCODE_CONFIG: pem::EncodeConfig = {
146 let line_ending = match cfg!(target_family = "windows") {
147 true => pem::LineEnding::CRLF,
148 false => pem::LineEnding::LF,
149 };
150 pem::EncodeConfig::new().set_line_ending(line_ending)
151};
152
153#[derive(Debug, PartialEq, Eq, Hash, Clone)]
154#[allow(missing_docs)]
155#[non_exhaustive]
156pub enum SanType {
158 Rfc822Name(Ia5String),
160 DnsName(Ia5String),
161 URI(Ia5String),
162 IpAddress(IpAddr),
163 OtherName((Vec<u64>, OtherNameValue)),
164}
165
166#[derive(Debug, PartialEq, Eq, Hash, Clone)]
174#[non_exhaustive]
175pub enum OtherNameValue {
176 Utf8String(String),
178}
179
180impl OtherNameValue {
181 fn write_der(&self, writer: DERWriter) {
182 writer.write_tagged(Tag::context(0), |writer| match self {
183 OtherNameValue::Utf8String(s) => writer.write_utf8_string(s),
184 });
185 }
186}
187
188impl<T> From<T> for OtherNameValue
189where
190 T: Into<String>,
191{
192 fn from(t: T) -> Self {
193 OtherNameValue::Utf8String(t.into())
194 }
195}
196
197#[cfg(feature = "x509-parser")]
198fn ip_addr_from_octets(octets: &[u8]) -> Result<IpAddr, Error> {
199 if let Ok(ipv6_octets) = <&[u8; 16]>::try_from(octets) {
200 Ok(Ipv6Addr::from(*ipv6_octets).into())
201 } else if let Ok(ipv4_octets) = <&[u8; 4]>::try_from(octets) {
202 Ok(Ipv4Addr::from(*ipv4_octets).into())
203 } else {
204 Err(Error::InvalidIpAddressOctetLength(octets.len()))
205 }
206}
207
208impl SanType {
209 #[cfg(feature = "x509-parser")]
210 fn try_from_general(name: &x509_parser::extensions::GeneralName<'_>) -> Result<Self, Error> {
211 use x509_parser::der_parser::asn1_rs::{self, FromDer, Tag, TaggedExplicit};
212 Ok(match name {
213 x509_parser::extensions::GeneralName::RFC822Name(name) => {
214 SanType::Rfc822Name((*name).try_into()?)
215 },
216 x509_parser::extensions::GeneralName::DNSName(name) => {
217 SanType::DnsName((*name).try_into()?)
218 },
219 x509_parser::extensions::GeneralName::URI(name) => SanType::URI((*name).try_into()?),
220 x509_parser::extensions::GeneralName::IPAddress(octets) => {
221 SanType::IpAddress(ip_addr_from_octets(octets)?)
222 },
223 x509_parser::extensions::GeneralName::OtherName(oid, value) => {
224 let oid = oid.iter().ok_or(Error::CouldNotParseCertificate)?;
225 let (_, other_name) = TaggedExplicit::<asn1_rs::Any, _, 0>::from_der(value)
227 .map_err(|_| Error::CouldNotParseCertificate)?;
228 let other_name = other_name.into_inner();
229
230 let other_name_value = match other_name.tag() {
231 Tag::Utf8String => OtherNameValue::Utf8String(
232 std::str::from_utf8(other_name.data)
233 .map_err(|_| Error::CouldNotParseCertificate)?
234 .to_owned(),
235 ),
236 _ => return Err(Error::CouldNotParseCertificate),
237 };
238 SanType::OtherName((oid.collect(), other_name_value))
239 },
240 _ => return Err(Error::InvalidNameType),
241 })
242 }
243
244 fn tag(&self) -> u64 {
245 const TAG_OTHER_NAME: u64 = 0;
248 const TAG_RFC822_NAME: u64 = 1;
249 const TAG_DNS_NAME: u64 = 2;
250 const TAG_URI: u64 = 6;
251 const TAG_IP_ADDRESS: u64 = 7;
252
253 match self {
254 SanType::Rfc822Name(_name) => TAG_RFC822_NAME,
255 SanType::DnsName(_name) => TAG_DNS_NAME,
256 SanType::URI(_name) => TAG_URI,
257 SanType::IpAddress(_addr) => TAG_IP_ADDRESS,
258 Self::OtherName(_oid) => TAG_OTHER_NAME,
259 }
260 }
261}
262
263#[derive(Debug, PartialEq, Eq, Hash, Clone)]
265#[non_exhaustive]
266pub enum DnValue {
267 BmpString(BmpString),
269 Ia5String(Ia5String),
271 PrintableString(PrintableString),
273 TeletexString(TeletexString),
275 UniversalString(UniversalString),
277 Utf8String(String),
279}
280
281impl<T> From<T> for DnValue
282where
283 T: Into<String>,
284{
285 fn from(t: T) -> Self {
286 DnValue::Utf8String(t.into())
287 }
288}
289
290#[derive(Debug, Default, PartialEq, Eq, Clone)]
291pub struct DistinguishedName {
302 entries: HashMap<DnType, DnValue>,
303 order: Vec<DnType>,
304}
305
306impl DistinguishedName {
307 pub fn new() -> Self {
309 Self::default()
310 }
311 pub fn get(&self, ty: &DnType) -> Option<&DnValue> {
313 self.entries.get(ty)
314 }
315 pub fn remove(&mut self, ty: DnType) -> bool {
321 let removed = self.entries.remove(&ty).is_some();
322 if removed {
323 self.order.retain(|ty_o| &ty != ty_o);
324 }
325 removed
326 }
327 pub fn push(&mut self, ty: DnType, s: impl Into<DnValue>) {
338 if !self.entries.contains_key(&ty) {
339 self.order.push(ty.clone());
340 }
341 self.entries.insert(ty, s.into());
342 }
343 pub fn iter(&self) -> DistinguishedNameIterator<'_> {
345 DistinguishedNameIterator {
346 distinguished_name: self,
347 iter: self.order.iter(),
348 }
349 }
350
351 #[cfg(feature = "x509-parser")]
352 fn from_name(name: &x509_parser::x509::X509Name) -> Result<Self, Error> {
353 use x509_parser::der_parser::asn1_rs::Tag;
354
355 let mut dn = DistinguishedName::new();
356 for rdn in name.iter() {
357 let mut rdn_iter = rdn.iter();
358 let dn_opt = rdn_iter.next();
359 let attr = if let Some(dn) = dn_opt {
360 if rdn_iter.next().is_some() {
361 return Err(Error::CouldNotParseCertificate);
363 } else {
364 dn
365 }
366 } else {
367 panic!("x509-parser distinguished name set is empty");
368 };
369
370 let attr_type_oid = attr
371 .attr_type()
372 .iter()
373 .ok_or(Error::CouldNotParseCertificate)?;
374 let dn_type = DnType::from_oid(&attr_type_oid.collect::<Vec<_>>());
375 let data = attr.attr_value().data;
376 let try_str =
377 |data| std::str::from_utf8(data).map_err(|_| Error::CouldNotParseCertificate);
378 let dn_value = match attr.attr_value().header.tag() {
379 Tag::BmpString => DnValue::BmpString(BmpString::from_utf16be(data.to_vec())?),
380 Tag::Ia5String => DnValue::Ia5String(try_str(data)?.try_into()?),
381 Tag::PrintableString => DnValue::PrintableString(try_str(data)?.try_into()?),
382 Tag::T61String => DnValue::TeletexString(try_str(data)?.try_into()?),
383 Tag::UniversalString => {
384 DnValue::UniversalString(UniversalString::from_utf32be(data.to_vec())?)
385 },
386 Tag::Utf8String => DnValue::Utf8String(try_str(data)?.to_owned()),
387 _ => return Err(Error::CouldNotParseCertificate),
388 };
389
390 dn.push(dn_type, dn_value);
391 }
392 Ok(dn)
393 }
394}
395
396pub struct DistinguishedNameIterator<'a> {
400 distinguished_name: &'a DistinguishedName,
401 iter: std::slice::Iter<'a, DnType>,
402}
403
404impl<'a> Iterator for DistinguishedNameIterator<'a> {
405 type Item = (&'a DnType, &'a DnValue);
406
407 fn next(&mut self) -> Option<Self::Item> {
408 self.iter
409 .next()
410 .and_then(|ty| self.distinguished_name.entries.get(ty).map(|v| (ty, v)))
411 }
412}
413
414#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
416pub enum KeyUsagePurpose {
417 DigitalSignature,
419 ContentCommitment,
421 KeyEncipherment,
423 DataEncipherment,
425 KeyAgreement,
427 KeyCertSign,
429 CrlSign,
431 EncipherOnly,
433 DecipherOnly,
435}
436
437impl KeyUsagePurpose {
438 fn to_u16(&self) -> u16 {
441 const FLAG: u16 = 0b1000_0000_0000_0000;
442 FLAG >> match self {
443 KeyUsagePurpose::DigitalSignature => 0,
444 KeyUsagePurpose::ContentCommitment => 1,
445 KeyUsagePurpose::KeyEncipherment => 2,
446 KeyUsagePurpose::DataEncipherment => 3,
447 KeyUsagePurpose::KeyAgreement => 4,
448 KeyUsagePurpose::KeyCertSign => 5,
449 KeyUsagePurpose::CrlSign => 6,
450 KeyUsagePurpose::EncipherOnly => 7,
451 KeyUsagePurpose::DecipherOnly => 8,
452 }
453 }
454
455 #[cfg(feature = "x509-parser")]
458 fn from_u16(value: u16) -> Vec<Self> {
459 [
460 KeyUsagePurpose::DigitalSignature,
461 KeyUsagePurpose::ContentCommitment,
462 KeyUsagePurpose::KeyEncipherment,
463 KeyUsagePurpose::DataEncipherment,
464 KeyUsagePurpose::KeyAgreement,
465 KeyUsagePurpose::KeyCertSign,
466 KeyUsagePurpose::CrlSign,
467 KeyUsagePurpose::EncipherOnly,
468 KeyUsagePurpose::DecipherOnly,
469 ]
470 .iter()
471 .filter_map(|key_usage| {
472 let present = key_usage.to_u16() & value != 0;
473 present.then_some(*key_usage)
474 })
475 .collect()
476 }
477}
478
479#[derive(Debug, PartialEq, Eq, Hash, Clone)]
492#[non_exhaustive]
493pub enum KeyIdMethod {
494 #[cfg(feature = "crypto")]
496 Sha256,
497 #[cfg(feature = "crypto")]
499 Sha384,
500 #[cfg(feature = "crypto")]
502 Sha512,
503 PreSpecified(Vec<u8>),
505}
506
507impl KeyIdMethod {
508 #[allow(unused_variables)]
516 pub(crate) fn derive(&self, subject_public_key_info: impl AsRef<[u8]>) -> Vec<u8> {
517 let digest_method = match &self {
518 #[cfg(feature = "crypto")]
519 Self::Sha256 => &digest::SHA256,
520 #[cfg(feature = "crypto")]
521 Self::Sha384 => &digest::SHA384,
522 #[cfg(feature = "crypto")]
523 Self::Sha512 => &digest::SHA512,
524 Self::PreSpecified(b) => {
525 return b.to_vec();
526 },
527 };
528 #[cfg(feature = "crypto")]
529 {
530 let digest = digest::digest(digest_method, subject_public_key_info.as_ref());
531 digest.as_ref()[0..20].to_vec()
532 }
533 }
534}
535
536fn dt_strip_nanos(dt: OffsetDateTime) -> OffsetDateTime {
537 let time =
545 Time::from_hms(dt.hour(), dt.minute(), dt.second()).expect("invalid or out-of-range time");
546 dt.replace_time(time)
547}
548
549fn dt_to_generalized(dt: OffsetDateTime) -> GeneralizedTime {
550 let date_time = dt_strip_nanos(dt);
551 GeneralizedTime::from_datetime(date_time)
552}
553
554fn write_dt_utc_or_generalized(writer: DERWriter, dt: OffsetDateTime) {
555 if (1950..2050).contains(&dt.year()) {
562 let date_time = dt_strip_nanos(dt);
563 let ut = UTCTime::from_datetime(date_time);
564 writer.write_utctime(&ut);
565 } else {
566 let gt = dt_to_generalized(dt);
567 writer.write_generalized_time(>);
568 }
569}
570
571fn write_distinguished_name(writer: DERWriter, dn: &DistinguishedName) {
572 writer.write_sequence(|writer| {
573 for (ty, content) in dn.iter() {
574 writer.next().write_set(|writer| {
575 writer.next().write_sequence(|writer| {
576 writer.next().write_oid(&ty.to_oid());
577 match content {
578 DnValue::BmpString(s) => writer
579 .next()
580 .write_tagged_implicit(TAG_BMPSTRING, |writer| {
581 writer.write_bytes(s.as_bytes())
582 }),
583
584 DnValue::Ia5String(s) => writer.next().write_ia5_string(s.as_str()),
585
586 DnValue::PrintableString(s) => {
587 writer.next().write_printable_string(s.as_str())
588 },
589 DnValue::TeletexString(s) => writer
590 .next()
591 .write_tagged_implicit(TAG_TELETEXSTRING, |writer| {
592 writer.write_bytes(s.as_bytes())
593 }),
594 DnValue::UniversalString(s) => writer
595 .next()
596 .write_tagged_implicit(TAG_UNIVERSALSTRING, |writer| {
597 writer.write_bytes(s.as_bytes())
598 }),
599 DnValue::Utf8String(s) => writer.next().write_utf8_string(s),
600 }
601 });
602 });
603 }
604 });
605}
606
607fn write_x509_extension(
609 writer: DERWriter,
610 extension_oid: &[u64],
611 is_critical: bool,
612 value_serializer: impl FnOnce(DERWriter),
613) {
614 writer.write_sequence(|writer| {
625 let oid = ObjectIdentifier::from_slice(extension_oid);
626 writer.next().write_oid(&oid);
627 if is_critical {
628 writer.next().write_bool(true);
629 }
630 let bytes = yasna::construct_der(value_serializer);
631 writer.next().write_bytes(&bytes);
632 })
633}
634
635fn write_x509_authority_key_identifier(writer: DERWriter, aki: Vec<u8>) {
637 write_x509_extension(writer, oid::AUTHORITY_KEY_IDENTIFIER, false, |writer| {
648 writer.write_sequence(|writer| {
649 writer
650 .next()
651 .write_tagged_implicit(Tag::context(0), |writer| writer.write_bytes(&aki))
652 });
653 });
654}
655
656#[cfg(feature = "zeroize")]
657impl zeroize::Zeroize for KeyPair {
658 fn zeroize(&mut self) {
659 self.serialized_der.zeroize();
660 }
661}
662
663#[derive(Debug, PartialEq, Eq, Hash, Clone)]
665pub struct SerialNumber {
666 inner: Vec<u8>,
667}
668
669impl SerialNumber {
670 pub fn from_slice(bytes: &[u8]) -> SerialNumber {
672 let inner = bytes.to_vec();
673 SerialNumber { inner }
674 }
675
676 pub fn to_bytes(&self) -> Vec<u8> {
678 self.inner.clone()
679 }
680
681 pub fn len(&self) -> usize {
683 self.inner.len()
684 }
685}
686
687impl fmt::Display for SerialNumber {
688 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
689 let hex: Vec<_> = self.inner.iter().map(|b| format!("{:02x}", b)).collect();
690 write!(f, "{}", hex.join(":"))
691 }
692}
693
694impl From<Vec<u8>> for SerialNumber {
695 fn from(inner: Vec<u8>) -> SerialNumber {
696 SerialNumber { inner }
697 }
698}
699
700impl From<u64> for SerialNumber {
701 fn from(u: u64) -> SerialNumber {
702 let inner = u.to_be_bytes().into();
703 SerialNumber { inner }
704 }
705}
706
707impl AsRef<[u8]> for SerialNumber {
708 fn as_ref(&self) -> &[u8] {
709 &self.inner
710 }
711}
712
713#[cfg(test)]
714mod tests {
715 use std::panic::catch_unwind;
716
717 use time::{Date, Month, PrimitiveDateTime};
718
719 use super::*;
720
721 fn times() -> [OffsetDateTime; 2] {
722 let dt_nanos = {
723 let date = Date::from_calendar_date(2020, Month::December, 3).unwrap();
724 let time = Time::from_hms_nano(0, 0, 1, 444).unwrap();
725 PrimitiveDateTime::new(date, time).assume_utc()
726 };
727 let dt_zero = {
728 let date = Date::from_calendar_date(2020, Month::December, 3).unwrap();
729 let time = Time::from_hms_nano(0, 0, 1, 0).unwrap();
730 PrimitiveDateTime::new(date, time).assume_utc()
731 };
732 [dt_nanos, dt_zero]
734 }
735
736 #[test]
737 fn test_dt_utc_strip_nanos() {
738 let times = times();
739
740 let res = catch_unwind(|| UTCTime::from_datetime(times[0]));
742 assert!(res.is_err());
743
744 for dt in times {
746 let date_time = dt_strip_nanos(dt);
747 assert_eq!(date_time.time().nanosecond(), 0);
748 let _ut = UTCTime::from_datetime(date_time);
749 }
750 }
751
752 #[test]
753 fn test_dt_to_generalized() {
754 let times = times();
755
756 for dt in times {
757 let _gt = dt_to_generalized(dt);
758 }
759 }
760
761 #[test]
762 fn signature_algos_different() {
763 for (i, alg_i) in SignatureAlgorithm::iter().enumerate() {
767 for (j, alg_j) in SignatureAlgorithm::iter().enumerate() {
768 assert_eq!(
769 alg_i == alg_j,
770 i == j,
771 "Algorighm relationship mismatch for algorithm index pair {} and {}",
772 i,
773 j
774 );
775 }
776 }
777 }
778
779 #[cfg(feature = "x509-parser")]
780 mod test_ip_address_from_octets {
781 use super::super::ip_addr_from_octets;
782 use super::super::Error;
783 use std::net::IpAddr;
784
785 #[test]
786 fn ipv4() {
787 let octets = [10, 20, 30, 40];
788
789 let actual = ip_addr_from_octets(&octets).unwrap();
790
791 assert_eq!(IpAddr::from(octets), actual)
792 }
793
794 #[test]
795 fn ipv6() {
796 let octets = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
797
798 let actual = ip_addr_from_octets(&octets).unwrap();
799
800 assert_eq!(IpAddr::from(octets), actual)
801 }
802
803 #[test]
804 fn mismatch() {
805 let incorrect = Vec::from_iter(0..10);
806 let actual = ip_addr_from_octets(&incorrect).unwrap_err();
807
808 assert_eq!(Error::InvalidIpAddressOctetLength(10), actual);
809 }
810
811 #[test]
812 fn none() {
813 let actual = ip_addr_from_octets(&[]).unwrap_err();
814
815 assert_eq!(Error::InvalidIpAddressOctetLength(0), actual);
816 }
817
818 #[test]
819 fn too_many() {
820 let incorrect = Vec::from_iter(0..20);
821 let actual = ip_addr_from_octets(&incorrect).unwrap_err();
822
823 assert_eq!(Error::InvalidIpAddressOctetLength(20), actual);
824 }
825 }
826
827 #[cfg(feature = "x509-parser")]
828 mod test_san_type_from_general_name {
829 use crate::SanType;
830 use std::net::IpAddr;
831 use x509_parser::extensions::GeneralName;
832
833 #[test]
834 fn with_ipv4() {
835 let octets = [1, 2, 3, 4];
836 let value = GeneralName::IPAddress(&octets);
837 let actual = SanType::try_from_general(&value).unwrap();
838
839 assert_eq!(SanType::IpAddress(IpAddr::from(octets)), actual);
840 }
841 }
842}