3 Using SSL 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
The minimal setup is not the most secure setup of SSL.
To set up client/server connections:
Step 1: Start the server side:
1 server> ssl:start(). ok
Step 2: Create an SSL listen socket:
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 SSL listen socket:
3 server> {ok, Socket} = ssl:transport_accept(ListenSocket). {ok,{sslsocket, [...]}}
Step 4: Start the client side:
1 client> ssl:start(). ok
2 client> {ok, Socket} = ssl:connect("localhost", 9999, [], infinity). {ok,{sslsocket, [...]}}
Step 5: Do the SSL handshake:
4 server> ok = ssl:ssl_accept(Socket). ok
Step 6: Send a message over SSL:
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
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 SSL handshake:
5 server> {ok, SSLSocket} = ssl:ssl_accept(Socket, [{cacertfile, "cacerts.pem"}, {certfile, "cert.pem"}, {keyfile, "key.pem"}]). {ok,{sslsocket,[...]}}
Step 7: Upgrade to an SSL connection. The client and server must agree upon the upgrade. The server must call ssl:accept/2 before the client calls ssl:connect/3.
3 client>{ok, SSLSocket} = ssl:connect(Socket, [{cacertfile, "cacerts.pem"}, {certfile, "cert.pem"}, {keyfile, "key.pem"}], infinity). {ok,{sslsocket,[...]}}
Step 8: Send a message over SSL:
4 client> ssl:send(SSLSocket, "foo"). ok
Step 9: Set active true on the SSL socket:
4 server> ssl:setopts(SSLSocket, [{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