1use crate::bio::{MemBio, MemBioBuf};
15use crate::error::ErrorStack;
16use native_ossl_sys as sys;
17use std::ffi::CStr;
18use std::marker::PhantomData;
19use std::sync::Arc;
20
21pub struct X509 {
27 ptr: *mut sys::X509,
28}
29
30unsafe impl Send for X509 {}
32unsafe impl Sync for X509 {}
33
34impl Clone for X509 {
35 fn clone(&self) -> Self {
36 unsafe { sys::X509_up_ref(self.ptr) };
37 X509 { ptr: self.ptr }
38 }
39}
40
41impl Drop for X509 {
42 fn drop(&mut self) {
43 unsafe { sys::X509_free(self.ptr) };
44 }
45}
46
47impl X509 {
48 pub(crate) unsafe fn from_ptr(ptr: *mut sys::X509) -> Self {
54 X509 { ptr }
55 }
56
57 pub fn from_pem(pem: &[u8]) -> Result<Self, ErrorStack> {
61 let bio = MemBioBuf::new(pem)?;
62 let ptr = unsafe {
63 sys::PEM_read_bio_X509(
64 bio.as_ptr(),
65 std::ptr::null_mut(),
66 None,
67 std::ptr::null_mut(),
68 )
69 };
70 if ptr.is_null() {
71 return Err(ErrorStack::drain());
72 }
73 Ok(unsafe { Self::from_ptr(ptr) })
74 }
75
76 pub fn from_pem_in(_ctx: &Arc<crate::lib_ctx::LibCtx>, pem: &[u8]) -> Result<Self, ErrorStack> {
87 Self::from_pem(pem)
88 }
89
90 pub fn from_der(der: &[u8]) -> Result<Self, ErrorStack> {
98 let mut der_ptr = der.as_ptr();
99 let len = i64::try_from(der.len()).map_err(|_| ErrorStack::drain())?;
100 let ptr =
101 unsafe { sys::d2i_X509(std::ptr::null_mut(), std::ptr::addr_of_mut!(der_ptr), len) };
102 if ptr.is_null() {
103 return Err(ErrorStack::drain());
104 }
105 Ok(unsafe { Self::from_ptr(ptr) })
106 }
107
108 pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
112 let mut bio = MemBio::new()?;
113 crate::ossl_call!(sys::PEM_write_bio_X509(bio.as_ptr(), self.ptr))?;
114 Ok(bio.into_vec())
115 }
116
117 pub fn to_der(&self) -> Result<Vec<u8>, ErrorStack> {
124 let len = unsafe { sys::i2d_X509(self.ptr, std::ptr::null_mut()) };
125 if len < 0 {
126 return Err(ErrorStack::drain());
127 }
128 let mut buf = vec![0u8; usize::try_from(len).unwrap_or(0)];
129 let mut out_ptr = buf.as_mut_ptr();
130 let written = unsafe { sys::i2d_X509(self.ptr, std::ptr::addr_of_mut!(out_ptr)) };
131 if written < 0 {
132 return Err(ErrorStack::drain());
133 }
134 buf.truncate(usize::try_from(written).unwrap_or(0));
135 Ok(buf)
136 }
137
138 #[must_use]
140 pub fn subject_name(&self) -> X509Name<'_> {
141 let ptr = unsafe { sys::X509_get_subject_name(self.ptr) }.cast();
145 X509Name {
146 ptr,
147 _owner: PhantomData,
148 }
149 }
150
151 #[must_use]
153 pub fn issuer_name(&self) -> X509Name<'_> {
154 let ptr = unsafe { sys::X509_get_issuer_name(self.ptr) }.cast();
155 X509Name {
156 ptr,
157 _owner: PhantomData,
158 }
159 }
160
161 #[must_use]
165 pub fn serial_number(&self) -> Option<i64> {
166 let ai = unsafe { sys::X509_get0_serialNumber(self.ptr) };
167 if ai.is_null() {
168 return None;
169 }
170 let mut n: i64 = 0;
171 let rc = unsafe { sys::ASN1_INTEGER_get_int64(std::ptr::addr_of_mut!(n), ai) };
172 if rc == 1 {
173 Some(n)
174 } else {
175 None
176 }
177 }
178
179 #[must_use]
184 pub fn not_before_str(&self) -> Option<String> {
185 let t = unsafe { sys::X509_get0_notBefore(self.ptr) };
186 asn1_time_to_str(t)
187 }
188
189 #[must_use]
193 pub fn not_after_str(&self) -> Option<String> {
194 let t = unsafe { sys::X509_get0_notAfter(self.ptr) };
195 asn1_time_to_str(t)
196 }
197
198 #[must_use]
200 pub fn is_valid_now(&self) -> bool {
201 let nb = unsafe { sys::X509_get0_notBefore(self.ptr) };
202 let na = unsafe { sys::X509_get0_notAfter(self.ptr) };
203 unsafe {
205 sys::X509_cmp_time(nb, std::ptr::null_mut()) <= 0
206 && sys::X509_cmp_time(na, std::ptr::null_mut()) >= 0
207 }
208 }
209
210 pub fn public_key(&self) -> Result<crate::pkey::Pkey<crate::pkey::Public>, ErrorStack> {
216 let ptr = unsafe { sys::X509_get_pubkey(self.ptr) };
217 if ptr.is_null() {
218 return Err(ErrorStack::drain());
219 }
220 Ok(unsafe { crate::pkey::Pkey::from_ptr(ptr) })
221 }
222
223 pub fn verify(&self, key: &crate::pkey::Pkey<crate::pkey::Public>) -> Result<bool, ErrorStack> {
230 match unsafe { sys::X509_verify(self.ptr, key.as_ptr()) } {
231 1 => Ok(true),
232 0 => Ok(false),
233 _ => Err(ErrorStack::drain()),
234 }
235 }
236
237 #[must_use]
239 pub fn is_self_signed(&self) -> bool {
240 unsafe { sys::X509_self_signed(self.ptr, 0) == 1 }
242 }
243
244 #[must_use]
246 pub fn extension_count(&self) -> usize {
247 let n = unsafe { sys::X509_get_ext_count(self.ptr) };
248 usize::try_from(n).unwrap_or(0)
249 }
250
251 #[must_use]
255 pub fn extension(&self, idx: usize) -> Option<X509Extension<'_>> {
256 let idx_i32 = i32::try_from(idx).ok()?;
257 let ptr = unsafe { sys::X509_get_ext(self.ptr, idx_i32) }.cast::<sys::X509_EXTENSION>();
259 if ptr.is_null() {
260 None
261 } else {
262 Some(X509Extension {
263 ptr,
264 _owner: PhantomData,
265 })
266 }
267 }
268
269 #[must_use]
273 pub fn extension_by_nid(&self, nid: i32) -> Option<X509Extension<'_>> {
274 let idx = unsafe { sys::X509_get_ext_by_NID(self.ptr, nid, -1) };
275 if idx < 0 {
276 return None;
277 }
278 let ptr = unsafe { sys::X509_get_ext(self.ptr, idx) }.cast::<sys::X509_EXTENSION>();
279 if ptr.is_null() {
280 None
281 } else {
282 Some(X509Extension {
283 ptr,
284 _owner: PhantomData,
285 })
286 }
287 }
288
289 #[must_use]
291 #[allow(dead_code)] pub(crate) fn as_ptr(&self) -> *mut sys::X509 {
293 self.ptr
294 }
295}
296
297pub struct X509Name<'cert> {
301 ptr: *mut sys::X509_NAME,
302 _owner: PhantomData<&'cert X509>,
303}
304
305impl X509Name<'_> {
306 #[must_use]
308 pub fn entry_count(&self) -> usize {
309 usize::try_from(unsafe { sys::X509_NAME_entry_count(self.ptr) }).unwrap_or(0)
310 }
311
312 #[must_use]
314 pub fn entry(&self, idx: usize) -> Option<X509NameEntry<'_>> {
315 let idx_i32 = i32::try_from(idx).ok()?;
316 let ptr =
318 unsafe { sys::X509_NAME_get_entry(self.ptr, idx_i32) }.cast::<sys::X509_NAME_ENTRY>();
319 if ptr.is_null() {
320 None
321 } else {
322 Some(X509NameEntry {
323 ptr,
324 _owner: PhantomData,
325 })
326 }
327 }
328
329 #[must_use]
334 pub fn to_string(&self) -> Option<String> {
335 let mut bio = MemBio::new().ok()?;
336 let n = unsafe { sys::X509_NAME_print_ex(bio.as_ptr(), self.ptr, 0, 0) };
338 if n < 0 {
339 return None;
340 }
341 String::from_utf8(bio.into_vec()).ok()
342 }
343}
344
345pub struct X509NameEntry<'name> {
349 ptr: *mut sys::X509_NAME_ENTRY,
350 _owner: PhantomData<&'name ()>,
351}
352
353impl X509NameEntry<'_> {
354 #[must_use]
356 pub fn nid(&self) -> i32 {
357 let obj = unsafe { sys::X509_NAME_ENTRY_get_object(self.ptr) };
358 unsafe { sys::OBJ_obj2nid(obj) }
359 }
360
361 #[must_use]
365 pub fn data(&self) -> &[u8] {
366 let asn1 = unsafe { sys::X509_NAME_ENTRY_get_data(self.ptr) };
367 if asn1.is_null() {
368 return &[];
369 }
370 unsafe { asn1_string_data(asn1) }
372 }
373}
374
375pub struct X509Extension<'cert> {
379 ptr: *mut sys::X509_EXTENSION,
380 _owner: PhantomData<&'cert X509>,
381}
382
383impl X509Extension<'_> {
384 #[must_use]
386 pub fn nid(&self) -> i32 {
387 let obj = unsafe { sys::X509_EXTENSION_get_object(self.ptr) };
388 unsafe { sys::OBJ_obj2nid(obj) }
389 }
390
391 #[must_use]
393 pub fn is_critical(&self) -> bool {
394 unsafe { sys::X509_EXTENSION_get_critical(self.ptr) == 1 }
395 }
396
397 #[must_use]
401 pub fn data(&self) -> &[u8] {
402 let asn1 = unsafe { sys::X509_EXTENSION_get_data(self.ptr) };
403 if asn1.is_null() {
404 return &[];
405 }
406 unsafe { asn1_string_data(asn1.cast()) }
409 }
410}
411
412pub struct X509NameOwned {
418 ptr: *mut sys::X509_NAME,
419}
420
421impl X509NameOwned {
422 pub fn new() -> Result<Self, ErrorStack> {
426 let ptr = unsafe { sys::X509_NAME_new() };
427 if ptr.is_null() {
428 return Err(ErrorStack::drain());
429 }
430 Ok(X509NameOwned { ptr })
431 }
432
433 pub fn add_entry_by_txt(&mut self, field: &CStr, value: &[u8]) -> Result<(), ErrorStack> {
443 let len = i32::try_from(value.len()).map_err(|_| ErrorStack::drain())?;
444 let rc = unsafe {
446 sys::X509_NAME_add_entry_by_txt(
447 self.ptr,
448 field.as_ptr(),
449 0x1000, value.as_ptr(),
451 len,
452 -1, 0,
454 )
455 };
456 if rc != 1 {
457 return Err(ErrorStack::drain());
458 }
459 Ok(())
460 }
461}
462
463impl Drop for X509NameOwned {
464 fn drop(&mut self) {
465 unsafe { sys::X509_NAME_free(self.ptr) };
466 }
467}
468
469pub struct X509Builder {
489 ptr: *mut sys::X509,
490}
491
492impl X509Builder {
493 pub fn new() -> Result<Self, ErrorStack> {
497 let ptr = unsafe { sys::X509_new() };
498 if ptr.is_null() {
499 return Err(ErrorStack::drain());
500 }
501 Ok(X509Builder { ptr })
502 }
503
504 pub fn set_version(self, version: i64) -> Result<Self, ErrorStack> {
508 crate::ossl_call!(sys::X509_set_version(self.ptr, version))?;
509 Ok(self)
510 }
511
512 pub fn set_serial_number(self, n: i64) -> Result<Self, ErrorStack> {
516 let ai = unsafe { sys::ASN1_INTEGER_new() };
517 if ai.is_null() {
518 return Err(ErrorStack::drain());
519 }
520 crate::ossl_call!(sys::ASN1_INTEGER_set_int64(ai, n)).map_err(|e| {
521 unsafe { sys::ASN1_INTEGER_free(ai) };
522 e
523 })?;
524 let rc = unsafe { sys::X509_set_serialNumber(self.ptr, ai) };
525 unsafe { sys::ASN1_INTEGER_free(ai) };
526 if rc != 1 {
527 return Err(ErrorStack::drain());
528 }
529 Ok(self)
530 }
531
532 pub fn set_not_before_offset(self, offset_secs: i64) -> Result<Self, ErrorStack> {
536 let t = unsafe { sys::X509_getm_notBefore(self.ptr) };
537 if unsafe { sys::X509_gmtime_adj(t, offset_secs) }.is_null() {
538 return Err(ErrorStack::drain());
539 }
540 Ok(self)
541 }
542
543 pub fn set_not_after_offset(self, offset_secs: i64) -> Result<Self, ErrorStack> {
547 let t = unsafe { sys::X509_getm_notAfter(self.ptr) };
548 if unsafe { sys::X509_gmtime_adj(t, offset_secs) }.is_null() {
549 return Err(ErrorStack::drain());
550 }
551 Ok(self)
552 }
553
554 pub fn set_subject_name(self, name: &X509NameOwned) -> Result<Self, ErrorStack> {
558 crate::ossl_call!(sys::X509_set_subject_name(self.ptr, name.ptr))?;
559 Ok(self)
560 }
561
562 pub fn set_issuer_name(self, name: &X509NameOwned) -> Result<Self, ErrorStack> {
566 crate::ossl_call!(sys::X509_set_issuer_name(self.ptr, name.ptr))?;
567 Ok(self)
568 }
569
570 pub fn set_public_key<T: crate::pkey::HasPublic>(
574 self,
575 key: &crate::pkey::Pkey<T>,
576 ) -> Result<Self, ErrorStack> {
577 crate::ossl_call!(sys::X509_set_pubkey(self.ptr, key.as_ptr()))?;
578 Ok(self)
579 }
580
581 pub fn sign(
588 self,
589 key: &crate::pkey::Pkey<crate::pkey::Private>,
590 digest: Option<&crate::digest::DigestAlg>,
591 ) -> Result<Self, ErrorStack> {
592 let md_ptr = digest.map_or(std::ptr::null(), crate::digest::DigestAlg::as_ptr);
593 let rc = unsafe { sys::X509_sign(self.ptr, key.as_ptr(), md_ptr) };
595 if rc <= 0 {
596 return Err(ErrorStack::drain());
597 }
598 Ok(self)
599 }
600
601 #[must_use]
603 pub fn build(self) -> X509 {
604 let ptr = self.ptr;
605 std::mem::forget(self);
606 X509 { ptr }
607 }
608}
609
610impl Drop for X509Builder {
611 fn drop(&mut self) {
612 unsafe { sys::X509_free(self.ptr) };
613 }
614}
615
616pub struct X509Store {
622 ptr: *mut sys::X509_STORE,
623}
624
625unsafe impl Send for X509Store {}
626unsafe impl Sync for X509Store {}
627
628impl Clone for X509Store {
629 fn clone(&self) -> Self {
630 unsafe { sys::X509_STORE_up_ref(self.ptr) };
631 X509Store { ptr: self.ptr }
632 }
633}
634
635impl Drop for X509Store {
636 fn drop(&mut self) {
637 unsafe { sys::X509_STORE_free(self.ptr) };
638 }
639}
640
641impl X509Store {
642 pub fn new() -> Result<Self, ErrorStack> {
646 let ptr = unsafe { sys::X509_STORE_new() };
647 if ptr.is_null() {
648 return Err(ErrorStack::drain());
649 }
650 Ok(X509Store { ptr })
651 }
652
653 pub fn add_cert(&mut self, cert: &X509) -> Result<(), ErrorStack> {
659 let rc = unsafe { sys::X509_STORE_add_cert(self.ptr, cert.ptr) };
660 if rc != 1 {
661 return Err(ErrorStack::drain());
662 }
663 Ok(())
664 }
665
666 pub fn add_crl(&mut self, crl: &X509Crl) -> Result<(), ErrorStack> {
670 let rc = unsafe { sys::X509_STORE_add_crl(self.ptr, crl.ptr) };
671 if rc != 1 {
672 return Err(ErrorStack::drain());
673 }
674 Ok(())
675 }
676
677 pub fn set_flags(&mut self, flags: u64) -> Result<(), ErrorStack> {
681 let rc = unsafe { sys::X509_STORE_set_flags(self.ptr, flags) };
682 if rc != 1 {
683 return Err(ErrorStack::drain());
684 }
685 Ok(())
686 }
687
688 #[must_use]
690 pub(crate) fn as_ptr(&self) -> *mut sys::X509_STORE {
691 self.ptr
692 }
693}
694
695pub struct X509StoreCtx {
702 ptr: *mut sys::X509_STORE_CTX,
703}
704
705impl Drop for X509StoreCtx {
706 fn drop(&mut self) {
707 unsafe { sys::X509_STORE_CTX_free(self.ptr) };
708 }
709}
710
711unsafe impl Send for X509StoreCtx {}
712
713impl X509StoreCtx {
714 pub fn new() -> Result<Self, ErrorStack> {
718 let ptr = unsafe { sys::X509_STORE_CTX_new() };
719 if ptr.is_null() {
720 return Err(ErrorStack::drain());
721 }
722 Ok(X509StoreCtx { ptr })
723 }
724
725 pub fn init(&mut self, store: &X509Store, cert: &X509) -> Result<(), ErrorStack> {
731 let rc = unsafe {
732 sys::X509_STORE_CTX_init(self.ptr, store.ptr, cert.ptr, std::ptr::null_mut())
733 };
734 if rc != 1 {
735 return Err(ErrorStack::drain());
736 }
737 Ok(())
738 }
739
740 pub fn verify(&mut self) -> Result<bool, ErrorStack> {
747 match unsafe { sys::X509_verify_cert(self.ptr) } {
748 1 => Ok(true),
749 0 => Ok(false),
750 _ => Err(ErrorStack::drain()),
751 }
752 }
753
754 #[must_use]
759 pub fn error(&self) -> i32 {
760 unsafe { sys::X509_STORE_CTX_get_error(self.ptr) }
761 }
762
763 #[must_use]
768 #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
770 pub fn chain(&self) -> Vec<X509> {
771 let stack = unsafe { sys::X509_STORE_CTX_get0_chain(self.ptr) };
772 if stack.is_null() {
773 return Vec::new();
774 }
775 let n = unsafe { sys::OPENSSL_sk_num(stack.cast::<sys::OPENSSL_STACK>()) };
776 let n = usize::try_from(n).unwrap_or(0);
777 let mut out = Vec::with_capacity(n);
778 for i in 0..n {
779 let raw =
780 unsafe { sys::OPENSSL_sk_value(stack.cast::<sys::OPENSSL_STACK>(), i as i32) };
781 if raw.is_null() {
782 continue;
783 }
784 let cert_ptr = raw.cast::<sys::X509>();
786 unsafe { sys::X509_up_ref(cert_ptr) };
787 out.push(X509 { ptr: cert_ptr });
788 }
789 out
790 }
791}
792
793pub struct X509Crl {
799 ptr: *mut sys::X509_CRL,
800}
801
802unsafe impl Send for X509Crl {}
803unsafe impl Sync for X509Crl {}
804
805impl Clone for X509Crl {
806 fn clone(&self) -> Self {
807 unsafe { sys::X509_CRL_up_ref(self.ptr) };
808 X509Crl { ptr: self.ptr }
809 }
810}
811
812impl Drop for X509Crl {
813 fn drop(&mut self) {
814 unsafe { sys::X509_CRL_free(self.ptr) };
815 }
816}
817
818impl X509Crl {
819 pub(crate) unsafe fn from_ptr(ptr: *mut sys::X509_CRL) -> Self {
825 X509Crl { ptr }
826 }
827
828 pub fn from_pem(pem: &[u8]) -> Result<Self, ErrorStack> {
832 let bio = MemBioBuf::new(pem)?;
833 let ptr = unsafe {
834 sys::PEM_read_bio_X509_CRL(
835 bio.as_ptr(),
836 std::ptr::null_mut(),
837 None,
838 std::ptr::null_mut(),
839 )
840 };
841 if ptr.is_null() {
842 return Err(ErrorStack::drain());
843 }
844 Ok(unsafe { Self::from_ptr(ptr) })
845 }
846
847 pub fn from_der(der: &[u8]) -> Result<Self, ErrorStack> {
851 let bio = MemBioBuf::new(der)?;
852 let ptr = unsafe { sys::d2i_X509_CRL_bio(bio.as_ptr(), std::ptr::null_mut()) };
853 if ptr.is_null() {
854 return Err(ErrorStack::drain());
855 }
856 Ok(unsafe { Self::from_ptr(ptr) })
857 }
858
859 pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
863 let mut bio = MemBio::new()?;
864 let rc = unsafe { sys::PEM_write_bio_X509_CRL(bio.as_ptr(), self.ptr) };
865 if rc != 1 {
866 return Err(ErrorStack::drain());
867 }
868 Ok(bio.into_vec())
869 }
870
871 pub fn to_der(&self) -> Result<Vec<u8>, ErrorStack> {
875 let mut bio = MemBio::new()?;
876 let rc = unsafe { sys::i2d_X509_CRL_bio(bio.as_ptr(), self.ptr) };
877 if rc != 1 {
878 return Err(ErrorStack::drain());
879 }
880 Ok(bio.into_vec())
881 }
882
883 #[must_use]
885 pub fn issuer_name(&self) -> X509Name<'_> {
886 let ptr = unsafe { sys::X509_CRL_get_issuer(self.ptr) };
887 X509Name {
888 ptr: ptr.cast(),
889 _owner: PhantomData,
890 }
891 }
892
893 #[must_use]
895 pub fn last_update_str(&self) -> Option<String> {
896 let t = unsafe { sys::X509_CRL_get0_lastUpdate(self.ptr) };
897 asn1_time_to_str(t)
898 }
899
900 #[must_use]
902 pub fn next_update_str(&self) -> Option<String> {
903 let t = unsafe { sys::X509_CRL_get0_nextUpdate(self.ptr) };
904 asn1_time_to_str(t)
905 }
906
907 pub fn verify(&self, key: &crate::pkey::Pkey<crate::pkey::Public>) -> Result<bool, ErrorStack> {
913 match unsafe { sys::X509_CRL_verify(self.ptr, key.as_ptr()) } {
914 1 => Ok(true),
915 0 => Ok(false),
916 _ => Err(ErrorStack::drain()),
917 }
918 }
919
920 #[must_use]
922 #[allow(dead_code)]
923 pub(crate) fn as_ptr(&self) -> *mut sys::X509_CRL {
924 self.ptr
925 }
926}
927
928fn asn1_time_to_str(t: *const sys::ASN1_TIME) -> Option<String> {
932 if t.is_null() {
933 return None;
934 }
935 let mut bio = MemBio::new().ok()?;
936 let rc = unsafe { sys::ASN1_TIME_print(bio.as_ptr(), t) };
937 if rc != 1 {
938 return None;
939 }
940 String::from_utf8(bio.into_vec()).ok()
941}
942
943unsafe fn asn1_string_data<'a>(asn1: *const sys::ASN1_STRING) -> &'a [u8] {
952 let len = usize::try_from(sys::ASN1_STRING_length(asn1)).unwrap_or(0);
953 let ptr = sys::ASN1_STRING_get0_data(asn1);
954 if ptr.is_null() || len == 0 {
955 return &[];
956 }
957 std::slice::from_raw_parts(ptr, len)
959}
960
961#[cfg(test)]
964mod tests {
965 use super::*;
966 use crate::pkey::{KeygenCtx, Pkey, Private, Public};
967
968 fn make_self_signed() -> (X509, Pkey<Private>, Pkey<Public>) {
970 let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
971 let priv_key = kgen.generate().unwrap();
972 let pub_key = Pkey::<Public>::from(priv_key.clone());
973
974 let mut name = X509NameOwned::new().unwrap();
975 name.add_entry_by_txt(c"CN", b"Test Cert").unwrap();
976 name.add_entry_by_txt(c"O", b"Example Org").unwrap();
977
978 let cert = X509Builder::new()
979 .unwrap()
980 .set_version(2)
981 .unwrap()
982 .set_serial_number(1)
983 .unwrap()
984 .set_not_before_offset(0)
985 .unwrap()
986 .set_not_after_offset(365 * 86400)
987 .unwrap()
988 .set_subject_name(&name)
989 .unwrap()
990 .set_issuer_name(&name)
991 .unwrap()
992 .set_public_key(&pub_key)
993 .unwrap()
994 .sign(&priv_key, None)
995 .unwrap()
996 .build();
997
998 (cert, priv_key, pub_key)
999 }
1000
1001 #[test]
1002 fn build_and_verify_self_signed() {
1003 let (cert, _, pub_key) = make_self_signed();
1004 assert!(cert.verify(&pub_key).unwrap());
1005 assert!(cert.is_self_signed());
1006 }
1007
1008 #[test]
1009 fn pem_round_trip() {
1010 let (cert, _, _) = make_self_signed();
1011 let pem = cert.to_pem().unwrap();
1012 assert!(pem.starts_with(b"-----BEGIN CERTIFICATE-----"));
1013
1014 let cert2 = X509::from_pem(&pem).unwrap();
1015 assert_eq!(cert.to_der().unwrap(), cert2.to_der().unwrap());
1017 }
1018
1019 #[test]
1020 fn der_round_trip() {
1021 let (cert, _, _) = make_self_signed();
1022 let der = cert.to_der().unwrap();
1023 assert!(!der.is_empty());
1024
1025 let cert2 = X509::from_der(&der).unwrap();
1026 assert_eq!(cert2.to_der().unwrap(), der);
1027 }
1028
1029 #[test]
1030 fn subject_name_entries() {
1031 let (cert, _, _) = make_self_signed();
1032 let name = cert.subject_name();
1033
1034 assert_eq!(name.entry_count(), 2);
1035
1036 let e0 = name.entry(0).unwrap();
1038 assert_eq!(e0.nid(), 13); assert!(!e0.data().is_empty());
1040
1041 let s = name.to_string().unwrap();
1043 assert!(s.contains("Test Cert") || s.contains("CN=Test Cert"));
1044 }
1045
1046 #[test]
1047 fn serial_number() {
1048 let (cert, _, _) = make_self_signed();
1049 assert_eq!(cert.serial_number(), Some(1));
1050 }
1051
1052 #[test]
1053 fn validity_strings_present() {
1054 let (cert, _, _) = make_self_signed();
1055 let nb = cert.not_before_str().unwrap();
1056 let na = cert.not_after_str().unwrap();
1057 assert!(nb.contains("GMT"), "not_before_str = {nb:?}");
1059 assert!(na.contains("GMT"), "not_after_str = {na:?}");
1060 }
1061
1062 #[test]
1063 fn is_valid_now() {
1064 let (cert, _, _) = make_self_signed();
1065 assert!(cert.is_valid_now());
1066 }
1067
1068 #[test]
1069 fn public_key_extraction() {
1070 let (cert, _, pub_key) = make_self_signed();
1071 let extracted = cert.public_key().unwrap();
1072 assert!(extracted.is_a(c"ED25519"));
1074 assert_eq!(pub_key.bits(), extracted.bits());
1075 }
1076
1077 #[test]
1078 fn clone_cert() {
1079 let (cert, _, pub_key) = make_self_signed();
1080 let cert2 = cert.clone();
1081 assert_eq!(cert.to_der().unwrap(), cert2.to_der().unwrap());
1083 assert!(cert2.verify(&pub_key).unwrap());
1084 }
1085
1086 #[test]
1087 fn verify_fails_with_wrong_key() {
1088 let (cert, _, _) = make_self_signed();
1089 let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
1091 let other_priv = kgen.generate().unwrap();
1092 let other_pub = Pkey::<Public>::from(other_priv);
1093
1094 assert!(!cert.verify(&other_pub).unwrap());
1096 }
1097
1098 #[test]
1101 fn x509_store_add_cert_and_verify() {
1102 let (cert, _, _) = make_self_signed();
1103
1104 let mut store = X509Store::new().unwrap();
1105 store.add_cert(&cert).unwrap();
1106
1107 let mut ctx = X509StoreCtx::new().unwrap();
1108 ctx.init(&store, &cert).unwrap();
1109 assert!(ctx.verify().unwrap());
1111 }
1112
1113 #[test]
1114 fn x509_store_verify_untrusted_fails() {
1115 let (cert, _, _) = make_self_signed();
1116 let store = X509Store::new().unwrap();
1118
1119 let mut ctx = X509StoreCtx::new().unwrap();
1120 ctx.init(&store, &cert).unwrap();
1121 assert!(!ctx.verify().unwrap());
1122 assert_ne!(ctx.error(), 0);
1124 }
1125
1126 #[test]
1127 fn x509_store_ctx_chain_populated_after_verify() {
1128 let (cert, _, _) = make_self_signed();
1129 let mut store = X509Store::new().unwrap();
1130 store.add_cert(&cert).unwrap();
1131
1132 let mut ctx = X509StoreCtx::new().unwrap();
1133 ctx.init(&store, &cert).unwrap();
1134 assert!(ctx.verify().unwrap());
1135
1136 let chain = ctx.chain();
1137 assert!(
1138 !chain.is_empty(),
1139 "verified chain should contain at least the leaf"
1140 );
1141 }
1142
1143 const TEST_CRL_PEM: &[u8] = b"\
1147-----BEGIN X509 CRL-----\n\
1148MIIBVjBAMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBlJTQSBDQRcNMjYwNDE1\n\
1149MTUwNDEzWhcNMjYwNTE1MTUwNDEzWjANBgkqhkiG9w0BAQsFAAOCAQEAi209u0hh\n\
1150Vz42YaqLplQwBoYCjtjETenl4xXRNcFOYU6Y+FmR66XNGkl9HbPClrz3hRMnbBYr\n\
1151OQJfWQOKS9lS0zpEI4qtlH/H1JBNGwiY32HMqf5HULn0w0ARvmoXR4NzsCecK22G\n\
1152gN61k5FCCpPY8HztsuoHMHAQ65W1WfBiTWu8ZH0nCCU0CA4MSaPZUiNt8/mJZzTG\n\
1153UwTGcZ/hcHQMpocBX40nE7ta5opcIpjG+q2uiCWhXwoqmYsLvdJ+Obw20bLirMHt\n\
1154UsmESTw5G+vcRCudoiSw89Z/jzsYq8yuFhRzF9kA/RtqCoQ+ylQSSH5hxzW2+bPd\n\
1155QPHivSGDiUhH6Q==\n\
1156-----END X509 CRL-----\n";
1157
1158 #[test]
1159 fn crl_pem_round_trip() {
1160 let crl = X509Crl::from_pem(TEST_CRL_PEM).unwrap();
1161 let issuer = crl.issuer_name();
1163 assert!(issuer.entry_count() > 0);
1164 assert!(crl.last_update_str().is_some());
1166 assert!(crl.next_update_str().is_some());
1167 let pem = crl.to_pem().unwrap();
1169 assert!(pem.starts_with(b"-----BEGIN X509 CRL-----"));
1170 }
1171
1172 #[test]
1173 fn crl_der_round_trip() {
1174 let crl = X509Crl::from_pem(TEST_CRL_PEM).unwrap();
1175 let der = crl.to_der().unwrap();
1176 assert!(!der.is_empty());
1177 let crl2 = X509Crl::from_der(&der).unwrap();
1178 assert_eq!(crl2.to_der().unwrap(), der);
1179 }
1180
1181 #[test]
1182 fn crl_clone() {
1183 let crl = X509Crl::from_pem(TEST_CRL_PEM).unwrap();
1184 let crl2 = crl.clone();
1185 assert_eq!(crl.to_der().unwrap(), crl2.to_der().unwrap());
1186 }
1187}