ba807279bd
* Rewords first 3 chapters * Rewrites flows module * Post kerberos rewording * x509 changes * Pre proof read * post visual review * post ccutil changes * Removing apostrophies * Post feedback changes * Post feedback rewording changes * Another minor change
420 lines
21 KiB
Text
420 lines
21 KiB
Text
[[_x509]]
|
|
|
|
=== X.509 Client Certificate User Authentication
|
|
|
|
{project_name} supports logging in with an X.509 client certificate if you have configured the server to use mutual SSL authentication.
|
|
|
|
A typical workflow:
|
|
|
|
* A client sends an authentication request over SSL/TLS channel.
|
|
* During the SSL/TLS handshake, the server and the client exchange their x.509/v3 certificates.
|
|
* The container ({appserver_name}) validates the certificate PKIX path and the certificate expiration date.
|
|
* The x.509 client certificate authenticator validates the client certificate by using the following methods:
|
|
+
|
|
** Checks the certificate revocation status by using CRL or CRL Distribution Points.
|
|
** Checks the Certificate revocation status by using OCSP (Online Certificate Status Protocol).
|
|
** Validates whether the key in the certificate matches the expected key.
|
|
** Validates whether the extended key in the certificate matches the expected extended key.
|
|
+
|
|
* If any of the these checks fail, the x.509 authentication fails. Otherwise, the authenticator extracts the certificate identity and maps it to an existing user.
|
|
|
|
When the certificate maps to an existing user, the behavior diverges depending on the authentication flow:
|
|
|
|
* In the Browser Flow, the server prompts users to confirm their identity or sign in with a username and password.
|
|
* In the Direct Grant Flow, the server signs in the user.
|
|
|
|
IMPORTANT: Note that it is the responsibility of the web container to validate certificate PKIX path. X.509 authenticator on the
|
|
{project_name} side provides just the additional support for check the certificate expiration, certificate revocation status and key usage. If you are
|
|
using {project_name} deployed behind reverse proxy, make sure that your reverse proxy is configured to validate PKIX path. If you
|
|
do not use reverse proxy and users directly access the {appserver_name}, you should be fine as {appserver_name} makes sure that PKIX path is validated as long
|
|
as it is configured as described below.
|
|
|
|
==== Features
|
|
|
|
Supported Certificate Identity Sources:
|
|
|
|
* Match SubjectDN by using regular expressions
|
|
* X500 Subject's email attribute
|
|
* X500 Subject's email from Subject Alternative Name Extension (RFC822Name General Name)
|
|
* X500 Subject's other name from Subject Alternative Name Extension. This other name is the User Principal Name (UPN), typically.
|
|
* X500 Subject's Common Name attribute
|
|
* Match IssuerDN by using regular expressions
|
|
* Certificate Serial Number
|
|
* Certificate Serial Number and IssuerDN
|
|
* SHA-256 Certificate thumbprint
|
|
* Full certificate in PEM format
|
|
|
|
===== Regular Expressions
|
|
{project_name} extracts the certificate identity from Subject DN or Issuer DN by using a regular expression as a filter. For example, this regular expression matches the email attribute:
|
|
|
|
```
|
|
emailAddress=(.*?)(?:,|$)
|
|
```
|
|
|
|
The regular expression filtering applies if the `Identity Source` is set to either `Match SubjectDN using regular expression` or `Match IssuerDN using regular expression`.
|
|
|
|
====== Mapping certificate identity to an existing user
|
|
|
|
The certificate identity mapping can map the extracted user identity to an existing user's username, email, or a custom attribute whose value matches the certificate identity. For example, setting `Identity source` to _Subject's email_ or `User mapping method` to _Username or email_ makes the X.509 client certificate authenticator use the email attribute in the certificate's Subject DN as the search criteria when searching for an existing user by username or by email.
|
|
|
|
[IMPORTANT]
|
|
====
|
|
* If you disable *Login with email* at realm settings, the same rules apply to certificate authentication. Users are unable to log in by using the email attribute.
|
|
* Using `Certificate Serial Number and IssuerDN` as an identity source requires two custom attributes for the serial number and the IssuerDN.
|
|
* `SHA-256 Certificate thumbprint` is the lowercase hexadecimal representation of SHA-256 certificate thumbprint.
|
|
* Using `Full certificate in PEM format` as an identity source is limited to the custom attributes mapped to external federation sources, such as LDAP. {project_name} cannot store certificates in its database due to length limitations, so in the case of LDAP, you must enable `Always Read Value From LDAP`.
|
|
====
|
|
|
|
====== Extended Certificate Validation
|
|
* Revocation status checking using CRL.
|
|
* Revocation status checking using CRL/Distribution Point.
|
|
* Revocation status checking using OCSP/Responder URI.
|
|
* Certificate KeyUsage validation.
|
|
* Certificate ExtendedKeyUsage validation.
|
|
|
|
==== Enable X.509 Client Certificate User Authentication
|
|
|
|
The following sections describe how to configure {appserver_name}/Undertow and the {project_name} Server to enable X.509 client certificate authentication.
|
|
|
|
[[_enable-mtls-wildfly]]
|
|
===== Enable mutual SSL in {appserver_name}
|
|
|
|
See link:https://docs.wildfly.org/19/Admin_Guide.html#enable-ssl[Enable SSL] for the instructions to enable SSL in {appserver_name}.
|
|
|
|
* Open {project_dirref}/standalone/configuration/standalone.xml and add a new realm:
|
|
```xml
|
|
<security-realms>
|
|
<security-realm name="ssl-realm">
|
|
<server-identities>
|
|
<ssl>
|
|
<keystore path="servercert.jks"
|
|
relative-to="jboss.server.config.dir"
|
|
keystore-password="servercert password"/>
|
|
</ssl>
|
|
</server-identities>
|
|
<authentication>
|
|
<truststore path="truststore.jks"
|
|
relative-to="jboss.server.config.dir"
|
|
keystore-password="truststore password"/>
|
|
</authentication>
|
|
</security-realm>
|
|
</security-realms>
|
|
```
|
|
|
|
`ssl/keystore`::
|
|
The `ssl` element contains the `keystore` element that contains the details to load the server public key pair from a JKS keystore.
|
|
|
|
`ssl/keystore/path`::
|
|
The path to the JKS keystore.
|
|
|
|
`ssl/keystore/relative-to`::
|
|
The path that the keystore path is relative to.
|
|
|
|
`ssl/keystore/keystore-password`::
|
|
The password to open the keystore.
|
|
|
|
`ssl/keystore/alias` (optional)::
|
|
The alias of the entry in the keystore. Set if the keystore contains multiple entries.
|
|
|
|
`ssl/keystore/key-password` (optional)::
|
|
The private key password, if different from the keystore password.
|
|
|
|
`authentication/truststore`::
|
|
Defines how to load a trust store to verify the certificate presented by the remote side of the inbound/outgoing connection. Typically, the truststore contains a collection of trusted CA certificates.
|
|
|
|
`authentication/truststore/path`::
|
|
The path to the JKS keystore containing the certificates of the trusted certificate authorities.
|
|
|
|
`authentication/truststore/relative-to`::
|
|
The path that the truststore path is relative to.
|
|
|
|
`authentication/truststore/keystore-password`::
|
|
The password to open the truststore.
|
|
|
|
|
|
===== Enable HTTPS listener
|
|
|
|
See link:https://docs.wildfly.org/19/Admin_Guide.html#https-listener[HTTPS Listener] for the instructions to enable HTTPS in WildFly.
|
|
|
|
* Add the <https-listener> element.
|
|
|
|
[source,xml,subs="attributes+"]
|
|
----
|
|
<subsystem xmlns="{subsystem_undertow_xml_urn}">
|
|
....
|
|
<server name="default-server">
|
|
<https-listener name="default"
|
|
socket-binding="https"
|
|
security-realm="ssl-realm"
|
|
verify-client="REQUESTED"/>
|
|
</server>
|
|
</subsystem>
|
|
----
|
|
|
|
`https-listener/security-realm`::
|
|
This value must match the name of the realm from the previous section.
|
|
|
|
`https-listener/verify-client`::
|
|
If set to *REQUESTED*, the server optionally asks for a client certificate.
|
|
If set to *REQUIRED*, the server refuses inbound connections if no client certificate has been provided.
|
|
|
|
[[_browser_flow]]
|
|
==== Adding X.509 Client Certificate Authentication to browser flows
|
|
|
|
. Click *Authentication* in the menu.
|
|
. Click the "Browser" flow.
|
|
. Click *Copy* to make a copy of the built-in "Browser" flow.
|
|
. Enter a name for the copy.
|
|
. Click *Ok*.
|
|
. Click the copy in the *Add policy* drop-down box.
|
|
. Click *Add execution*.
|
|
. Click "X509/Validate Username Form".
|
|
. Click *Save*.
|
|
+
|
|
.X509 Execution
|
|
image:images/x509-execution.png[X509 Execution]
|
|
+
|
|
. Click the up/down arrow buttons to move the "X509/Validate Username Form" over the "Browser Forms" execution.
|
|
. Set the requirement to "ALTERNATIVE".
|
|
+
|
|
.X509 Browser Flow
|
|
image:images/x509-browser-flow.png[X509 Browser Flow]
|
|
+
|
|
. Click the *Bindings* tab.
|
|
. Click the *Browser Flow* drop-down list.
|
|
. Click the copy of the browser flow from the drop-down list.
|
|
. Click *Save*.
|
|
+
|
|
.X509 Browser Flow Bindings
|
|
image:images/x509-browser-flow-bindings.png[X509 Browser Flow Bindings]
|
|
|
|
==== Configuring X.509 Client Certificate Authentication
|
|
|
|
.X509 Configuration
|
|
image:images/x509-configuration.png[X509 Configuration]
|
|
|
|
*User Identity Source*::
|
|
Defines the method for extracting the user identity from a client certificate.
|
|
|
|
*Canonical DN representation enabled*::
|
|
Defines whether to use canonical format to determine a distinguished name. The official link:https://docs.oracle.com/javase/8/docs/api/javax/security/auth/x500/X500Principal.html#getName-java.lang.String-[Java API documentation] describes the format. This option affects the two User Identity Sources _Match SubjectDN using regular expression_ and _Match IssuerDN using regular expression_ only. Enable this option when you set up a new {project_name} instance. Disable this option to retain backward compatibility with existing {project_name} instances.
|
|
|
|
*Enable Serial Number hexadecimal representation*::
|
|
Represent the link:https://tools.ietf.org/html/rfc5280#section-4.1.2.2[serial number] as hexadecimal. The serial number with the sign bit set to 1 must be left padded with 00 octet. For example, a serial number with decimal value _161_, or _a1_ in hexadecimal representation is encoded as _00a1_, according to RFC5280. See link:https://tools.ietf.org/html/rfc5280#appendix-B[RFC5280, appendix-B] for more details.
|
|
|
|
*A regular expression*::
|
|
A regular expression to use as a filter for extracting the certificate identity. The expression must contain a single group.
|
|
|
|
*User Mapping Method*::
|
|
Defines the method to match the certificate identity with an existing user. _Username or email_ searches for existing users by username or email. _Custom Attribute Mapper_ searches for existing users with a custom attribute that matches the certificate identity. The name of the custom attribute is configurable.
|
|
|
|
*A name of user attribute*::
|
|
A custom attribute whose value matches against the certificate identity. Use multiple custom attributes when attribute mapping is related to multiple values, For example, 'Certificate Serial Number and IssuerDN'.
|
|
|
|
*CRL Checking Enabled*::
|
|
Check the revocation status of the certificate by using the Certificate Revocation List. The location of the list is defined in the *CRL file path* attribute.
|
|
|
|
*Enable CRL Distribution Point to check certificate revocation status*::
|
|
Use CDP to check the certificate revocation status. Most PKI authorities include CDP in their certificates.
|
|
|
|
*CRL file path*::
|
|
The path to a file containing a CRL list. The value must be a path to a valid file if the *CRL Checking Enabled* option is enabled.
|
|
|
|
*OCSP Checking Enabled*::
|
|
Checks the certificate revocation status by using Online Certificate Status Protocol.
|
|
|
|
*OCSP Responder URI*::
|
|
Override the value of the OCSP responder URI in the certificate.
|
|
|
|
*Validate Key Usage*::
|
|
Verifies the certificate's KeyUsage extension bits are set. For example, "digitalSignature,KeyEncipherment" verifies if bits 0 and 2 in the KeyUsage extension are set.
|
|
Leave this parameter empty to disable the Key Usage validation. See link:https://tools.ietf.org/html/rfc5280#section-4.2.1.3[RFC5280, Section-4.2.1.3] for more information. {project_name} raises an error when a key usage mismatch occurs.
|
|
|
|
*Validate Extended Key Usage*::
|
|
Verifies one or more purposes defined in the Extended Key Usage extension. See link:https://tools.ietf.org/html/rfc5280#section-4.2.1.12[RFC5280, Section-4.2.1.12] for more information. Leave this parameter empty to disable the Extended Key Usage validation. {project_name} raises an error when flagged as critical by the issuing CA and a key usage extension mismatch occurs.
|
|
|
|
*Bypass identity confirmation*::
|
|
If enabled, X.509 client certificate authentication does not prompt the user to confirm the certificate identity. {project_name} signs in the user upon successful authentication.
|
|
|
|
`Revalidate client certificate`::
|
|
If set, the client certificate trust chain will be always verified at the application level using the certificates present in the configured trust store. This can be useful if the underlying web server does not enforce client certificate chain validation, for example because it is behind a non-validating load balancer or reverse proxy, or when the number of allowed CAs is too large for the mutual SSL negotiation (most browsers cap the maximum SSL negotiation packet size at 32767 bytes, which corresponds to about 200 advertised CAs). By default this option is off.
|
|
|
|
==== Adding X.509 Client Certificate Authentication to a Direct Grant Flow
|
|
|
|
. Click *Authentication* in the menu.
|
|
. Click the "Direct Grant" flow.
|
|
. Click *Copy* to make a copy of the "Direct Grant" flow.
|
|
. Enter a name for the copy.
|
|
. Click *Ok*.
|
|
. Click on the *Actions* link for "Username Validation" and click *Delete*.
|
|
. Click *Delete*.
|
|
. Click on the *Actions* link for "Password" and click *Delete*.
|
|
. Click *Delete*.
|
|
. Click *Add execution*.
|
|
. Click "X509/Validate Username".
|
|
. Click *Save*.
|
|
+
|
|
.X509 Direct Grant Execution
|
|
image:images/x509-directgrant-execution.png[X509 Direct Grant Execution]
|
|
+
|
|
. Set up the x509 authentication configuration by following the steps described in the <<_browser_flow, x509 Browser Flow>> section.
|
|
. Click the *Bindings* tab.
|
|
. Click the *Direct Grant Flow* drop-down list.
|
|
. Click the newly created "x509 Direct Grant" flow.
|
|
. Click *Save*.
|
|
+
|
|
.X509 Direct Grant Flow Bindings
|
|
image:images/x509-directgrant-flow-bindings.png[X509 Direct Grant Flow Bindings]
|
|
|
|
==== Client certificate lookup
|
|
|
|
When the {project_name} server receives a direct HTTP request, the {appserver_name} undertow subsystem establishes an SSL handshake and extracts the client certificate. The {appserver_name} saves the client certificate to the `javax.servlet.request.X509Certificate` attribute of the HTTP request, as specified in the servlet specification. The {project_name} X509 authenticator can look up the certificate from this attribute.
|
|
|
|
However, when the {project_name} server listens to HTTP requests behind a load balancer or reverse proxy, the proxy server may extract the client certificate and establish a mutual SSL connection. A reverse proxy generally puts the authenticated client certificate in the HTTP header of the underlying request. The proxy forwards the request to the back end {project_name} server. In this case, {project_name} must look up the X.509 certificate chain from the HTTP headers rather than the attribute of the HTTP request.
|
|
|
|
If {project_name} is behind a reverse proxy, you generally need to configure the alternative provider of the `x509cert-lookup` SPI in {project_dirref}/standalone/configuration/standalone.xml. With the `default` provider looking up the HTTP header certificate, two additional built-in providers exist: `haproxy` and `apache`.
|
|
|
|
===== HAProxy certificate lookup provider
|
|
|
|
You use this provider when your {project_name} server is behind an HAProxy reverse proxy. Use the following configuration for your server:
|
|
|
|
[source,xml]
|
|
----
|
|
<spi name="x509cert-lookup">
|
|
<default-provider>haproxy</default-provider>
|
|
<provider name="haproxy" enabled="true">
|
|
<properties>
|
|
<property name="sslClientCert" value="SSL_CLIENT_CERT"/>
|
|
<property name="sslCertChainPrefix" value="CERT_CHAIN"/>
|
|
<property name="certificateChainLength" value="10"/>
|
|
</properties>
|
|
</provider>
|
|
</spi>
|
|
----
|
|
|
|
In this example configuration, the client certificate is looked up from the HTTP header, `SSL_CLIENT_CERT`, and the other certificates from its chain are looked up from HTTP headers such as `CERT_CHAIN_0` through `CERT_CHAIN_9`. The attribute `certificateChainLength` is the maximum length of the chain so the last attribute is `CERT_CHAIN_9`.
|
|
|
|
Consult the HAProxy documentation for the details of configuring the HTTP Headers for the client certificate and client certificate chain.
|
|
|
|
===== Apache certificate lookup provider
|
|
|
|
You can use this provider when your {project_name} server is behind an Apache reverse proxy. Use the following configuration for your server:
|
|
|
|
[source,xml]
|
|
----
|
|
<spi name="x509cert-lookup">
|
|
<default-provider>apache</default-provider>
|
|
<provider name="apache" enabled="true">
|
|
<properties>
|
|
<property name="sslClientCert" value="SSL_CLIENT_CERT"/>
|
|
<property name="sslCertChainPrefix" value="CERT_CHAIN"/>
|
|
<property name="certificateChainLength" value="10"/>
|
|
</properties>
|
|
</provider>
|
|
</spi>
|
|
----
|
|
|
|
This configuration is the same as the `haproxy` provider. Consult the Apache documentation on link:https://httpd.apache.org/docs/current/mod/mod_ssl.html[mod_ssl] and link:https://httpd.apache.org/docs/current/mod/mod_headers.html[mod_headers] for details on how the HTTP Headers for the client certificate and client certificate chain are configured.
|
|
|
|
===== NGINX certificate lookup provider
|
|
|
|
You can use this provider when your {project_name} server is behind an NGINX reverse proxy. Use the following configuration for your server:
|
|
|
|
[source,xml]
|
|
----
|
|
<spi name="x509cert-lookup">
|
|
<default-provider>nginx</default-provider>
|
|
<provider name="nginx" enabled="true">
|
|
<properties>
|
|
<property name="sslClientCert" value="ssl-client-cert"/>
|
|
<property name="sslCertChainPrefix" value="USELESS"/>
|
|
<property name="certificateChainLength" value="2"/>
|
|
</properties>
|
|
</provider>
|
|
</spi>
|
|
----
|
|
|
|
[NOTE]
|
|
====
|
|
The NGINX link:http://nginx.org/en/docs/http/ngx_http_ssl_module.html#variables[SSL/TLS module] does not expose the client certificate chain. {project_name}'s NGINX certificate lookup provider rebuilds it by using the link:{installguide_truststore_link}[{installguide_truststore_name}]. Populate the {project_name} truststore by using the keytool CLI with all root and intermediate CA's for rebuilding client certificate chain.
|
|
====
|
|
|
|
Consult the NGINX documentation for the details of configuring the HTTP Headers for the client certificate.
|
|
|
|
Example of NGINX configuration file :
|
|
[source,txt]
|
|
----
|
|
...
|
|
server {
|
|
...
|
|
ssl_client_certificate trusted-ca-list-for-client-auth.pem;
|
|
ssl_verify_client optional_no_ca;
|
|
ssl_verify_depth 2;
|
|
...
|
|
location / {
|
|
...
|
|
proxy_set_header ssl-client-cert $ssl_client_escaped_cert;
|
|
...
|
|
}
|
|
...
|
|
}
|
|
----
|
|
|
|
[NOTE]
|
|
====
|
|
All certificates in trusted-ca-list-for-client-auth.pem must be added to link:{installguide_truststore_link}[{installguide_truststore_name}].
|
|
====
|
|
|
|
===== Other reverse proxy implementations
|
|
|
|
{project_name} does not have built-in support for other reverse proxy implementations. However, you can make other reverse proxies behave in a similar way to `apache` or `haproxy`. If none of these work, create your implementation of the `org.keycloak.services.x509.X509ClientCertificateLookupFactory` and `org.keycloak.services.x509.X509ClientCertificateLookup` providers. See the link:{developerguide_link}[{developerguide_name}] for details on how to add your provider.
|
|
|
|
==== Troubleshooting
|
|
|
|
Dumping HTTP headers::
|
|
To view what the reverse proxy sends to Keycloak, enable the `RequestDumpingHandler` Undertow filter and consult the `server.log` file.
|
|
|
|
Enable TRACE logging under the logging subsystem::
|
|
[source,xml]
|
|
----
|
|
...
|
|
<profile>
|
|
<subsystem xmlns="urn:jboss:domain:logging:8.0">
|
|
...
|
|
<logger category="org.keycloak.authentication.authenticators.x509">
|
|
<level name="TRACE"/>
|
|
</logger>
|
|
<logger category="org.keycloak.services.x509">
|
|
<level name="TRACE"/>
|
|
</logger>
|
|
----
|
|
[WARNING]
|
|
====
|
|
Do not use RequestDumpingHandler or TRACE logging in production.
|
|
====
|
|
|
|
Direct Grant authentication with X.509::
|
|
You can use the following template to request a token by using the Resource Owner Password Credentials Grant:
|
|
|
|
```
|
|
$ curl https://[host][:port]/auth/realms/master/protocol/openid-connect/token \
|
|
--insecure \
|
|
--data "grant_type=password&scope=openid profile&username=&password=&client_id=CLIENT_ID&client_secret=CLIENT_SECRET" \
|
|
-E /path/to/client_cert.crt \
|
|
--key /path/to/client_cert.key
|
|
```
|
|
|
|
`[host][:port]`::
|
|
The host and the port number of the remote {project_name} server.
|
|
|
|
`CLIENT_ID`::
|
|
The client id.
|
|
|
|
`CLIENT_SECRET`::
|
|
For confidential clients, a client secret.
|
|
|
|
`client_cert.crt`::
|
|
A public key certificate to verify the identity of the client in mutual SSL authentication. The certificate must be in PEM format.
|
|
|
|
`client_cert.key`::
|
|
A private key in the public key pair. This key must be in PEM format.
|