4646import org .mockito .ArgumentCaptor ;
4747
4848import java .io .ByteArrayInputStream ;
49+ import java .io .File ;
50+ import java .io .FileInputStream ;
4951import java .io .IOException ;
5052import java .nio .charset .StandardCharsets ;
5153import java .nio .file .Files ;
5860import java .security .cert .X509Certificate ;
5961import java .time .Clock ;
6062import java .time .Instant ;
63+ import java .time .ZonedDateTime ;
64+ import java .time .temporal .ChronoUnit ;
6165import java .util .ArrayList ;
6266import java .util .Base64 ;
6367import java .util .HashMap ;
@@ -1086,21 +1090,60 @@ public void testReconcileCasWhenUserManagedCertsAreMissingThrows(Vertx vertx, Ve
10861090 }
10871091
10881092 @ Test
1089- public void testUserManagedCertsNotReconciled (Vertx vertx , VertxTestContext context )
1090- throws IOException , CertificateException , KeyStoreException , NoSuchAlgorithmException {
1093+ public void testUserManagedCertsNotReconciled (Vertx vertx , VertxTestContext context ) throws IOException {
10911094 CertificateAuthority certificateAuthority = new CertificateAuthorityBuilder ()
1092- .withValidityDays (2 )
1093- .withRenewalDays (3 )
1095+ .withValidityDays (100 )
1096+ .withRenewalDays (10 )
10941097 .withGenerateCertificateAuthority (false )
10951098 .build ();
10961099
1097- List <Secret > clusterCaSecrets = initialClusterCaSecrets (certificateAuthority );
1098- Secret initialClusterCaKeySecret = clusterCaSecrets .get (0 );
1099- Secret initialClusterCaCertSecret = clusterCaSecrets .get (1 );
1100-
1101- List <Secret > clientsCaSecrets = initialClientsCaSecrets (certificateAuthority );
1102- Secret initialClientsCaKeySecret = clientsCaSecrets .get (0 );
1103- Secret initialClientsCaCertSecret = clientsCaSecrets .get (1 );
1100+ Subject sbj = new Subject .Builder ()
1101+ .withOrganizationName ("io.strimzi" )
1102+ .withCommonName ("ca" ).build ();
1103+
1104+ File rootKey = Files .createTempFile ("key-" , ".key" ).toFile ();
1105+ rootKey .deleteOnExit ();
1106+ File rootCert = Files .createTempFile ("crt-" , ".crt" ).toFile ();
1107+ rootCert .deleteOnExit ();
1108+ File intermediateKey1 = Files .createTempFile ("key-" , ".key" ).toFile ();
1109+ intermediateKey1 .deleteOnExit ();
1110+ File intermediateCert1 = Files .createTempFile ("crt-" , ".crt" ).toFile ();
1111+ intermediateCert1 .deleteOnExit ();
1112+ File intermediateKey2 = Files .createTempFile ("key-" , ".key" ).toFile ();
1113+ intermediateKey2 .deleteOnExit ();
1114+ File intermediateCert2 = Files .createTempFile ("crt-" , ".crt" ).toFile ();
1115+
1116+ Instant now = Instant .now ();
1117+ ZonedDateTime notBefore = now .truncatedTo (ChronoUnit .SECONDS ).atZone (Clock .systemUTC ().getZone ());
1118+ ZonedDateTime notAfter = now .plus (2 , ChronoUnit .HOURS ).truncatedTo (ChronoUnit .SECONDS ).atZone (Clock .systemUTC ().getZone ());
1119+ CERT_MANAGER .generateRootCaCert (sbj , rootKey , rootCert , notBefore , notAfter , 1 );
1120+
1121+ // Generate an intermediate cert
1122+ Subject intermediateSubject1 = new Subject .Builder ().withCommonName ("IntermediateCn1" ).withOrganizationName ("MyOrganization" ).build ();
1123+ CERT_MANAGER .generateIntermediateCaCert (rootKey , rootCert , intermediateSubject1 , intermediateKey1 , intermediateCert1 , notBefore , notAfter , 1 );
1124+
1125+ // Generate an additional intermediate cert
1126+ Subject intermediateSubject2 = new Subject .Builder ().withCommonName ("IntermediateCn2" ).withOrganizationName ("MyOrganization" ).build ();
1127+ CERT_MANAGER .generateIntermediateCaCert (intermediateKey1 , intermediateCert1 , intermediateSubject2 , intermediateKey2 , intermediateCert2 , notBefore , notAfter , 1 );
1128+
1129+ String caKey = Base64 .getEncoder ().encodeToString (Files .readAllBytes (rootKey .toPath ()));
1130+
1131+ // Generate combined Pem files with CA chain in correct order
1132+ String validCombinedPem ;
1133+ try (FileInputStream int2CertFis = new FileInputStream (intermediateCert2 );
1134+ FileInputStream int1CertFis = new FileInputStream (intermediateCert1 );
1135+ FileInputStream rootCertFis = new FileInputStream (rootCert )) {
1136+ String combinedPem = String .join ("\n " ,
1137+ new String (int2CertFis .readAllBytes (), StandardCharsets .US_ASCII ),
1138+ new String (int1CertFis .readAllBytes (), StandardCharsets .US_ASCII ),
1139+ new String (rootCertFis .readAllBytes (), StandardCharsets .US_ASCII ));
1140+ validCombinedPem = Base64 .getEncoder ().encodeToString (combinedPem .getBytes (StandardCharsets .US_ASCII ));
1141+ }
1142+
1143+ Secret initialClusterCaKeySecret = ResourceUtils .createInitialCaKeySecret (NAMESPACE , NAME , AbstractModel .clusterCaKeySecretName (NAME ), caKey );
1144+ Secret initialClusterCaCertSecret = ResourceUtils .createInitialCaCertSecret (NAMESPACE , NAME , AbstractModel .clusterCaCertSecretName (NAME ), validCombinedPem , null , null );
1145+ Secret initialClientsCaKeySecret = ResourceUtils .createInitialCaKeySecret (NAMESPACE , NAME , KafkaResources .clientsCaKeySecretName (NAME ), caKey );
1146+ Secret initialClientsCaCertSecret = ResourceUtils .createInitialCaCertSecret (NAMESPACE , NAME , KafkaResources .clientsCaCertificateSecretName (NAME ), validCombinedPem , null , null );
11041147
11051148 secrets .add (initialClusterCaCertSecret );
11061149 secrets .add (initialClusterCaKeySecret );
@@ -1117,4 +1160,74 @@ public void testUserManagedCertsNotReconciled(Vertx vertx, VertxTestContext cont
11171160 async .flag ();
11181161 })));
11191162 }
1163+
1164+ @ Test
1165+ public void testUserManagedCasWithInvalidCa (Vertx vertx , VertxTestContext context ) throws IOException {
1166+ CertificateAuthority certificateAuthority = new CertificateAuthorityBuilder ()
1167+ .withValidityDays (100 )
1168+ .withRenewalDays (10 )
1169+ .withGenerateCertificateAuthority (false )
1170+ .build ();
1171+
1172+ Subject sbj = new Subject .Builder ()
1173+ .withOrganizationName ("io.strimzi" )
1174+ .withCommonName ("ca" ).build ();
1175+
1176+ File rootKey = Files .createTempFile ("key-" , ".key" ).toFile ();
1177+ rootKey .deleteOnExit ();
1178+ File rootCert = Files .createTempFile ("crt-" , ".crt" ).toFile ();
1179+ rootCert .deleteOnExit ();
1180+ File intermediateKey1 = Files .createTempFile ("key-" , ".key" ).toFile ();
1181+ intermediateKey1 .deleteOnExit ();
1182+ File intermediateCert1 = Files .createTempFile ("crt-" , ".crt" ).toFile ();
1183+ intermediateCert1 .deleteOnExit ();
1184+ File intermediateKey2 = Files .createTempFile ("key-" , ".key" ).toFile ();
1185+ intermediateKey2 .deleteOnExit ();
1186+ File intermediateCert2 = Files .createTempFile ("crt-" , ".crt" ).toFile ();
1187+
1188+ Instant now = Instant .now ();
1189+ ZonedDateTime notBefore = now .truncatedTo (ChronoUnit .SECONDS ).atZone (Clock .systemUTC ().getZone ());
1190+ ZonedDateTime notAfter = now .plus (2 , ChronoUnit .HOURS ).truncatedTo (ChronoUnit .SECONDS ).atZone (Clock .systemUTC ().getZone ());
1191+ CERT_MANAGER .generateRootCaCert (sbj , rootKey , rootCert , notBefore , notAfter , 1 );
1192+
1193+ // Generate an intermediate cert
1194+ Subject intermediateSubject1 = new Subject .Builder ().withCommonName ("IntermediateCn1" ).withOrganizationName ("MyOrganization" ).build ();
1195+ CERT_MANAGER .generateIntermediateCaCert (rootKey , rootCert , intermediateSubject1 , intermediateKey1 , intermediateCert1 , notBefore , notAfter , 1 );
1196+
1197+ // Generate an additional intermediate cert
1198+ Subject intermediateSubject2 = new Subject .Builder ().withCommonName ("IntermediateCn2" ).withOrganizationName ("MyOrganization" ).build ();
1199+ CERT_MANAGER .generateIntermediateCaCert (intermediateKey1 , intermediateCert1 , intermediateSubject2 , intermediateKey2 , intermediateCert2 , notBefore , notAfter , 1 );
1200+
1201+ String caKey = Base64 .getEncoder ().encodeToString (Files .readAllBytes (rootKey .toPath ()));
1202+
1203+ // Generate combined Pem files with CA chain in wrong order
1204+ String invalidCombinedPem ;
1205+ try (FileInputStream int2CertFis = new FileInputStream (intermediateCert2 );
1206+ FileInputStream int1CertFis = new FileInputStream (intermediateCert1 );
1207+ FileInputStream rootCertFis = new FileInputStream (rootCert )) {
1208+ String combinedPem = String .join ("\n " ,
1209+ new String (rootCertFis .readAllBytes (), StandardCharsets .US_ASCII ),
1210+ new String (int1CertFis .readAllBytes (), StandardCharsets .US_ASCII ),
1211+ new String (int2CertFis .readAllBytes (), StandardCharsets .US_ASCII ));
1212+ invalidCombinedPem = Base64 .getEncoder ().encodeToString (combinedPem .getBytes (StandardCharsets .US_ASCII ));
1213+ }
1214+
1215+ Secret initialClusterCaKeySecret = ResourceUtils .createInitialCaKeySecret (NAMESPACE , NAME , AbstractModel .clusterCaKeySecretName (NAME ), caKey );
1216+ Secret initialClusterCaCertSecret = ResourceUtils .createInitialCaCertSecret (NAMESPACE , NAME , AbstractModel .clusterCaCertSecretName (NAME ), invalidCombinedPem , null , null );
1217+ Secret initialClientsCaKeySecret = ResourceUtils .createInitialCaKeySecret (NAMESPACE , NAME , KafkaResources .clientsCaKeySecretName (NAME ), caKey );
1218+ Secret initialClientsCaCertSecret = ResourceUtils .createInitialCaCertSecret (NAMESPACE , NAME , KafkaResources .clientsCaCertificateSecretName (NAME ), invalidCombinedPem , null , null );
1219+
1220+ secrets .add (initialClusterCaCertSecret );
1221+ secrets .add (initialClusterCaKeySecret );
1222+ secrets .add (initialClientsCaCertSecret );
1223+ secrets .add (initialClientsCaKeySecret );
1224+
1225+ Checkpoint async = context .checkpoint ();
1226+ reconcileCas (vertx , certificateAuthority , certificateAuthority )
1227+ .onComplete (context .failing (e -> context .verify (() -> {
1228+ assertThat (e , instanceOf (RuntimeException .class ));
1229+ assertThat (e .getMessage (), is ("User supplied Cluster CA cert chain ca.crt is not valid. Certificates must be provided in the correct order." ));
1230+ async .flag ();
1231+ })));
1232+ }
11201233}
0 commit comments