@@ -438,9 +438,35 @@ fn check_issuer_independent_properties(
438438 } ) ?;
439439 untrusted:: read_all_optional ( cert. eku , Error :: BadDer , |input| check_eku ( input, eku) ) ?;
440440
441+ if let Some ( key_usage) = cert. key_usage {
442+ // RFC 5280 requires the KeyUsage extension be present in CA certificates, but historically
443+ // its absence has been tolerated and treated as if all usages were asserted. We follow that
444+ // convention here and only enforce keyCertSign when a KeyUsage extension is present.
445+ check_key_usage_cert_sign ( key_usage, role) ?;
446+ }
447+
441448 Ok ( ( ) )
442449}
443450
451+ /// Check that issuer certificate have the keyCertSign bit set in their KeyUsage extension.
452+ ///
453+ /// <https://www.rfc-editor.org/info/rfc5280/#section-4.2.1.3>
454+ /// <https://www.rfc-editor.org/info/rfc5280/#section-6.1.4> step (n)
455+ fn check_key_usage_cert_sign ( key_usage : untrusted:: Input < ' _ > , role : Role ) -> Result < ( ) , Error > {
456+ // The KeyUsage extension is a BIT STRING; keyCertSign is bit 5.
457+ const KEY_CERT_SIGN : usize = 5 ;
458+ let bit_string = der:: expect_tag ( & mut untrusted:: Reader :: new ( key_usage) , der:: Tag :: BitString ) ?;
459+ match (
460+ role,
461+ der:: bit_string_flags ( bit_string) ?. bit_set ( KEY_CERT_SIGN ) ,
462+ ) {
463+ ( Role :: Issuer , true ) | ( Role :: EndEntity , false ) => Ok ( ( ) ) ,
464+ ( Role :: Issuer , false ) => Err ( Error :: IssuerNotCertSigner ) ,
465+ // Disallowed per https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.9
466+ ( Role :: EndEntity , true ) => Err ( Error :: EndEntityCertHasCertSignKeyUsage ) ,
467+ }
468+ }
469+
444470fn check_eku (
445471 input : Option < & mut untrusted:: Reader < ' _ > > ,
446472 eku : & dyn ExtendedKeyUsageValidator ,
@@ -1285,6 +1311,66 @@ mod tests {
12851311 CertifiedIssuer :: signed_by ( params, key, issuer) . unwrap ( )
12861312 }
12871313
1314+ #[ test]
1315+ fn intermediate_without_key_cert_sign_rejected ( ) {
1316+ // An intermediate that carries a KeyUsage extension which does not assert keyCertSign
1317+ // must not be usable as a CA certificate during path building.
1318+ let trust_anchor = make_issuer ( "Trust Anchor" ) ;
1319+ let trust_anchors = & [ anchor_from_trusted_cert ( trust_anchor. der ( ) ) . unwrap ( ) ] ;
1320+
1321+ let mut params = issuer_params ( "Intermediate" ) ;
1322+ params. key_usages = vec ! [ rcgen:: KeyUsagePurpose :: CrlSign ] ;
1323+ let key = KeyPair :: generate_for ( test_utils:: RCGEN_SIGNATURE_ALG ) . unwrap ( ) ;
1324+ let intermediate = CertifiedIssuer :: signed_by ( params, key, & trust_anchor) . unwrap ( ) ;
1325+ let intermediates = & [ intermediate. der ( ) . clone ( ) ] ;
1326+
1327+ let ee = make_end_entity ( & intermediate) ;
1328+ let ee_cert = & EndEntityCert :: try_from ( ee. cert . der ( ) ) . unwrap ( ) ;
1329+
1330+ assert ! ( matches!(
1331+ verify_chain( trust_anchors, intermediates, ee_cert, None , None ) ,
1332+ Err ( ControlFlow :: Continue ( Error :: IssuerNotCertSigner ) )
1333+ ) ) ;
1334+ }
1335+
1336+ #[ test]
1337+ fn intermediate_without_key_usage_accepted ( ) {
1338+ // An intermediate without any KeyUsage extension is treated as if all usages are asserted,
1339+ // so it remains usable as a CA certificate.
1340+ let trust_anchor = make_issuer ( "Trust Anchor" ) ;
1341+ let trust_anchors = & [ anchor_from_trusted_cert ( trust_anchor. der ( ) ) . unwrap ( ) ] ;
1342+
1343+ let mut params = issuer_params ( "Intermediate" ) ;
1344+ params. key_usages = vec ! [ ] ;
1345+ let key = KeyPair :: generate_for ( test_utils:: RCGEN_SIGNATURE_ALG ) . unwrap ( ) ;
1346+ let intermediate = CertifiedIssuer :: signed_by ( params, key, & trust_anchor) . unwrap ( ) ;
1347+ let intermediates = & [ intermediate. der ( ) . clone ( ) ] ;
1348+
1349+ let ee = make_end_entity ( & intermediate) ;
1350+ let ee_cert = & EndEntityCert :: try_from ( ee. cert . der ( ) ) . unwrap ( ) ;
1351+
1352+ assert ! ( verify_chain( trust_anchors, intermediates, ee_cert, None , None ) . is_ok( ) ) ;
1353+ }
1354+
1355+ #[ test]
1356+ fn trust_anchor_without_key_cert_sign_accepted ( ) {
1357+ // The keyCertSign check applies only to intermediate certificates, not trust anchors.
1358+ // A trust anchor whose KeyUsage omits keyCertSign must still be usable.
1359+ let mut ta_params = issuer_params ( "Trust Anchor" ) ;
1360+ ta_params. key_usages = vec ! [ rcgen:: KeyUsagePurpose :: CrlSign ] ;
1361+ let ta_key = KeyPair :: generate_for ( test_utils:: RCGEN_SIGNATURE_ALG ) . unwrap ( ) ;
1362+ let trust_anchor = CertifiedIssuer :: self_signed ( ta_params, ta_key) . unwrap ( ) ;
1363+ let trust_anchors = & [ anchor_from_trusted_cert ( trust_anchor. der ( ) ) . unwrap ( ) ] ;
1364+
1365+ let intermediate = make_intermediate ( "Intermediate" , & trust_anchor) ;
1366+ let intermediates = & [ intermediate. der ( ) . clone ( ) ] ;
1367+
1368+ let ee = make_end_entity ( & intermediate) ;
1369+ let ee_cert = & EndEntityCert :: try_from ( ee. cert . der ( ) ) . unwrap ( ) ;
1370+
1371+ assert ! ( verify_chain( trust_anchors, intermediates, ee_cert, None , None ) . is_ok( ) ) ;
1372+ }
1373+
12881374 fn build_and_verify_degenerate_chain (
12891375 intermediate_count : usize ,
12901376 trust_anchor : ChainTrustAnchor ,
0 commit comments