The minimal setup is not the most secure setup of SSL/TLS/DTLS.
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.
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.
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
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
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}, ...]
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
© 2010–2017 Ericsson AB
Licensed under the Apache License, Version 2.0.