3 Using SSL application API

To see relevant version information for ssl, call ssl:versions/0 .

To see all supported cipher suites, call ssl:cipher_suites(all). The available cipher suites for a connection depend on your certificate. Specific cipher suites that you want your connection to use can also be specified. Default is to use the strongest available.

3.1  Setting up Connections

This section shows a small example of how to set up client/server connections using the Erlang shell. The returned value of the sslsocket is abbreviated with [...] as it can be fairly large and is opaque.

Minimal Example

Note

The minimal setup is not the most secure setup of SSL/TLS/DTLS.

To set up client/server connections:

Step 1: Start the server side:

1 server> ssl:start().
ok

Step 2: Create an TLS listen socket: (To run DTLS add the option {protocol, dtls})

2 server> {ok, ListenSocket} =
ssl:listen(9999, [{certfile, "cert.pem"}, {keyfile, "key.pem"},{reuseaddr, true}]).
{ok,{sslsocket, [...]}}

Step 3: Do a transport accept on the TLS listen socket:

3 server> {ok, TLSTransportSocket} = ssl:transport_accept(ListenSocket).
{ok,{sslsocket, [...]}}

Step 4: Start the client side:

1 client> ssl:start().
ok

To run DTLS add the option {protocol, dtls} to third argument.

2 client> {ok, Socket} = ssl:connect("localhost", 9999,  [], infinity).
{ok,{sslsocket, [...]}}

Step 5: Do the TLS handshake:

4 server> {ok, Socket} = ssl:handshake(TLSTransportSocket).
ok

Step 6: Send a message over TLS:

5 server> ssl:send(Socket, "foo").
ok

Step 7: Flush the shell message queue to see that the message was sent on the server side:

3 client> flush().
Shell got {ssl,{sslsocket,[...]},"foo"}
ok

Upgrade Example - TLS only

Note

To upgrade a TCP/IP connection to an SSL connection, the client and server must agree to do so. The agreement can be accomplished by using a protocol, for example, the one used by HTTP specified in RFC 2817.

To upgrade to an SSL connection:

Step 1: Start the server side:

1 server> ssl:start().
ok

Step 2: Create a normal TCP listen socket:

2 server> {ok, ListenSocket} = gen_tcp:listen(9999, [{reuseaddr, true}]).
{ok, #Port<0.475>}

Step 3: Accept client connection:

3 server> {ok, Socket} = gen_tcp:accept(ListenSocket).
{ok, #Port<0.476>}

Step 4: Start the client side:

1 client> ssl:start().
ok
2 client> {ok, Socket} = gen_tcp:connect("localhost", 9999,  [], infinity).

Step 5: Ensure active is set to false before trying to upgrade a connection to an SSL connection, otherwise SSL handshake messages can be delivered to the wrong process:

4 server> inet:setopts(Socket, [{active, false}]).
ok

Step 6: Do the TLS handshake:

5 server> {ok, TLSSocket} = ssl:handshake(Socket, [{cacertfile, "cacerts.pem"},
{certfile, "cert.pem"}, {keyfile, "key.pem"}]).
{ok,{sslsocket,[...]}}

Step 7: Upgrade to an TLS connection. The client and server must agree upon the upgrade. The server must call ssl:handshake/2 before the client calls ssl:connect/3.

3 client>{ok, TLSSocket} = ssl:connect(Socket, [{cacertfile, "cacerts.pem"},
{certfile, "cert.pem"}, {keyfile, "key.pem"}], infinity).
{ok,{sslsocket,[...]}}

Step 8: Send a message over TLS:

4 client> ssl:send(TLSSocket, "foo").
ok

Step 9: Set active true on the TLS socket:

4 server> ssl:setopts(TLSSocket, [{active, true}]).
ok

Step 10: Flush the shell message queue to see that the message was sent on the client side:

5 server> flush().
Shell got {ssl,{sslsocket,[...]},"foo"}
ok

3.2  Customizing cipher suits

Fetch default cipher suite list for an TLS/DTLS version. Change default to all to get all possible cipher suites.

1>  Default = ssl:cipher_suites(default, 'tlsv1.2').
    [#{cipher => aes_256_gcm,key_exchange => ecdhe_ecdsa,
    mac => aead,prf => sha384}, ....]

In OTP 20 it is desirable to remove all cipher suites that uses rsa kexchange (removed from default in 21)

2> NoRSA =
    ssl:filter_cipher_suites(Default,
                            [{key_exchange, fun(rsa) -> false;
			                       (_) -> true end}]).
    [...]

Pick just a few suites

 3> Suites =
    ssl:filter_cipher_suites(Default,
                            [{key_exchange, fun(ecdh_ecdsa) -> true;
			                       (_) -> false end},
                             {cipher, fun(aes_128_cbc) ->true;
			                  (_) ->false end}]).
    [#{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,
     mac => sha256,prf => sha256},
     #{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,mac => sha,
     prf => default_prf}]

Make some particular suites the most preferred, or least preferred by changing prepend to append.

 4>ssl:prepend_cipher_suites(Suites, Default).
  [#{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,
     mac => sha256,prf => sha256},
   #{cipher => aes_128_cbc,key_exchange => ecdh_ecdsa,mac => sha,
     prf => default_prf},
   #{cipher => aes_256_cbc,key_exchange => ecdhe_ecdsa,
     mac => sha384,prf => sha384}, ...]

3.3  Using an Engine Stored Key

Erlang ssl application is able to use private keys provided by OpenSSL engines using the following mechanism:

1> ssl:start().
ok

Load a crypto engine, should be done once per engine used. For example dynamically load the engine called MyEngine:

2> {ok, EngineRef} =
crypto:engine_load(<<"dynamic">>,
                   [{<<"SO_PATH">>, "/tmp/user/engines/MyEngine"},<<"LOAD">>],[]).
{ok,#Ref<0.2399045421.3028942852.173962>}

Create a map with the engine information and the algorithm used by the engine:

3> PrivKey =
 #{algorithm => rsa,
   engine => EngineRef,
   key_id => "id of the private key in Engine"}.

Use the map in the ssl key option:

4> {ok, SSLSocket} =
ssl:connect("localhost", 9999,
            [{cacertfile, "cacerts.pem"},
             {certfile, "cert.pem"},
             {key, PrivKey}], infinity).

See also crypto documentation