Fix CRL verification failing due to client cert not being in chain (#29582)

closes #19853

Signed-off-by: Micah Algard <micahalgard@gmail.com>
Signed-off-by: rmartinc <rmartinc@redhat.com>


Co-authored-by: Micah Algard <micahalgard@gmail.com>
Co-authored-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
Ricardo Martin 2024-05-17 11:28:07 +02:00 committed by GitHub
parent 34a61d72e5
commit 74a80997c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 172 additions and 12 deletions

View file

@ -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 * @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 { public static void check(X509Certificate[] certs, X509CRL crl, KeycloakSession session) throws GeneralSecurityException {
if (certs.length < 2) { if (certs == null || certs.length < 1) {
throw new GeneralSecurityException("Not possible to verify signature on CRL. X509 certificate doesn't have CA chain available on it"); throw new GeneralSecurityException("Not possible to verify signature on CRL because no certificate chain was passed.");
} }
X500Principal crlIssuerPrincipal = crl.getIssuerX500Principal(); X500Principal crlIssuerPrincipal = crl.getIssuerX500Principal();
X509Certificate crlSignatureCertificate = null; X509Certificate crlSignatureCertificate = null;
// Try to find the certificate in the CA chain, which was used to sign the CRL // Try to find the certificate in the CA chain, which was used to sign the CRL
for (int i=1 ; i<certs.length ; i++) { for (X509Certificate currentCACert: certs) {
X509Certificate currentCACert = certs[i];
if (crlIssuerPrincipal.equals(currentCACert.getSubjectX500Principal())) { if (crlIssuerPrincipal.equals(currentCACert.getSubjectX500Principal())) {
crlSignatureCertificate = currentCACert; crlSignatureCertificate = currentCACert;
@ -110,12 +109,9 @@ public final class CRLUtils {
// Check if CRL issuer has trust anchor with the checked certificate (See https://tools.ietf.org/html/rfc5280#section-6.3.3 , paragraph (f)) // Check if CRL issuer has trust anchor with the checked certificate (See https://tools.ietf.org/html/rfc5280#section-6.3.3 , paragraph (f))
Set<X500Principal> certificateCAPrincipals = Arrays.asList(certs).stream() Set<X500Principal> certificateCAPrincipals = Arrays.asList(certs).stream()
.map(X509Certificate::getSubjectX500Principal) .map(X509Certificate::getIssuerX500Principal)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
// Remove the checked certificate itself
certificateCAPrincipals.remove(certs[0].getSubjectX500Principal());
X509Certificate currentCRLAnchorCertificate = crlSignatureCertificate; X509Certificate currentCRLAnchorCertificate = crlSignatureCertificate;
X500Principal currentCRLAnchorPrincipal = crlIssuerPrincipal; X500Principal currentCRLAnchorPrincipal = crlIssuerPrincipal;

View file

@ -0,0 +1,35 @@
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----

View file

@ -0,0 +1,54 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
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
-----END ENCRYPTED PRIVATE KEY-----

View file

@ -71,6 +71,8 @@
<include>ca.crt</include> <include>ca.crt</include>
<include>client.crt</include> <include>client.crt</include>
<include>client.key</include> <include>client.key</include>
<include>client-ca.crt</include>
<include>client-ca.key</include>
<include>*.crl</include> <include>*.crl</include>
</includes> </includes>
</resource> </resource>

View file

@ -183,10 +183,18 @@ public abstract class AbstractX509AuthenticationTest extends AbstractTestRealmKe
cliArgs.append("--ignore-ssl-errors=true "); cliArgs.append("--ignore-ssl-errors=true ");
cliArgs.append("--web-security=false "); cliArgs.append("--web-security=false ");
cliArgs.append("--ssl-certificates-path=").append(authServerHome).append(certificatesPath).append(" "); if (certificatesPath != null) {
cliArgs.append("--ssl-client-certificate-file=").append(authServerHome).append(clientCertificateFile).append(" "); cliArgs.append("--ssl-certificates-path=").append(authServerHome).append(certificatesPath).append(" ");
cliArgs.append("--ssl-client-key-file=").append(authServerHome).append(clientKeyFile).append(" "); }
cliArgs.append("--ssl-client-key-passphrase=" + clientKeyPassword).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()); phantomjsCliArgs = new SetSystemProperty("keycloak.phantomjs.cli.args", cliArgs.toString());
} }

View file

@ -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");
}
}