382 lines
21 KiB
Text
382 lines
21 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.
|
|
|
|
==== 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.
|
|
|