Uploaded image for project: 'Red Hat build of Keycloak'
  1. Red Hat build of Keycloak
  2. RHBK-2348

CRL Verification failing due to client certificate not being in a chain [GHI#19853]

XMLWordPrintable

    • False
    • Hide

      None

      Show
      None
    • False

      Before reporting an issue

      [X] I have searched existing issues
      [X] I have reproduced the issue with the latest release

      Area

      authentication

      Describe the bug

      Behavior:

      When we Enable X509 Browser certificate authentication and turn CRL checking on with the CRL file option, the user always fails to authenticate. This seems to be due to Keycloak expecting a chain of certificates from the Client cert rather than just the client cert. As far as I can tell, it is not standard for the client cert to contain the CA Chain, so Keycloak always end up with this error:

      ```20T21:27:46.901Z","sequence":8562,"loggerClassName":"org.jboss.logging.DelegatingBasicLogger","loggerName":"org.keycloak.services","level":"ERROR","message":"Not possible to verify signature on CRL. X509 certificate doesn't have CA chain available on it","threadName":"executor-thread-3","threadId":118,"mdc":{},"ndc":"","hostName":"ip-10-38-1-109.us-gov-west-1.compute.internal","processName":"QuarkusEntryPoint","processId":77,"exception":{"refId":1,"exceptionType":"java.security.GeneralSecurityException","message":"Not possible to verify signature on CRL. X509 certificate doesn't have CA chain available on it","frames":[

      {"class":"org.keycloak.utils.CRLUtils","method":"check","line":55}

      ,

      {"class":"org.keycloak.authentication.authenticators.x509.CertificateValidator","method":"checkRevocationStatusUsingCRL","line":737}

      ,

      {"class":"org.keycloak.authentication.authenticators.x509.CertificateValidator","method":"checkRevocationStatusUsingCRLDistributionPoints","line":760}

      ,```

      Can this be fixed to accept the child client cert and get its issuer from there? Rather than forcing a minimum length of two certificates?

      ```

      public static void check(X509Certificate[] certs, X509CRL crl, KeycloakSession session) throws GeneralSecurityException {
          if (certs.length < 1) {
              throw new GeneralSecurityException("Not possible to verify signature on CRL. X509 certificate doesn't exist");
          }
      
          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=0 ; i<certs.length ; i++) {
              X509Certificate currentCert = certs[i];
              if (crlIssuerPrincipal.equals(currentCert.getIssuerX500Principal())) {
                  crlSignatureCertificate = currentCert;
      
                  log.tracef("Found certificate used to sign CRL in the CA chain of the certificate. CRL issuer: %s", crlIssuerPrincipal);
                  break;
              }
          }
      

      ```

          1. Version

      21.1.0

          1. Expected behavior

      Users be able to login with x509 Certificate and CRL checking to be working.

          1. Actual behavior

      When CRL checking is turned on in the browser auth flow, and a CRL file is specified. Users gets rejected due to failed CRL verification.

      ``{{20T21:27:46.901Z","sequence":8562,"loggerClassName":"org.jboss.logging.DelegatingBasicLogger","loggerName":"org.keycloak.services","level":"ERROR","message":"Not possible to verify signature on CRL. X509 certificate doesn't have CA chain available on it","threadName":"executor-thread-3","threadId":118,"mdc":{},"ndc":"","hostName":"ip-10-38-1-109.us-gov-west-1.compute.internal","processName":"QuarkusEntryPoint","processId":77,"exception":{"refId":1,"exceptionType":"java.security.GeneralSecurityException","message":"Not possible to verify signature on CRL. X509 certificate doesn't have CA chain available on it","frames":[

      {"class":"org.keycloak.utils.CRLUtils","method":"check","line":55}

      ,

      {"class":"org.keycloak.authentication.authenticators.x509.CertificateValidator","method":"checkRevocationStatusUsingCRL","line":737}

      ,

      {"class":"org.keycloak.authentication.authenticators.x509.CertificateValidator","method":"checkRevocationStatusUsingCRLDistributionPoints","line":760}

      ,}}``

      Can this be fixed to accept the child client cert and get its issuer from there? Rather than forcing a minimum length of two certificates?

      ```

      public static void check(X509Certificate[] certs, X509CRL crl, KeycloakSession session) throws GeneralSecurityException {
          if (certs.length < 1) {
              throw new GeneralSecurityException("Not possible to verify signature on CRL. X509 certificate doesn't exist");
          }
      
          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=0 ; i<certs.length ; i++) {
              X509Certificate currentCert = certs[i];
              if (crlIssuerPrincipal.equals(currentCert.getIssuerX500Principal())) {
                  crlSignatureCertificate = currentCert;
      
                  log.tracef("Found certificate used to sign CRL in the CA chain of the certificate. CRL issuer: %s", crlIssuerPrincipal);
                  break;
              }
          }
      

      ```

      How to Reproduce?

      1. Create a login Browser flow using x509 certificate check
      2. Turn on CRL checking and specify a CRL file
      3. Login with a certificate that is signed by a single root CA

      Anything else?

      No response

              Unassigned Unassigned
              pvlha Pavel Vlha
              Keycloak Core Clients
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Created:
                Updated:
                Resolved: