385 lines
22 KiB
Text
385 lines
22 KiB
Text
[[_x509]]
|
|
|
|
=== X.509 Client Certificate User Authentication
|
|
|
|
{project_name} supports login with a X.509 client certificate if the server is configured for mutual SSL authentication.
|
|
|
|
A typical workflow is as follows:
|
|
|
|
- A client sends an authentication request over SSL/TLS channel
|
|
- During SSL/TLS handshake, the server and the client exchange their x.509/v3 certificates
|
|
- The web container (either the reverse proxy or {appserver_name} where {project_name} is deployed) validates the certificate PKIX path and the certificate expiration
|
|
- The x.509 client certificate authenticator validates the client certificate as follows:
|
|
* Optionally checks the certificate revocation status using CRL and/or CRL Distribution Points
|
|
* Optionally checks the Certificate revocation status using OCSP (Online Certificate Status Protocol)
|
|
* Optionally validates whether the key usage in the certificate matches the expected key usage
|
|
* Optionally validates whether the extended key usage in the certificate matches the expected extended key usage
|
|
- If any of the above checks fails, the x.509 authentication fails
|
|
- Otherwise, the authenticator extracts the certificate identity and maps it to an existing user
|
|
- Once the certificate is mapped to an existing user, the behavior diverges depending on the authentication flow:
|
|
* In the Browser Flow, the server prompts the user to confirm identity or to ignore it and instead sign in with username/password
|
|
* In the case of 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 using regular expression
|
|
- X500 Subject's e-mail attribute
|
|
- X500 Subject's e-mail from Subject Alternative Name Extension (RFC822Name General Name)
|
|
- X500 Subject's other name from Subject Alternative Name Extension. This is typically UPN (User Principal Name)
|
|
- X500 Subject's Common Name attribute
|
|
- Match IssuerDN using regular expression
|
|
- Certificate Serial Number
|
|
- Certificate Serial Number and IssuerDN
|
|
- SHA-256 Certificate thumbprint
|
|
- Full certificate in PEM format
|
|
|
|
Regular Expressions::
|
|
The certificate identity can be extracted from either Subject DN or Issuer DN using a regular expression as a filter. For example, the regular expression below will match the e-mail attribute:
|
|
```
|
|
emailAddress=(.*?)(?:,|$)
|
|
```
|
|
The regular expression filtering is applicable only 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 be configured to map the extracted user identity to an existing user's username or e-mail or to a custom attribute which value matches the certificate identity. For example, setting the `Identity source` to _Subject's e-mail_ and `User mapping method` to _Username or email_ will have the X.509 client certificate authenticator use the e-mail attribute in the certificate's Subject DN as a search criteria to look up an existing user by username or by e-mail.
|
|
|
|
IMPORTANT: Please notice that if we disable `Login with email` at realm settings, the same rules will be applied to certificate authentication. In other words, users won't be able to log in using e-mail attribute.
|
|
|
|
IMPORTANT: Usage of `Certificate Serial Number and IssuerDN` as an identity source requires two custom attributes - one for serial number and the other for IssuerDN.
|
|
|
|
IMPORTANT: `SHA-256 Certificate thumbprint` is lowercase hexadecimal representation of SHA-256 certificate thumbprint.
|
|
|
|
IMPORTANT: Usage of `Full certificate in PEM format` as an identity source is limited to custom attributes mapped to external federation sources like LDAP. You must enable `Always Read Value From LDAP` in this case, because certificates cannot be stored in Keycloak database due to a length limitation.
|
|
|
|
Other Features: 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 how 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 defines how to load the server public key pair from a JKS keystore
|
|
|
|
`ssl/keystore/path`::
|
|
A path to a JKS keystore
|
|
|
|
`ssl/keystore/relative-to`::
|
|
Defines a path 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 it 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`::
|
|
A path to a JKS keystore that contains the certificates of the trusted CAs (certificate authorities)
|
|
|
|
`authentication/truststore/relative-to`::
|
|
Defines a path 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 how to enable HTTPS in WildFly.
|
|
|
|
* Add the <https-listener> element as shown below:
|
|
|
|
[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`::
|
|
The value must match the name of the realm from the previous section
|
|
|
|
`https-listener/verify-client`::
|
|
If set to `REQUESTED`, the server will optionally ask for a client certificate. Setting the attribute to `REQUIRED` will have the server to refuse inbound connections if no client certificate has been provided.
|
|
|
|
==== Adding X.509 Client Certificate Authentication to a Browser Flow
|
|
|
|
* Select a realm, click on Authentication link, select the "Browser" flow
|
|
* Make a copy of the built-in "Browser" flow. You may want to give the new flow a distinctive name, i.e. "X.509 Browser"
|
|
* Using the drop down, select the copied flow, and click on "Add execution"
|
|
* Select "X509/Validate Username Form" using the drop down and click on "Save"
|
|
|
|
image:images/x509-execution.png[]
|
|
|
|
* Using the up/down arrows, change the order of the "X509/Validate Username Form" by moving it above the "Browser Forms" execution, and set the requirement to "ALTERNATIVE"
|
|
|
|
image:images/x509-browser-flow.png[]
|
|
|
|
* Select the "Bindings" tab, find the drop down for "Browser Flow". Select the newly created X509 browser flow from the drop down and click on "Save".
|
|
|
|
image:images/x509-browser-flow-bindings.png[]
|
|
|
|
Configuring X.509 Client Certificate Authentication::
|
|
|
|
image:images/x509-configuration.png[]
|
|
|
|
`User Identity Source`::
|
|
Defines how to extract the user identity from a client certificate.
|
|
|
|
`Canonical DN representation enabled` (optional)::
|
|
Defines whether to use the canonical format to determine a distinguished name.
|
|
The format is described in detail in the official link:https://docs.oracle.com/javase/8/docs/api/javax/security/auth/x500/X500Principal.html#getName-java.lang.String-[Java API documentation] .
|
|
This option only affects the two User Identity Sources _Match SubjectDN using regular expression_ and _Match IssuerDN using regular expression_.
|
|
If you setup a new {project_name} instance it is recommended to enable this option. Leave this option disabled to remain beckward compatible with existing {project_name} instances.
|
|
|
|
`Enable Serial Number hexadecimal representation` (optional)::
|
|
An option to use hexadecimal representation of the Serial Number. See link:https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.2[RFC5280, Section-4.1.2.2]. Serial Number with sign bit set to 1 should be left padded with 00 octet. E.g. Serial number with decimal value _161_, or _a1_ in hexadecimal representation according to RFC5280 must be encoded as _00a1_. More details can be found: link:https://datatracker.ietf.org/doc/html/rfc5280#appendix-B[RFC5280, appendix-B].
|
|
|
|
`A regular expression` (optional)::
|
|
Defines a regular expression to use as a filter to extract the certificate identity. The regular expression must contain a single group.
|
|
|
|
`User Mapping Method`::
|
|
Defines how to match the certificate identity to an existing user. _Username or e-mail_ will search for an existing user by username or e-mail. _Custom Attribute Mapper_ will search for an existing user with a custom attribute which value matches the certificate identity. The name of the custom attribute is configurable.
|
|
|
|
`A name of user attribute` (optional)::
|
|
A custom attribute which value will be matched against the certificate identity. Multiple custom attributes are relevant when attribute mapping is related to multiple values, e.g. 'Certificate Serial Number and IssuerDN'.
|
|
|
|
WARNING: It is highly recommended that attribute used here is read-only for the users. By default, only the `usercertificate` attribute is read-only by {project_name}.
|
|
If you use a different attribute name, you may need to add it to the list of read-only attributes. See the details in the link:#_read_only_user_attributes[Threat model mitigation chapter].
|
|
|
|
`CRL Checking Enabled` (optional)::
|
|
Defines whether to check the revocation status of the certificate using Certificate Revocation List.
|
|
|
|
`Enable CRL Distribution Point to check certificate revocation status` (optional)::
|
|
Defines whether to use CDP to check the certificate revocation status. Most PKI authorities include CDP in their certificates.
|
|
|
|
`CRL file path` (optional)::
|
|
Defines a path to a file that contains a CRL list. The value must be a path to a valid file if `CRL Checking Enabled` option is turned on.
|
|
|
|
`OCSP Checking Enabled`(optional)::
|
|
Defines whether to check the certificate revocation status using Online Certificate Status Protocol.
|
|
|
|
`OCSP Responder URI` (optional)::
|
|
Allows to override a value of the OCSP responder URI in the certificate.
|
|
|
|
`Validate Key Usage` (optional)::
|
|
Verifies whether the certificate's KeyUsage extension bits are set. For example, "digitalSignature,KeyEncipherment" will verify if bits 0 and 2 in the KeyUsage extension are asserted. Leave the parameter empty to disable the Key Usage validation. See link:https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.3[RFC5280, Section-4.2.1.3]. The server will raise an error only when flagged as critical by the issuing CA and there is a key usage extension mismatch.
|
|
|
|
`Validate Extended Key Usage` (optional)::
|
|
Verifies one or more purposes as defined in the Extended Key Usage extension. See link:https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.12[RFC5280, Section-4.2.1.12]. Leave the parameter empty to disable the Extended Key Usage validation. The server will raise an error only when flagged as critical by the issuing CA and there is a key usage extension mismatch.
|
|
|
|
`Bypass identity confirmation`::
|
|
If set, X.509 client certificate authentication will not prompt the user to confirm the certificate identity and will automatically sign 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
|
|
|
|
* Using {project_name} admin console, click on "Authentication" and select the "Direct Grant" flow,
|
|
* Make a copy of the build-in "Direct Grant" flow. You may want to give the new flow a distinctive name, i.e. "X509 Direct Grant",
|
|
* Delete "Username Validation" and "Password" authenticators,
|
|
* Click on "Add execution" and add "X509/Validate Username" and click on "Save" to add the execution step to the parent flow.
|
|
|
|
image:images/x509-directgrant-execution.png[]
|
|
|
|
* Change the `Requirement` to _REQUIRED_.
|
|
|
|
image:images/x509-directgrant-flow.png[]
|
|
|
|
* Set up the x509 authentication configuration by following the steps described earlier in the x.509 Browser Flow section.
|
|
* Select the "Bindings" tab, find the drop down for "Direct Grant Flow". Select the newly created X509 direct grant flow from the drop down and click on "Save".
|
|
|
|
image:images/x509-directgrant-flow-bindings.png[]
|
|
|
|
==== Client certificate lookup
|
|
|
|
When an HTTP request is sent directly to {project_name} server, the {appserver_name} undertow subsystem will establish an SSL handshake and extract the client certificate. The client certificate will be then saved to the attribute `javax.servlet.request.X509Certificate` of the HTTP request, as specified in the servlet specification. The {project_name} X509 authenticator will be then able to lookup the certificate from this attribute.
|
|
|
|
However, when the {project_name} server listens to HTTP requests behind a load balancer or reverse proxy, it may be the proxy server which extracts the client certificate and establishes the mutual SSL connection. A reverse proxy usually puts the authenticated client certificate in the HTTP header of the underlying request and forwards it to the back end {project_name} server. In this case, {project_name} must be able to look up the X.509 certificate chain from the HTTP headers instead of from the attribute of HTTP request, as is done for Undertow.
|
|
|
|
If {project_name} is behind a reverse proxy, you usually need to configure alternative provider of the `x509cert-lookup` SPI in {project_dirref}/standalone/configuration/standalone.xml. Along with the `default` provider, which looks up the certificate from the HTTP header, we also have two additional built-in providers: `haproxy` and `apache`, which are described next.
|
|
|
|
===== HAProxy certificate lookup provider
|
|
|
|
You can use this provider when your {project_name} server is behind an HAProxy reverse proxy. Configure the server like this:
|
|
|
|
[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 will be looked up from the HTTP header, `SSL_CLIENT_CERT`, and the other certificates from its chain will be looked up from HTTP headers like `CERT_CHAIN_0` , `CERT_CHAIN_1`, ..., `CERT_CHAIN_9` . The attribute `certificateChainLength` is the maximum length of the chain, so the last one tried attribute would be `CERT_CHAIN_9` .
|
|
|
|
Consult the HAProxy documentation for the details of how the HTTP Headers for the client certificate and client certificate chain can be configured and their proper names.
|
|
|
|
===== Apache certificate lookup provider
|
|
|
|
You can use this provider when your {project_name} server is behind an Apache reverse proxy. Configure the server like this:
|
|
|
|
[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>
|
|
----
|
|
|
|
The configuration is same as for 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 the details of how the HTTP Headers for the client certificate and client certificate chain can be configured and their proper names.
|
|
|
|
===== Nginx certificate lookup provider
|
|
|
|
You can use this provider when your {project_name} server is behind an Nginx reverse proxy. Configure the server like this:
|
|
|
|
[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: NGINX link:http://nginx.org/en/docs/http/ngx_http_ssl_module.html#variables[SSL/TLS module] does not expose the client certificate chain, so Keycloak NGINX certificate lookup provider is rebuilding it using the link:{installguide_truststore_link}[{installguide_truststore_name}]. Please populate Keycloak truststore using keytool CLI with all root and intermediate CA's needed for rebuilding client certificate chain.
|
|
|
|
Consult the NGINX documentation for the details of how the HTTP Headers for the client certificate can be configured.
|
|
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
|
|
|
|
We do not have built-in support for other reverse proxy implementations. However, it is possible that other reverse proxies can be made to behave in a similar way to `apache` or `haproxy` and that some of those providers can be used. If none of those works, you may need to create your own implementation of the `org.keycloak.services.x509.X509ClientCertificateLookupFactory` and `org.keycloak.services.x509.X509ClientCertificateLookup` provider. See the link:{developerguide_link}[{developerguide_name}] for the details on how to add your own provider.
|
|
|
|
==== Troubleshooting
|
|
|
|
Dumping HTTP headers::
|
|
If you want to view what the reverse proxy is sending to Keycloak, enable the `RequestDumpingHandler` Undertow filter and consult `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: Don't use RequestDumpingHandler or TRACE logging in production.
|
|
|
|
Direct Grant authentication with X.509::
|
|
The following template can be used to request a token 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 a remote {project_name} server that has been configured to allow users authenticate with x.509 client certificates using the Direct Grant Flow.
|
|
|
|
`CLIENT_ID`::
|
|
A client id.
|
|
|
|
`CLIENT_SECRET`::
|
|
For confidential clients, a client secret; otherwise, leave it empty.
|
|
|
|
`client_cert.crt`::
|
|
A public key certificate that will be used to verify the identity of the client in mutual SSL authentication. The certificate should be in PEM format.
|
|
|
|
`client_cert.key`::
|
|
A private key in the public key pair. Also expected in PEM format.
|
|
|