Erlang -- Using SSL application API

Secure Socket Layer

User's Guide

Version 9.0.2

- User's Guide
- Reference Manual
- Release Notes
- PDF
- Top

- Expand All
- Contract All

### Chapters

- Introduction
  - Top of chapter
  - Purpose
  - Prerequisites
- TLS/DTLS and TLS Predecessor, SSL
  - Top of chapter
  - Security Overview
  - Data Privacy and Integrity
  - Digital Certificates
  - Peer Authentication
  - TLS Sessions
- Using SSL application API
  - Top of chapter
  - Setting up Connections
  - Customizing cipher suits
  - Using an Engine Stored Key
- Using TLS for Erlang Distribution
  - Top of chapter
  - Building Boot Scripts Including the SSL Application
  - Specifying Distribution Module for net_kernel
  - Specifying SSL/TLS Options
  - Specifying SSL/TLS Options (Legacy)
  - Setting up Environment to Always Use SSL/TLS (Legacy)
  - Using SSL/TLS distribution over IPv6

# 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