1
- use std:: { fmt:: Debug , net:: IpAddr } ;
1
+ use std:: { fmt:: Debug , net:: IpAddr , time :: SystemTime } ;
2
2
3
3
use bon:: Builder ;
4
4
use const_oid:: db:: rfc5280:: { ID_KP_CLIENT_AUTH , ID_KP_SERVER_AUTH } ;
5
5
use rsa:: pkcs8:: EncodePublicKey ;
6
- use snafu:: { ResultExt , Snafu } ;
6
+ use snafu:: { ResultExt , Snafu , ensure } ;
7
7
use stackable_operator:: time:: Duration ;
8
8
use tracing:: { debug, instrument, warn} ;
9
9
use x509_cert:: {
62
62
63
63
#[ snafu( display( "failed to build certificate" ) ) ]
64
64
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
+ } ,
65
78
}
66
79
67
80
/// This builder builds certificates of type [`CertificatePair`].
77
90
/// - A default validity of [`DEFAULT_CERTIFICATE_VALIDITY`]
78
91
/// - A randomly generated serial number
79
92
/// - 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`].
82
95
///
83
96
/// Example code to construct a CA and a signed certificate:
84
97
///
@@ -158,6 +171,7 @@ where
158
171
) ]
159
172
pub fn build ( self ) -> Result < CertificatePair < SKP > , CreateCertificateError < SKP :: Error > > {
160
173
let validity = Validity :: from_now ( * self . validity ) . context ( ParseValiditySnafu ) ?;
174
+ let subject_for_error = & self . subject ;
161
175
let subject: Name = self . subject . parse ( ) . context ( ParseSubjectSnafu {
162
176
subject : self . subject ,
163
177
} ) ?;
@@ -172,17 +186,17 @@ where
172
186
173
187
let ca_validity = self . signed_by . ca_cert ( ) . tbs_certificate . validity ;
174
188
let ca_not_after = ca_validity. not_after . to_system_time ( ) ;
189
+ let ca_not_before = ca_validity. not_before . to_system_time ( ) ;
175
190
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
+ } ) ;
186
200
187
201
let spki_pem = key_pair
188
202
. verifying_key ( )
@@ -306,7 +320,7 @@ mod tests {
306
320
. subject ( "CN=trino-coordinator-default-0" )
307
321
. subject_alternative_dns_names ( & sans)
308
322
. subject_alternative_ip_addresses ( & san_ips)
309
- . validity ( Duration :: from_days_unchecked ( 42 ) )
323
+ . validity ( Duration :: from_hours_unchecked ( 12 ) )
310
324
. key_pair ( rsa:: SigningKey :: new ( ) . unwrap ( ) )
311
325
. signed_by ( & ca)
312
326
. build ( )
@@ -317,10 +331,27 @@ mod tests {
317
331
"CN=trino-coordinator-default-0" ,
318
332
& sans,
319
333
& san_ips,
320
- Duration :: from_days_unchecked ( 42 ) ,
334
+ Duration :: from_hours_unchecked ( 12 ) ,
321
335
) ;
322
336
}
323
337
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
+
324
355
fn assert_certificate_attributes (
325
356
certificate : & TbsCertificateInner ,
326
357
subject : & str ,
0 commit comments