From 99eafb1a5e6923ebc65664ff1d205182fd7a0a58 Mon Sep 17 00:00:00 2001 From: Ricardo Martin Date: Fri, 17 May 2024 11:28:07 +0200 Subject: [PATCH] Fix CRL verification failing due to client cert not being in chain (#29582) closes #19853 Signed-off-by: Micah Algard Signed-off-by: rmartinc Co-authored-by: Micah Algard Co-authored-by: rmartinc (cherry picked from commit 74a80997c79928bc928bc7ff9402b47f06aa3a97) --- .../java/org/keycloak/utils/CRLUtils.java | 12 ++-- .../auth-server/common/keystore/client-ca.crt | 35 ++++++++++ .../auth-server/common/keystore/client-ca.key | 54 +++++++++++++++ .../servers/auth-server/quarkus/pom.xml | 2 + .../x509/AbstractX509AuthenticationTest.java | 16 +++-- .../X509SingleCertificateBrowserCRLTest.java | 65 +++++++++++++++++++ 6 files changed, 172 insertions(+), 12 deletions(-) create mode 100644 testsuite/integration-arquillian/servers/auth-server/common/keystore/client-ca.crt create mode 100644 testsuite/integration-arquillian/servers/auth-server/common/keystore/client-ca.key create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509SingleCertificateBrowserCRLTest.java diff --git a/services/src/main/java/org/keycloak/utils/CRLUtils.java b/services/src/main/java/org/keycloak/utils/CRLUtils.java index 862b0d1549f..7b883b0bb9a 100644 --- a/services/src/main/java/org/keycloak/utils/CRLUtils.java +++ b/services/src/main/java/org/keycloak/utils/CRLUtils.java @@ -51,16 +51,15 @@ public final class CRLUtils { * @throws GeneralSecurityException if some error in validation happens. Typically certificate not valid, or CRL signature not valid */ public static void check(X509Certificate[] certs, X509CRL crl, KeycloakSession session) throws GeneralSecurityException { - if (certs.length < 2) { - throw new GeneralSecurityException("Not possible to verify signature on CRL. X509 certificate doesn't have CA chain available on it"); + if (certs == null || certs.length < 1) { + throw new GeneralSecurityException("Not possible to verify signature on CRL because no certificate chain was passed."); } X500Principal crlIssuerPrincipal = crl.getIssuerX500Principal(); X509Certificate crlSignatureCertificate = null; // Try to find the certificate in the CA chain, which was used to sign the CRL - for (int i=1 ; i certificateCAPrincipals = Arrays.asList(certs).stream() - .map(X509Certificate::getSubjectX500Principal) + .map(X509Certificate::getIssuerX500Principal) .collect(Collectors.toSet()); - // Remove the checked certificate itself - certificateCAPrincipals.remove(certs[0].getSubjectX500Principal()); - X509Certificate currentCRLAnchorCertificate = crlSignatureCertificate; X500Principal currentCRLAnchorPrincipal = crlIssuerPrincipal; diff --git a/testsuite/integration-arquillian/servers/auth-server/common/keystore/client-ca.crt b/testsuite/integration-arquillian/servers/auth-server/common/keystore/client-ca.crt new file mode 100644 index 00000000000..373987fde27 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/common/keystore/client-ca.crt @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- diff --git a/testsuite/integration-arquillian/servers/auth-server/common/keystore/client-ca.key b/testsuite/integration-arquillian/servers/auth-server/common/keystore/client-ca.key new file mode 100644 index 00000000000..79e5798f83f --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/common/keystore/client-ca.key @@ -0,0 +1,54 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIJrTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIS13x7pyoT7YCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBADJ2geZQ+lx0YP+VUtwxh2BIIJ +UDMmuSTfEKvRmNCQ2FJRysj6kPNum3aUIsovSzsaHo634BHoIZhdFifUrqCzSEE9 +52yCf1ROiTiZi6AT/HmrX97p3g1ZbtIeNoOafdBuzUA9uJF9x14CitryNnrq10wx +r4vjMx3rKvmsPW1SlDPR+gfXiqFt7M+883/1aOwpWDDWt5J8u0zoBJoUaWu1nvXa +aRIvLYERWHxAArUjePIpYLD3SyWRBvzjsPG18AhOiUoxQsUkUsGiojiBL9z4IIFd +YWkb1MgOT96fXhGh36mfzGuMDzgvk6/Sf7ayxooeVqvY2glmQYa1XCzuzE5PN3I9 +a71PFLG3E0HCrQQ6IoZW0CjXvmzF/R0TtOgsxVczdHqr1UkgTUeIvv4tqAdyionR +v8JbXxD64ju5Y6j0g9169Vg1PWASY3uYBETdWUM2e9YP5Yvt1hV8Hps+/37K0UOi +XzSFB3zwy/zLXsUok5cc+CSnHPqxkVdHWuohLHik9kgYGzTot5qk8Iom4GV1hXWC +j+pmf2+/zwa6+BACXvLrQ7VXeiVYhNSvfms/kixzvEkwmlBAlgVShzRbua7GUZDK +IKjfzaLbqr6C/OlsiC+zDtN8iv4WSahtw/ESd14iNH4Efggy8qQDDAi5axZ3+acC +Z8zVvYX6sfHWHrvCmGQPnrqRuF7/ZqJGoNkLR5Xfb5hHNoohdoe7E/o6j/5gmKoN +/shs2q2s9lez3yxwizFBVFzds+D1b7T/RBEZE0VvwW3m2OIRrM294ieHiRduSxw1 +kKHwdxbC0+C6sT2D7sdLtL2jmxG2X79P6GPFU7pSHuROX4b2ub4nhw1hMmui3wAK +rUniJSLy911WH9YB+x/lRWdBpiUK1Ws97DdTmDqcVSFSh0aOwF9MtwqAJKsXBIXk +SFwR95fqFSc0r3mein7MRDMyebH8fAvSpMb8AHiV5M6dfSRfP2vsIVBAdzeEzllD +orIkD3uc9FU7wgbubFwgYyvKsazvKqD+UA1SIBK1WE2MszsZPLtQ9GCanARAfWAZ +0xq9D8+d3jAXO/wbMb+CZcvwIB7UJ0hcFP1tKwiDbC73ovdq8hxrGI7KhehTnJis +HG53LFN9mAsr7WJudYPLQMdkjG5U/HECRac10//XzC+d9cz3TnGPRSG49CdY1izY +pq12FZHzIrvXu8yqKa1vEihCGUE9G3gjt+VxiH1ZO8mvRJVgQedJ0UQueo56CCBx +qM5ewehocCnGeoOftcIkmVz+HDDutqYPDo3EPgphq9b02EggAfCped29BmLaZgyS +MY5Gjn0MyFsdCXWVxuzOdMyL3yd33w1O6Rq1mGykk90Xl1jdFwhgtOeqH6r7dMV8 +rGJVWPvnVtfddYhlg1zhu5ndit4fAxbyPHu1CnbFXnbMOwbyP0W38GWZQKDV6J9S +uGVV/Qqm6iG4SLqjoWpTyCtK6LShjG/3QDcS7hjhHwTsQQJQFMRGGCBY/xCWo0gG +mXIROcp6Xc31OXgptqwHvDFIA4648RTBg4kXNteVeN7+H06MwisiDfB6AJGIreuK +0CpJsIMtQBSqG63FecMqzj8H0I86lt8pbYl4sVUCEKvfo0zwJj06WuowkVzl2flI +6Km7hEWfmQkyRoZp48OuB2qWoKKJaQsYfrL+kDTyY90qwyg5kakUEghA8LVgOCXO +IKVcND8KwEWj4xgynkea2b8klD9I0MUsvH/gMWva6bVAs3IXIY7SDsroSttTzp1E +7goX50CDeKI+tYS1AG5+Xv/TElrM2GgHZO760JXGaIssVnyZxiyzX6LCZInRNgJH +fO0EvmBdY8RkihTFNScLDUsQcn4rh/yRiLgClFn4u0cnE3eKpgPdIBsB1cO0b+g4 +MqEXVgaNtzOywAAKu2vbVVxReGJqIOfJo/4u1sXgHmIAPQrveL0/o1X5ztzxDT1Q +Qi/u94hU/omH+vLVwc4OukKMpBiacD4C7fs1xicqkpf5SzHW7ft9Y0GECVyVjmSS +7ACl/r97pfj/WNlV649c0KSSg7H3Y/18i/dqw1/Rjdn5JxMxPINhxpo+JUZ/Rc9p +nN+a7tJwDWizu58+Hk0/I6Reh6yCxDCPqX5M0v204pL2CSysOptmLmZb9512NXii +KdrdvfB9g4fqiki4GoCYLfeMTpwsISSALIBrNpeuoGAsje44fMRgK6kozHl/ecri +oCwrbXYhRDty+fg217k/TZ5gZb0hsgOiuHlvIIZQ5/AESooDt2jKO+pfKeI2VT3R +M5DvhTumnygyO1hQBBoESl1+V9+4/3Rr1LfvD4mbQ+jqpNXQbsVDSZszc4DyK37G +DKboXF3DNZnocxQA2zCgYe37lrLs5nIpJTBtReseoaMykHv3MSFYLfqEyEyPMXGq +DPqsoXNU5g3S35huuUK5nuwvL1wSVyD1Smag5YQP1gV17k3XlvYap2XuUPn1lnmo ++zpwzciZ4RLVTa8NuDTj9fkBY68mgPS/sbj1H3eVhSAn3+mT4Vuky0ueVY0zi80Z +N8B8Yv3CMI2ek9FIKVpMtQBy6vPZhIssif85K26/7YUDF1hB8tYE3c1pCDOd68sO +yaR0Trhs+voZaey+kPl2IAda6POYQm/YrZ5uP3ETvy1/jz+b1tP6drYNg5zg9jYu +KJu0n/V50QmGQzQHSdruIRW8o0hqDQd6vxh27d9n/9D7MDTc39t6ug9cI/ppZ7Ec +K3BRl9P0vO7oCbFqi2jvti3cJMSdbj+OsESZDz2OKXxWWHSZzqeB4+uS6+Y6zPWu +Xur6ZOj9RJLlXmHjCbXafbCnHQ5Ob7OmeFpDhOZk1e3wbRdh49YEXhB0xPwix2FR +Gv6HxuDFyJK2a9VTiyyICsBMF2NSHdvv6jouD9GQNNL23ta9xBIlgHpG9F8kuOAj +ASsGUGuKogrWrigHQaylKK1v+sFz/GJyvCPmKMPxxykOemEjCyz6Jk1ZbtlRnYBw +Buxgyo07cjA2Ls+h7HsSeLOcCWPDYSmlH487uvrsoJfZxsnA9WbZ/5fe5cpw5dJh +dh20suavMNtEDpvx/Foj1kSiUeCAVl1p6fXRn3tpQ+QOYmhn79LqQTAFhwGgFzSX +fLw16gp4Dyogsm/4d7tpFbL/XxtcMX4YgnZMQLBVZGLD+l+1+h7xP1EjgOaKNPDD +lT3o2oN97UH8cTMJf1vdRVVP1kZJRY7b2bZbWcTnAnL/ +-----END ENCRYPTED PRIVATE KEY----- diff --git a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml index 6bbd8ab3e41..423545438cc 100644 --- a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml @@ -71,6 +71,8 @@ ca.crt client.crt client.key + client-ca.crt + client-ca.key *.crl diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/AbstractX509AuthenticationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/AbstractX509AuthenticationTest.java index 0c9a7795424..195af0672f4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/AbstractX509AuthenticationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/AbstractX509AuthenticationTest.java @@ -183,10 +183,18 @@ public abstract class AbstractX509AuthenticationTest extends AbstractTestRealmKe cliArgs.append("--ignore-ssl-errors=true "); cliArgs.append("--web-security=false "); - cliArgs.append("--ssl-certificates-path=").append(authServerHome).append(certificatesPath).append(" "); - cliArgs.append("--ssl-client-certificate-file=").append(authServerHome).append(clientCertificateFile).append(" "); - cliArgs.append("--ssl-client-key-file=").append(authServerHome).append(clientKeyFile).append(" "); - cliArgs.append("--ssl-client-key-passphrase=" + clientKeyPassword).append(" "); + if (certificatesPath != null) { + cliArgs.append("--ssl-certificates-path=").append(authServerHome).append(certificatesPath).append(" "); + } + if (clientCertificateFile != null) { + cliArgs.append("--ssl-client-certificate-file=").append(authServerHome).append(clientCertificateFile).append(" "); + } + if (clientKeyFile != null) { + cliArgs.append("--ssl-client-key-file=").append(authServerHome).append(clientKeyFile).append(" "); + } + if (clientKeyPassword != null) { + cliArgs.append("--ssl-client-key-passphrase=").append(clientKeyPassword).append(" "); + } phantomjsCliArgs = new SetSystemProperty("keycloak.phantomjs.cli.args", cliArgs.toString()); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509SingleCertificateBrowserCRLTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509SingleCertificateBrowserCRLTest.java new file mode 100644 index 00000000000..b3678145ee5 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/x509/X509SingleCertificateBrowserCRLTest.java @@ -0,0 +1,65 @@ +/* + * Copyright 2024 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.x509; + +import org.jboss.arquillian.drone.api.annotation.Drone; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.keycloak.authentication.authenticators.x509.X509AuthenticatorConfigModel; +import org.keycloak.authentication.authenticators.x509.X509AuthenticatorConfigModel.IdentityMapperType; +import org.keycloak.authentication.authenticators.x509.X509AuthenticatorConfigModel.MappingSourceType; +import org.keycloak.testsuite.util.PhantomJSBrowser; +import org.openqa.selenium.WebDriver; + +/** + * + * @author rmartinc + */ +public class X509SingleCertificateBrowserCRLTest extends AbstractX509AuthenticationTest { + + @ClassRule + public static CRLRule crlRule = new CRLRule(); + + @Drone + @PhantomJSBrowser + private WebDriver phantomJS; + + @Before + public void replaceTheDefaultDriver() { + replaceDefaultWebDriver(phantomJS); + } + + @BeforeClass + public static void onBeforeTestClass() { + // configure single certificate without CA cert + configurePhantomJS(null, "/client-ca.crt", "/client-ca.key", "password"); + } + + @Test + public void loginSuccessWithSingleCertificateEmptyRevocationListFromHttp() throws Exception { + X509AuthenticatorConfigModel config = + new X509AuthenticatorConfigModel() + .setCRLEnabled(true) + .setCRLRelativePath(CRLRule.CRL_RESPONDER_ORIGIN + "/" + EMPTY_CRL_PATH) + .setConfirmationPageAllowed(true) + .setMappingSourceType(MappingSourceType.SUBJECTDN_EMAIL) + .setUserIdentityMapperType(IdentityMapperType.USERNAME_EMAIL); + x509BrowserLogin(config, userId, "test-user@localhost", "test-user@localhost"); + } +}