1- use std:: { fmt:: Debug , net:: IpAddr } ;
1+ use std:: { fmt:: Debug , net:: IpAddr , time :: SystemTime } ;
22
33use bon:: Builder ;
44use const_oid:: db:: rfc5280:: { ID_KP_CLIENT_AUTH , ID_KP_SERVER_AUTH } ;
55use rsa:: pkcs8:: EncodePublicKey ;
6- use snafu:: { ResultExt , Snafu } ;
6+ use snafu:: { ResultExt , Snafu , ensure } ;
77use stackable_operator:: time:: Duration ;
88use tracing:: { debug, instrument, warn} ;
99use x509_cert:: {
6262
6363 #[ snafu( display( "failed to build certificate" ) ) ]
6464 BuildCertificate { source : x509_cert:: builder:: Error } ,
65+
66+ #[ snafu( display(
67+ "the generated certificate would outlive the CA, subject {subject:?}, \
68+ CA notAfter {ca_not_after:?}, CA notBefore {ca_not_before:?}, \
69+ cert notAfter {cert_not_after:?}, cert notBefore {cert_not_before:?}"
70+ ) ) ]
71+ CertOutlivesCa {
72+ subject : String ,
73+ ca_not_after : SystemTime ,
74+ ca_not_before : SystemTime ,
75+ cert_not_after : SystemTime ,
76+ cert_not_before : SystemTime ,
77+ } ,
6578}
6679
6780/// This builder builds certificates of type [`CertificatePair`].
7790/// - A default validity of [`DEFAULT_CERTIFICATE_VALIDITY`]
7891/// - A randomly generated serial number
7992/// - In case no `key_pair` was provided, a fresh keypair will be created. The algorithm
80- /// (`rsa`/`ecdsa`) is chosen by the generic [`CertificateKeypair`] type of this struct,
81- /// which is normally inferred from the [`CertificateAuthority`].
93+ /// (`rsa`/`ecdsa`) is chosen by the generic [`CertificateKeypair`] type of this struct,
94+ /// which is normally inferred from the [`CertificateAuthority`].
8295///
8396/// Example code to construct a CA and a signed certificate:
8497///
@@ -158,6 +171,7 @@ where
158171 ) ]
159172 pub fn build ( self ) -> Result < CertificatePair < SKP > , CreateCertificateError < SKP :: Error > > {
160173 let validity = Validity :: from_now ( * self . validity ) . context ( ParseValiditySnafu ) ?;
174+ let subject_for_error = & self . subject ;
161175 let subject: Name = self . subject . parse ( ) . context ( ParseSubjectSnafu {
162176 subject : self . subject ,
163177 } ) ?;
@@ -172,17 +186,17 @@ where
172186
173187 let ca_validity = self . signed_by . ca_cert ( ) . tbs_certificate . validity ;
174188 let ca_not_after = ca_validity. not_after . to_system_time ( ) ;
189+ let ca_not_before = ca_validity. not_before . to_system_time ( ) ;
175190 let cert_not_after = validity. not_after . to_system_time ( ) ;
176- if ca_not_after < cert_not_after {
177- warn ! (
178- ca. validity = ?ca_validity,
179- cert. validity = ?validity,
180- ca. not_after = ?ca_not_after,
181- cert. not_after = ?cert_not_after,
182- subject = ?subject,
183- "The lifetime of certificate authority is shorted than the lifetime of the generated certificate" ,
184- ) ;
185- }
191+ let cert_not_before = validity. not_before . to_system_time ( ) ;
192+
193+ ensure ! ( ca_not_after > cert_not_after, CertOutlivesCaSnafu {
194+ subject: subject_for_error. to_string( ) ,
195+ ca_not_after,
196+ ca_not_before,
197+ cert_not_after,
198+ cert_not_before,
199+ } ) ;
186200
187201 let spki_pem = key_pair
188202 . verifying_key ( )
@@ -306,7 +320,7 @@ mod tests {
306320 . subject ( "CN=trino-coordinator-default-0" )
307321 . subject_alternative_dns_names ( & sans)
308322 . subject_alternative_ip_addresses ( & san_ips)
309- . validity ( Duration :: from_days_unchecked ( 42 ) )
323+ . validity ( Duration :: from_hours_unchecked ( 12 ) )
310324 . key_pair ( rsa:: SigningKey :: new ( ) . unwrap ( ) )
311325 . signed_by ( & ca)
312326 . build ( )
@@ -317,10 +331,27 @@ mod tests {
317331 "CN=trino-coordinator-default-0" ,
318332 & sans,
319333 & san_ips,
320- Duration :: from_days_unchecked ( 42 ) ,
334+ Duration :: from_hours_unchecked ( 12 ) ,
321335 ) ;
322336 }
323337
338+ #[ test]
339+ fn cert_outlives_ca ( ) {
340+ let ca = CertificateAuthority :: builder_with_ecdsa ( )
341+ . validity ( Duration :: from_days_unchecked ( 365 ) )
342+ . build ( )
343+ . expect ( "failed to build CA" ) ;
344+
345+ let err = CertificatePair :: builder ( )
346+ . subject ( "CN=Test" )
347+ . signed_by ( & ca)
348+ . validity ( Duration :: from_days_unchecked ( 366 ) )
349+ . build ( )
350+ . err ( )
351+ . expect ( "Certificate creation must error" ) ;
352+ assert ! ( matches!( err, CreateCertificateError :: CertOutlivesCa { .. } ) ) ;
353+ }
354+
324355 fn assert_certificate_attributes (
325356 certificate : & TbsCertificateInner ,
326357 subject : & str ,
0 commit comments