Salesforce Mutual Authentication – Part 1: the Basics

Mutual Authentication was introduced by Salesforce in the Winter ’14 release. As the Salesforce Winter ’14 release notes explain,  mutually authenticated transport layer security (TLS) allows secure server-to-server connections initiated by a client using client certificate authentication, and means that both the client and the server authenticate and verify that they are who they say they are. In this blog post, I’ll show you how to enable Mutual Authentication and perform some basic tests using the curl command line tool. In a future blog post, I’ll show you how to implement Mutual Authentication in your Java apps.

In the default case, without Mutual Authentication, when an API client connects to Salesforce via TLS, the client authenticates the server via its TLS certificate, but the TLS connection itself gives the server no information on the client’s identity. After the TLS session is established, the client sends a login request containing its credentials over the secure channel, the Salesforce login service responding with a session ID. The client then sends this session ID with each API request.

Mutual Authentication provides an additional layer of security. Each time you connect to a Salesforce API, the server checks that the client’s certificate is valid for the client’s org, as well as checking the validity of the session ID. Note that Mutual Authentication is intended for API use and not for user interface (web browser) use.

Before you can use Mutual Authentication, you need to obtain a client certificate. This certificate must be issued by a certificate authority with its root certificate in the Salesforce Outbound Messaging SSL CA Certificates list; Mutual Authentication will not work with a self-signed client certificate. More information is available in the Salesforce document, Set Up a Mutual Authentication Certificate. I bought an SSL certificate from GoDaddy – you can almost certainly find a cheaper alternative if you spend some time looking.

Enabling Mutual Authentication in Salesforce

Mutual Authentication is not enabled by default. You must open a support case with Salesforce to enable it. When it is enabled, you will see a Mutual Authentication Certificates section at Setup | Administer | Security Controls | Certificate and Key Management.

Mutual Authentication Configuration

You must upload a PEM-encoded client certificate to this list. Note that you need only upload the client certificate itself; do not upload a certificate chain.

You will also need to create a user profile with the Enforce SSL/TLS Mutual Authentication user permission enabled. Clone an existing Salesforce profile and enable Enforce SSL/TLS Mutual Authentication. Check that the profile has the Salesforce object permissions that your application will need to access data. Assign the new profile to the user which your app will use to access Salesforce.

Testing Mutual Authentication with curl

This was a stumbling block for me for some time. First, despite what the Salesforce documentation (Configure Your API Client to Use Mutual Authentication) says, the Salesforce login service does not support Mutual Authentication. You cannot connect to on port 8443 as described in the docs.

You can, however, send a normal authentication request for a user with Enforce SSL/TLS Mutual Authentication enabled to the default TLS port, 443. The login service responds with a session ID as for any other login request. Mutual Authentication is enforced when you use the session ID with an API endpoint.

Let’s try this out. Here’s a SOAP login request – add a username/password and save it to login.xml:

<?xml version="1.0" encoding="utf-8" ?>
<env:Envelope xmlns:xsd=""
    <n1:login xmlns:n1="">

Now you can send it to the login service with curl:

$ curl -s -k \
    -H "Content-Type: text/xml; charset=UTF-8" \
    -H "SOAPAction: login" \
    -d @login.xml | xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="" xmlns="" xmlns:xsi="">
          ...lots of user data...

We need to create a PEM file for curl with the signing key, client certificate, and all the certificates in its chain except the root. This file looks something like this:

...base 64 encoded private key data...
...base64 encoded client certificate data...
...base64 encoded CA issuing cert...
...another base64 encoded CA issuing cert...

We’ll call the getUserInfo API. Here’s the SOAP request – add the session ID returned from login and save it as getuserinfo.xml:

<?xml version="1.0" encoding="utf-8"?> 
<soapenv:Envelope xmlns:soapenv=""
    <urn:getUserInfo />

Now we’re ready to make a mutually authenticated call to a Salesforce API! You’ll need to specify the correct instance, as returned in the login response, in the URL. Note the port number is 8443:

$ curl -s -k \
    -H "Content-Type: text/xml; charset=UTF-8" \
    -H "SOAPAction: example" \
    -d @getuserinfo.xml \
    -E fullcert.pem | xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="" xmlns="" xmlns:xsi="">
        <type>API REQUESTS</type>
        ...all the user data...

Now let’s look at a couple of failure modes. What happens when we call the 8443 port, but don’t pass a client certificate?

$ curl -s -k \
    -H "Content-Type: text/xml; charset=UTF-8" \
    -H "SOAPAction: example" \
    -d @getuserinfo.xml
<html><head><title>Certificate Error</title></head><body bgcolor=#ffffff text=#3198d8><center><img src=""><p><h3>Client certificate error:<i>No client certificate provided.</i></h3></center></body></html>

Note the HTML response, rather than XML!

What about calling the regular 443 port with this session ID?

$ curl -s -k \
    -H "Content-Type: text/xml; charset=UTF-8" \
    -H "SOAPAction: example" \
    -d @getuserinfo.xml
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="" xmlns:sf="" xmlns:xsi="">
      <faultstring>MUTUAL_AUTHENTICATION_FAILED: This session could not be mutually authenticated for use with the API</faultstring>
        <sf:UnexpectedErrorFault xsi:type="sf:UnexpectedErrorFault">
          <sf:exceptionMessage>This session could not be mutually authenticated for use with the API</sf:exceptionMessage>

This time we get a much more palatable response!

Now you know how to get the basics of Salesforce Mutual Authentication working. In part 2 of this series, I look at using Salesforce’s Web Service Connector (WSC) to access the SOAP and Bulk APIs with Mutual Authentication, and in part 3, I explain how to access the Salesforce REST APIs with common Java HTTP clients such as the Apache and Jetty.

18 Replies to “Salesforce Mutual Authentication – Part 1: the Basics”

  1. Hi,
    Nice to read your article. its very detailed.

    I need your help in Client Certificate. Please let me know how did you get the SSL Client Certificate from Godaddy . I tried a lot but didn’t get any information.


  2. Hi,

    I got a timeout on port 8443 when I mentioned it. Have you encountered this before and how did you resolve it?

    1. Aaron – which host were you trying to connect to? As I mentioned in the article, when I was working on this, was not listening on 8443.

  3. I followed all the steps but my curl keeps saying “Client certificate error: unable to get local issuer certificate”
    Need urgent help on this.

  4. Hi,
    I am also working with
    I have the certificate which is issued by ADP . I have inserted by certificate in salesforce under Mutual Authentication but when i tried to access certificate in my HTTP Request using req.setClientCertificateName(‘ADP’);
    I got an error :
    System.CalloutException: Could not find client cert with dev name: ‘ADP’
    Please suggests what am i missing?

  5. Hi Pat,

    I had a very basic question. When you say the certificate should be signed by a Saleforce trusted Root CA, you mean we need to buy one even to try MTLS on sandboxes?

  6. Hi,

    We got a CA Signed Certificate from the Client Target Host.

    We have uploaded it in Mutual Authentication.

    Do we also have to share Self-Signed with the Client Target Host now?

    We thought it will automatically generate a Self-Signed one.

  7. Hi Pat,

    In this article we see a statement – “We need to create a PEM file for curl with the signing key, client certificate, and all the certificates in its chain except the root.”.

    From where do we get this file?

    1. Hi Prem! You have to create a signing key and submit the public key to one of the CA’s trusted by Salesforce. You CANNOT use a self-signed certificate. The details vary according to which CA you use. I’ve used GoDaddy in the past – their instructions are here. You generate the key and certificate signing request (CSR), keep the key (it’s your secret!) and submit the CSR to the CA. They will issue the certificate as a PEM file that you can download. NOTE – you need an SSL certificate, not a code-signing certificate.

  8. Fully support your preference on the Scotch side 😉 !

    This is all well explained, but this is for the case when Somebody externally trying to reach Salesforce.

    But would you know if it is possible in an opposite direction? When we have an external End Point and we shared our Certificate with the server side, and we have received a Server certificate.

    I see no place to save it, at least not on the Named Credential side.

    This article is written to pretend it is two-way ssl but it describes only the client certificate (salesforce) on how to sign requests:

    I need to understand if this is possible, and if it is, on where should I save Server side certificate in the salesforce, and make sure my code validates the end point against it?


    1. Hi Vlad… Unfortunately, it’s not possible; you have to use a ‘real’ SSL server cert signed by a CA trusted by Salesforce:

      When sending outbound messages, delegated authentication requests or Apex callouts to secure/SSL endpoints (e.g., a organization (acting as the client) will only trust the target host (that will act as the server) if this presents a certificate signed by a root Certification Authority (CA) […]. In other words, in this scenario self-signed certificates are not allowed to be used by the target host.


Leave a Reply

Your email address will not be published. Required fields are marked *