Skip to main content

Certificate-Based Authentication Using SAP Cloud SDK

note

This article talks about using mTLS to connect with cloud services like XSUAA or Destination service. If you are interested in using mTLS for communication between your application and target destination, please refer here instead.

The SAP Cloud SDK supports certificate-based authentication while establishing connections to other cloud services like XSUAA or Destination service.

Why Certificate-Based Authentication (mTLS)

Mutual TLS or mTLS for short is a method for mutual authentication. It ensures that before proceeding with the HTTP exchange both the client and the server each mutually verify each other's identities by the use of X.509 certificates. Using certificate-based authentication ensures overcoming the shortcomings of authenticating with clientsecret.

Shortcomings of Using Secret for Token Retrieval

Let's take an example of a service or an application protected by XSUAA. To access the service or application, you'll need a JWT. You will usually fetch a JWT by providing clientid, clientsecret and uri stored in your service binding to run a token retrieval flow say for e.g. client

The use of clientsecret has an inherent risk of these credentials being leaked, especially as they are not frequently rotated. Leaking these credentials into the hands of an attacker can cause a lot of harm and stay long unnoticed. The use of certificate-based authentication to fetch the JWT from XSUAA significantly reduces the risk of leaking secrets and makes rotating them easier.

note

The SAP Cloud SDK automatically

  • Fetches the JWT for you
  • Adds the fetched JWT to the Authorization header when requesting a service to ensure that the request gets authenticated and authorized.

Application developers do not need to worry about doing any of this themselves.

Certificate-Based Authentication for Services

To adhere to the latest recommended security best practices by SAP, some re-use and kernel services have enabled certificate-based authentication. As a consequence, SAP Cloud SDK also supports certificate-based authentication while accessing these services. If a service binding has a property credentials: { credential-type: x509 } then this is an indication that you can use certificate-based authentication to access the service.

XSUAA service is one of the services that has enabled certificate-based authentication. This means that in the service binding in addition to the client_secret you will also find certificate and key entries now. Example:

{
"VCAP_SERVICES": {
"xsuaa": [
{
"label": "xsuaa",
...
"credentials": {
...
"credential-type": "x509"
"clientid": "fictional-client-id",
"clientsecret": "fictional-secret",
"certificate": "-----BEGIN CERTIFICATE-----fictional-certificate-----END CERTIFICATE-----\n",
"key": "key"
...
},
}
]
}
}

This certificate would now be used in place of clientsecret to obtain the JWT using XSUAA token retrieval flows. The SAP Cloud SDK will as before fetch the JWT for you. The only difference is that the certificate from your service binding is used to authenticate the client.

Default Validity Period of Certificates

The X.509 certificates used in the service binding could either be external or XSUAA managed. By default XSUAA managed certificates are valid only for 7 days. Your calls to the XSUAA to fetch a JWT will fail after your certificate expires.

You can verify this by looking at the logs of your application, you should see

[APP/PROC/WEB/0] OUT Caused by: com.sap.cloud.sdk.cloudplatform.security.exception.TokenRequestFailedException:
com.sap.cloud.security.xsuaa.tokenflows.TokenFlowException:
Error requesting technical user token with grant_type 'client_credentials':
Error retrieving JWT token. Server URI https://<subdomain>.authentication.cert.<landscape domain>/oauth/token.
Http status code 400. Response body '400 Bad Request: TLS handshake failed.

[APP/PROC/WEB/0] OUT ssl_c_err: X509_V_ERR_CERT_HAS_EXPIRED

Don't get confused with the grant_type of client_credentials in the log, this is expected.

For X.509 certificate-based authentication, the token retrieval end point will look like https://<subdomain>.authentication.cert.<landscape domain>/oauth/token. Notice that the endpoint contains cert .

For authentication based on clientsecret, the end point would have looked like: https://<subdomain>.authentication.<landscape domain>/oauth/token

certificate renewal process

To obtain a new certificate, you will have to delete and re-create a service binding. The application also needs to be restarted to use the updated service binding.

Extending the Validity of a Certificate

For less frequent certificate rotation you can extend its validity to a maximum of 1 year. For the XSUAA managed certificate use parameters below while creating a service binding.

cf bind-service myapp myservice -c parameters.json

{
"credential-type": "x509",
"x509": {
"key-length": 2048,
"validity": 365,
"validity-type": "DAYS"
}
}
note

With longer certificate validity there is a danger that compromised certificate might go unnoticed for a significant period of time. This significantly increases potential damage to a compromised system.

Sticking to Using Secret for Token Retrieval

If you prefer to continue using clientSecret instead of Certificate-based Authentication, make sure your xs-security.json contains the instance-secret as the first entry in the property credential-types.

"oauth2-configuration": {
"credential-types": ["instance-secret"]
}

Using Automated Certificate Rotation using the Zero Trust Identity Service (SAP-internal)

For SAP-internal development teams the Zero Trust Identity Service (ZTIS) provides a way to automate the certificate rotation process.

Configuring your Application to use ZTIS

This guide covers how you can configure and use the SAP Cloud SDK to use certificates provided by ZTIS for certificate-based authentication.

Prerequisites for using ZTIS

The following prerequisites are required to use ZTIS:

  • You are deploying an application on Cloud Foundry
  • You have assigned the required entitlement for using ZTIS to your subaccount
  • You have created a service instance of zero-trust-identity in your Cloud Foundry space
  • You have bound the ZTIS service instance to your application
  • You have added the zero_trust_sidecar_buildpack as an additional buildpack for your application

Head over to the official documentation, the reference manual and sample code to learn more about how to use ZTIS.

To enable support by SAP Cloud SDK for certificates provided by ZTIS in your application, you need to add the following dependency to your pom.xml:

<dependency>
<groupId>com.sap.cloud.sdk.cloudplatform</groupId>
<artifactId>connectivity-ztis</artifactId>
</dependency>

With this dependency you can create a new or modify an existing HttpDestination to use the certificate provided by ZTIS.

var ks = ZeroTrustIdentityService.getInstance().getOrCreateKeyStore();

var newDestination = DefaultHttpDestination.builder("https://foo.com")
.keyStore(ks)
.build();
var enhancedDestination = DefaultHttpDestination.fromDestination(DestinationAccessor.getDestination("myDestination"))
.keyStore(ks)
.build();

Integration with Identity Authentication Service (IAS)

The SAP Cloud SDK also supports using certificates provided by ZTIS to authenticate to the Identity Authentication Service (IAS).

This works fully out of the box if you have an instance of IAS with the corresponding credential type X509_ATTESTED configured. For more details please refer to the documentation on connecting to services.

Developing Locally

On Cloud Foundry the zero_trust_sidecar_buildpack adds a sidecar to your application that fetches the certificates from ZTIS. When developing locally this sidecar is usually not available.

To develop locally you can instead store the certificate and key on your local file system. Download the certificate and key from a running instance of your application on Cloud Foundry via cf ssh:

cf ssh <app-name> -c "/home/vcap/deps/0/bin/spire-agent api fetch -socketPath /tmp/spire-agent/public/api.sock -write /tmp/"
cf ssh <app-name> -c "cat /tmp/svid.0.pem" > /path/to/certificate.pem
cf ssh <app-name> -c "cat /tmp/svid.0.key" > /path/to/key.key

You then need to provide the paths to the certificate and key with a service binding locally.

For example, you can locally create the VCAP_SERVICES environment variable with the following content:

{
"zero-trust-identity": [
{
"label": "zero-trust-identity",
"credentials": {
"certPath": "/path/to/certificate.pem",
"keyPath": "/path/to/key.key"
}
}
]
}

Alternatively, you can set the service binding programmatically:

new DefaultServiceBindingBuilder()
.withServiceIdentifier(ZTIS_IDENTIFIER)
.withCredentials(Map.of(
"certPath", "/path/to/certificate.pem",
"keyPath", "/path/to/key.key"))
.build();

// in case you only need this one service binding the following code is fine:
DefaultServiceBindingAccessor.setInstance(() -> List.of(binding));

You can read more about how to create service bindings for local development here.