Mutual TLS#
When a TLS connection is set up, it is possible to authenticate both ends of the communication channel: the server and the client. When both parties authenticate it is called a mutual TLS authenticated connection. Being the transport layer that the Curity Identity Server builds its application layer access security features on, a mutual TLS authenticated connection has properties that can be re-used for application level security.
The process of issuing tokens can benefit from mutual TLS authentication in multiple ways. First of all, a client that
is configured in the Curity Identity Server is required to authenticate to different endpoints of the server, for example to the
token-endpoint. If a client is able to present valid credentials (i.e. a valid X509-certificate) as part of
setting up a mutual TLS authenticated connection to the server, the Curity Identity Server can then take over and verify whether the same
credentials can also authenticate the configured client of the token profile.
On top of authenticating the client at the token endpoint, the Curity Identity Server can also issue a token that is actually bound to the certificate that the client used to authenticate the token request. Once the client uses the same client certificate to authenticate to a resource server when it presents the token there, that particular resource server can verify that the same client certificate was used when the token was requested as when the request to the resource server was made. In other words: the client can prove to the resource server that the token being used was issued to it, as the token is bound to the same client certificate that is used for the TLS connection between the client and the resource server.
This last property is very powerful, because it prevents a leaked token from being used by a party that is not in possession of that private key, as such a party would not be able to setup a mutually authenticated TLS connection. This type of authentication is based on what is called Proof-Of-Possession: the client can prove that it has access to the private key, but the proof doesn’t require the client to hand over the private key itself.
The Curity Identity Server implements the RFC-8705 (OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens) specification.
TLS termination#
A TLS connection is set up from a source, terminated at its destination and protects the connection between these two entities. While this sounds pretty obvious, the most common deployment of a security server is not one where the client makes a connection directly to the server. In other words, the TLS connection that the client initiates is not always terminated at the Curity Identity Server, but instead it is terminated by one or more intermediate systems (e.g. loadbalancers, reverse proxies, firewalls, etc.) and verified before it is being forwarded to Curity.

In order to support mutual TLS authentication in these situations, Curity can be configured to receive client certificate information through HTTP request headers instead of directly from the TLS connection. This requires a cooperation between the reverse proxy and Curity that needs to be carefully designed. Further details can be found in the Generic Reverse Proxy Server Setup section.
The alternative to mutual TLS by proxy is direct TLS, where the TLS connection is terminated by Curity. As this exposes Curity directly to the client, it must be considered very carefully to use this in any other environment than for development or testing.

Binding certificates to tokens#
Once a token request is based on a mutual TLS authenticated connection, the token will automatically be bound
to the client certificate. This is achieved by including the x5t#s256 thumbprint of the client certificate as a
confirmation claim to the Access Token and, if this is also issued, the Refresh Token.
Claims of an Access Token from introspection
{
"sub": "mutual_tls_client",
"purpose": "access_token",
"iss": "https://localhost:8443/dev/oauth/anonymous",
"active": true,
"token_type": "bearer",
"client_id": "mutual_tls_client",
"aud": "mutual_tls_client",
"nbf": 1542896887,
"scope": "",
"cnf": {
"x5t#S256": "FjeHcvJwiHXlr8dgnP7UvLQ7dLLMTe_3SgMYMuEpekc"
},
"exp": 1542897187,
"delegationId": "d0d2bc6b-55f9-4412-83bc-a42b992e15c1",
"iat": 1542896887
}
The purpose of this binding is for the receiver of the token to be able to verify that it is the same client that presents the token, as the client that the token was issued to. To make this work, the same client certificate must be used to request a token at the token endpoint, as to request a protected resource from a resource server.
Please note that this also applies to using the refresh token: when a client uses the Refresh Token to request a new
Access Token, the request to the token-endpoint must be authenticated using the same client certificate that was
used when that Refresh Token was issued. Also note that because the tokens are bound to the thumbprint of the
certificate, using a newly issued certificate with the same Subject DN, will not be accepted, as this new certificate
will have a different x5t#s256 thumbprint.
Trusted certificates#
A mutual TLS authenticated connection is based on the property that each end of the connection is capable of proving to be in possession of the private key that belongs to the presented certificate. For client authentication, this means that a client certificate identifies the client. A certificate exposes a number of attributes, among which are the issuing party (i.e. the Certificate Authority), the subject (i.e. the Subject Distinguished Name), and, perhaps, alternative names for the subject (see SAN below).
A client in Curity can be configured for mutual TLS authentication, and by doing so there are two options available to indicate how a client certificate can be trusted as a credential to authenticate the client:
- Trust by PKI
- Trust by pinned certificate
Trust by PKI#
Configuring trust by PKI means configuring a Trusted CA (i.e. the issuer) and a Client DN (i.e. the subject) that must
match the presented client certificate chain. The Trusted CA is a reference to a certificate as it is stored in the
client-ssl-truststores section of the crypto facility. This issuer certificate must chain up to the actual CA that is
stored as issuer in the client certificates. In other words, the client certificate and any intermediary certificate authorities that signed that certificate will be walked to find the root certificate of the chain. This root will be used to validate the certificate chain from the client
certificate up to a trusted issuer.
The Client DN is the Distinguished Name (DN) of the presented client certificate. The admin of Curity lets you upload
the certificate to extract the value of the Subject DN from it, but a DN that conforms to RFC-2253 is
accepted. If the DN should not be used, but a SAN instead, configure one of the alternative names as described below.
A name and a CA must be configured, except in the case where the client authenticates a user or client when
requesting an Initial Token (i.e. an Access Token with the dcr scope). This is a special case, where the
actual Client DN might be unknown, but the Trusted CA is known. In this case, the configuration allows
you to leave the value for Client DN empty.
Trust by a pinned certificate#
In case there is no PKI to assert trust, you can assign individual certificates to a client and by doing so, limit the
client to one specific certificate that it can use to authenticate through mutual TLS. This client certificate must
be added to the client-ssl-truststores section of the crypto facility, and can be referenced from the client
configuration.
DN comparison#
When a client provides a certificate, and the DN of the certificate is matched with the configured value, by default the matching is done to either match the DN’s RDNs (both DNs are made up of the same RDNs and the order of the RDNs in each DN is the same), or match the DN’s RDNs in reverse order (both DNs are made up of the same RDNs and the order of the RDNs in each DN is exactly the reverse).
This second match condition is based on OpenSSL’s way of presenting a DN in its text output (i.e. when running OpenSSL
like openssl x509 -text -noout <certificate>). This means that the following DNs will result in a positive match:
| provided DN | configured DN |
|---|---|
| CN=client,OU=example.com | CN=client,OU=example.com |
| CN=client,OU=example.com | OU=example.com,CN=client |
When the latter case should not result in a positive match, the server can be started in a strict DN comparison mode.
To do so, the Java runtime property se.curity:identity-server:strict-dn-comparison must be set to true when
starting the server.
openssl can be instructed to reverse the DN in its output by using the -nameopt dn_rev option, to
conform to RFC’s (see the nameopt documentation of OpenSSL).
While this works with standard OpenSSL, it does not work with OpenSSL that comes bundled with Mac OSX. To use the
nameopt flag with OpenSSL on OSX, you must use a non-Apple version of OpenSSL, e.g. install it through homebrew or
build it yourself.
Subject Alternative Name#
As defined in section 4.2.1.6 of RFC-5280, an X.509 certificate can include alternative names for the subject of that certificate.
In the case of OAuth client authentication, this extension, called a Subject Alternative Name (SAN), can be used to indicate to the OAuth server that a name other than the DN should be used to identify the client. This is a helpful extension when the DN contains temporal data or arbitrary data that increases the cost of maintaining the client configuration. Common examples of SANs are email addresses, IP addresses, and DNS names.
Another, more novel example, is a SPIFFE ID.
Dynamic clients can also configure that a SAN be used instead of the DN by registering the metadata set forth in section 2.1.2 of RFC-8705.
Configuring Mutual TLS#
Mutual TLS configuration consists of
- (in case of proxy terminated mutual TLS) how to communicate between a reverse proxy and Curity on each profile, or
- (in case of direct terminated mutual TLS) how TLS termination should behave on each Server, and
- trusted certificate credentials of a client
Proxy terminated Mutual TLS#
Configuring mutual TLS terminated by a reverse proxy is done per profile. This setup relies on the reverse proxy having verified the presented client certificate as being issued by a trusted CA, as well as that the status of the client certificate being valid (e.g. OCSP and CRL verification is done by the reverse proxy).
When Curity gets a certificate from a proxy, it will NOT check its status. Instead, Curity relies on the proxy for making these checks (for example through OCSP or a CRL) as part of terminating the TLS connection. Make sure that the proxy takes this responsibility!
The certificate that is accepted, is sent over in a request header to Curity over a connection that can be protected by
basic Proxy-Authorization authentication. If neither a User ID nor a Password is configured, the proxy
will not be authenticated by Curity.
These settings are configured as part of the Token Service’s Client Authentication configuration.

See the Generic Reverse Proxy Server Setup section for more concrete guidance on setting up proxies from specific vendors.
Direct terminated Mutual TLS#
It takes three settings to enable mutual TLS terminated by Curity.
Open the Server configuration:
Admin UI → Deployments
Configure the Service Role to use the HTTPS protocol.
Ensure the server is configured with the
httpsprotocol. In theTLStab of each Service Role, select the Mutual TLS Port:
Configuration of TLS termination on a Server By default, the Server will be able to accept client authentication on the regular https-port. It is however possible to add a separate port for mutual TLS connections, and leave the main listening port unaffected by any of the mutual TLS functionality.
To configure which client certificates are acceptable, all trusted CA certificates must be whitelisted. These are configured in the
Allowed Client Trust Storesdropdown.Admin UI → Facilities → Keys And Cryptography → Trust Anchors → Client Trust Stores
Client Trust Stores are configured in Facilities.
In case no entries are explicitly allowed, then all the configured Client Trust Stores are valid. Note that all presented certificates must be directly signed by one of the trusted client trust stores, a certificate chain that is sent by the client, that contains certificates between the client certificate and a trusted CA certificate is ignored when validating trust.
This configures the server’s listener capabilities, which is the first part of configuring Direct terminated mutual TLS.
Enable Client Authentication on each endpoint that requires mutual TLS authentication. To do so, open the Profile’s
Endpointssettings.Admin UI → Profiles → Oauth → Oauth Dev → Endopints
Client Trust Stores are configured in Facilities.

Configuration client authentication of endpoints The
Client Authenticationsetting controls Mutual TLS client authentication, with three options:Disallow- no mutual TLS client authentication may be doneAllow- mutual TLS client authentication is optionalRequired- the endpoint may not be called without mutual TLS client authentication
Make sure to always verify the configuration of Client Authentication on the endpoints of each profile when you enable direct terminated TLS in the Curity Identity Server.
You must enable
Mutual TLSas a valid authentication method for Clients of a Token Service. To do so, open the Token Service’sGeneralsettings, and toggle theMutual TLSslider to enable it.Admin UI → Profiles → Oauth → Oauth Dev → Client Settings
Client Trust Stores are configured in Facilities.

Enabling direct terminated Mutual TLS on a Token Service
Note that it is not possible to enable both Mutual TLS and Mutual TLS by Proxy. Make sure
Mutual TLS by Proxy is toggled off if you need direct terminated Mutual TLS.
Configuring trust#
Once direct Mutual TLS or Mutual TLS by proxy is enabled as a Client Authentication Method in the Token Profile, it becomes configurable for all clients.
Admin UI → Profiles → Oauth → Oauth Dev → Clients → Client
Configure Mutual TLS for a Client in the General Tab, under Client Authentication.

See the Trusted certificates section for more on these settings.
Reverse Proxy Server Setup#
Generic Reverse Proxy Server Setup#
It is always recommended to operate the Server in a secure environment. To create such an enclave that exposes services to the Internet, a reverse proxy is typically set up in front of the Curity Identity Server as a facade. By using a reverse proxy in a Demilitarized Zone (DMZ), private keys and credentials used by the Curity Identity Server can be kept in a private network.
In addition to TLS offloading, mutual TLS client authentication can also be offloaded to the reverse proxy server. In this case, the reverse proxy will perform TLS handshake and validate client certificate.
After validation, the client certificate is passed to the Curity Identity Server in a HTTP header (header names in the Server’s configuration and reverse proxy configuration should match).
To accommodate different types of proxy servers, the Curity Identity Server supports several formats for client certificates:
- Base64-encoded DER format (default for HAProxy);
- PEM format with newlines encoded as spaces (default for Apache HTTPD);
- URL-encoded PEM format (with space encoded as
%20, default for NGINX, or+).
Please keep in mind that, if mutual TLS client authentication is performed on a reverse proxy, the Curity Identity Server will treat all forwarded requests as trusted and perform only limited client authentication on its own.
Accidentally turning of client certificate verification on the reverse proxy (or making it optional instead of mandatory) can severely compromise your setup.
The Curity Identity Server has no way of telling between HTTP headers from the original request and HTTP headers injected by the reverse proxy server, it is important to filter out headers, that are used for authentication, from incoming HTTP requests.
Another thing to consider, is to use a randomized header name (e.g. X-ClientCertificate-Idsvr-CXVvfqrg) that is
kept a secret between the reverse proxy and Curity, so an attacker cannot guess the header name that is being used.
While it does not replace the need for request header sanitization, it would reduce the risk when request header
sanitization somehow doesn’t work as expected.
As an added security measure, the proxy server itself can be configured to perform basic HTTP authentication against
the Curity Identity Server.
In this case, a Proxy-Authentication HTTP header with corresponding credentials should be injected into requests by
the proxy server.
Setting Up NGINX As a Reverse Proxy Server#
If you’re running NGINX as a reverse proxy server in front of the Curity Identity Server, your NGINX configuration might look like this:
Example configuration snippet for NGINX before enabling mutual TLS:
server {
listen 443 ssl;
ssl_certificate /path/to/your/ssl/bundle-server.cert.pem;
ssl_certificate_key /path/to/your/ssl/server.key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
server_name server_name id.example.com;
location / {
proxy_pass http://localhost:8443/;
}
}
To enable mutual TLS client authentication, several things should be done:
- Enable client certificate validation (lines 8-9).
- Forward client certificate to the Curity Identity Server (line 15). Header name is assumed to be “X-ClientCertificate-Idsvr”.
- (Optionally) Add proxy authentication header (line 16).
Example configuration snippet for NGINX after enabling mutual TLS:
server {
listen 443 ssl;
ssl_certificate /path/to/your/ssl/bundle-server.cert.pem;
ssl_certificate_key /path/to/your/ssl/server.key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_verify_client on;
ssl_client_certificate /path/to/your/ssl/ca-chain-client.cert.pem;
server_name id.example.com;
location / {
proxy_pass http://localhost:8443/;
proxy_set_header X-ClientCertificate-Idsvr $ssl_client_escaped_cert;
proxy_set_header Proxy-Authorization "Basic dXNlcjpwYXNzd29yZA==";
}
}
For more advanced mutual TLS configuration options please refer to the NGINX configuration manual.
Setting Up HAProxy As a Reverse Proxy Server#
If you’re running HAProxy as a reverse proxy server in front of the Curity Identity Server, your HAProxy configuration might look like this:
Example configuration snippet for HAProxy before enabling mutual TLS:
frontend idsvr-frontend
bind 0.0.0.0:443 ssl crt /path/to/your/ssl/server.pem
default_backend idsvr-backend
timeout client 50000
backend idsvr-backend
mode https
timeout connect 5000
timeout server 50000
server idsvr1 localhost:8443 ssl verify none
Steps to enable proxy-based mutual TLS authentication for HAProxy are equivalent to NGINX:
- Enable client certificate validation (line 2).
- Forward client certificate to the Curity Identity Server (line 11). Header name is assumed to be “X-ClientCertificate-Idsvr”.
- (Optionally) Add proxy authentication header (line 12). Authentication token is a base64-encoded “username:password” string.
Example configuration snippet for HAProxy after enabling mutual TLS:
frontend idsvr-frontend
bind 0.0.0.0:443 ssl crt /path/to/your/ssl/server.pem ca-file /path/to/your/ssl/client-chain.pem verify required
default_backend idsvr-backend
timeout client 50000
backend idsvr-backend
mode https
timeout connect 5000
timeout server 50000
server idsvr1 localhost:8443 ssl verify none
http-request set-header X-ClientCertificate-Idsvr %{+Q}[ssl_c_der,base64]
http-request set-header Proxy-Authorization "Basic dXNlcjpwYXNzd29yZA=="
Setting Up Apache HTTPD 2.x As a Reverse Proxy#
If you’re running HTTPD as a reverse proxy server in front of the Curity Identity Server, your configuration might look like this:
Example configuration snippet for Apache HTTPD 2 before enabling mutual TLS:
Listen 443
<VirtualHost *:443>
ServerName id.example.com
SSLEngine on
SSLProxyEngine on
SSLProxyVerify require
SSLCertificateFile "/path/to/your/ssl/cert.pem"
SSLCertificateKeyFile "/path/to/your/ssl/key.pem"
# ID Server location
ProxyPass "/" "https://localhost:8443/"
ProxyPassReverse "/" "https://localhost:8443/"
</VirtualHost>
Steps to enable proxy-based mutual TLS authentication for Apache HTTPD are equivalent to NGINX or HAProxy:
- Enable client certificate validation (lines 10-11 and 19-20).
- Forward client certificate to the Curity Identity Server (lines 14 and 28). Header name is assumed to be “X-ClientCertificate-Idsvr”.
- (Optionally) Add proxy authentication header (line 30). Authentication token is a base64-encoded “username:password” string.
Example configuration snippet for Apache HTTPD 2 after enabling mutual TLS:
Listen 443
<VirtualHost *:443>
ServerName id.example.com
SSLEngine on
SSLProxyEngine on
SSLProxyVerify require
# Mandatory client certificate verification
SSLVerifyClient require
# Export additional environment variables (SSL_CLIENT_CERT among others)
SSLOptions +ExportCertData
SSLCertificateFile "/path/to/your/ssl/cert.pem"
SSLCertificateKeyFile "/path/to/your/ssl/key.pem"
# CA certificate bundle to verify client certs against.
SSLCACertificateFile "/path/to/your/ssl/ca-cert-chain.pem"
# ID Server location
ProxyPass "/" "https://localhost:8443/"
ProxyPassReverse "/" "https://localhost:8443/"
# Forward client certificate
RequestHeader set X-ClientCertificate-Idsvr %{SSL_CLIENT_CERT}e
# Proxy authentication
RequestHeader set Proxy-Authorization "Basic dXNlcjpwYXNzd29yZA=="
</VirtualHost>
Non-Templatized Dynamic Client Registration using Mutual TLS#
It is possible for non-templatized For Dynamic Clients to register using a DN of a certificate.
That DN should be sent on the registration request while calling the Dynamic Client Registration (DCR) endpoint.
The field that should be sent in the request is tls_client_auth_subject_dn and it should be a valid subject name.
For more information on what is considered a valid subject name feel free to consult the relevant RFC 4514.
Mutual TLS-specific client-authentication-method settings are only available when mutual-tls is enabled on the Token Profile,
and the profile’s settings of mutual-tls or mutual-tls-by-proxy will imply how they are used by the DCR’s settings.
The provided DN is accepted without further verification upon registration,
and when the client is authenticating using a client certificate, the provided certificate will have to be trusted by a configured
trusted-ca.
In other words, in order to authenticate the client, the CA of the certificate presented has to be in the list of trusted CAs provided in the configuration.
When tls_client_auth_subject_dn is sent in the request, the client-authentication-method for the client must be Mutual TLS.
This implies that, in this case, the value of the token_endpoint_auth_method registration parameter must be tls_client_auth.
If tls_client_auth_subject_dn is not sent in the request, any client-authentication-method allowed by the configuration
can be requested, even if Mutual TLS is used when calling the DCR endpoint.
More information about Mutual TLS Client Authentication and Certificate-Bound Access Tokens can be found in the relevant RFC (Section 2.1.2).
OrganizationIdentifier#
In eIDAS certificates, issued for PSD2 TPP’s contains an organizationIdentifier (OID 2.5.4.97) in the DN of the certificate.
When using this certificate to do non-templatized DCR, the organizationIdentifier will be parsed and returned as the client_id.
This means that there can be only one client registered per eIDAS certificate.
Match only organizationIdentifier#
The DN of eIDAS certificates contains an organizationIdentifier along with things like Adress, Company name etc.
When these certificates expire eIDAS might issue a new certificate with a slightly different DN, since those types of
attributes are subject to change. The organizationIdentifier is known to be persistent, and can be used to identify
the client instead of the full DN.
Enabling this setting also enables any certificate with the same organizationIdentifier as the DN in tls_client_auth_subject_dn.
This setting applies to client authentication to the token endpoint, as well as to a refresh token bound to a certificate.
The refresh flow will allow a client with a matching organizationIdentifier to refresh, enabling the clients to roll
certificates with eIDAS, and still use the received refresh token.
The Admin UI only allows the admin to configure the matching of organizationIdentifier, but the CLI and RESTCONF allows the admin to set other or multiple attributes to match. This could be used if other authorities do similar things as eIDAS, but with other attribute names. These attribute names are matched against attributes in the DN, not the certificate.
Database Clients upload client certificate PEM#
For Database Clients there is an option to upload the Client Certificate in the PEM format.
However, some constraints that need to be taken into the account:
- The certificate needs to be in the unencrypted PEM format without password protection.
- The PEM should contain only the certificate part, i.e. no private key present.
- The uploaded certificate should be issued by one of the Trusted certificates already present in the Curity Identity Server, i.e. Self-signed certificates are not allowed.
- Trusted certificate needs to be the immediate issuer of the uploaded certificate, i.e. certificate chain is not taken into account.