Red5 Pro with SSL

This document covers the use of free SSL certificates from the Let’s Encrypt project on a Debian-based operating system (Ubuntu). Additionally, we've included some notes on DigiCert and GoDaddy. Most other ssl certificate providers will provide documentation on how to use their certs to create the necessary keystore and truststore files.

Let’s Encrypt is a free, automated and open certificate authority brought to you by the Internet Security Research Group (ISRG). ISRG is a California public benefit corporation, and is recognized by the IRS as a tax-exempt organization under Section 501(c)(3) of the Internal Revenue Code. Find them online at: https://letsencrypt.org/


Prerequisites

These prerequisites are only for the examples provided below, they are not meant to be the only platform on which Red5 Pro will work using SSL.

The last item is very important and can prevent certificate creation, if not configured properly.


Installing Let's Encrypt

If we’re not using Let’s Encrypt, this section may be skipped.

To create our certificate and chain, we first clone the letsencrypt project repository:

$ git clone https://github.com/letsencrypt/letsencrypt
$ cd letsencrypt

To prepare our environment and see the available options, execute the following command:

$ ./letsencrypt-auto --help

System dependencies will be downloaded and installed; you may also see some warnings like this, which may be disregarded:

InsecurePlatformWarning
./root/.local/share/letsencrypt/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.

If Red5 Pro is running at this point, shut it down.


Getting a Certificate

To obtain a CA signed certificate from Let’s Encrypt, substitute the yourname@example.com with your email address and ssl.example.com with the fully qualified domain name for your Red5 Pro server in the command below (multiple hostnames may be supplied with additional “-d” options):

$ ./letsencrypt-auto certonly --standalone --email yourname@example.com --agree-tos -d ssl.example.com

If the process completes successfully, you will see a message similar to this:

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/ssl.example.com/fullchain.pem. Your cert will
   expire on 2016-03-20. To obtain a new version of the certificate in
   the future, simply run Let's Encrypt again.
 - If you like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Please note that these particular certificates expire after 90 days and must be renewed.

You can renew by running the above command again.

If the hostname is improperly configured in DNS or some other issue occurs, you may see this message:

Failed authorization procedure. ssl.example.com (tls-sni-01): urn:acme:error:connection :: The server could not connect to the client to verify the domain :: Server failure at resolver

IMPORTANT NOTES:
 - If you lose your account credentials, you can recover through
   e-mails sent to yourname@example.com.
 - The following 'urn:acme:error:connection' errors were reported by
   the server:

   Domains: ssl.example.com
   Error: The server could not connect to the client to verify the
   domain
 - Your account credentials have been saved in your Let's Encrypt
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Let's
   Encrypt so making regular backups of this folder is ideal.

Creating the Keystore

A keystore contains private keys and certificates with their corresponding public keys.

To create a keystore for Red5 Pro’s embedded Tomcat, we expect to have our full certificate chain in PEM format; if you have your certificate, root, and intermediate certificates in some other format or in separate files, you’ll need to convert and consolidate them per your certificate authorities instructions.

The first step is to export our keys and certs into a PKCS12 formatted file:

When prompted for a password, enter one and make note of it since it will be needed throughout this process. The examples herein use the password changeit (we suggest that you use your own password).

$ sudo openssl pkcs12 -export \
  -in /etc/letsencrypt/live/ssl.example.com/fullchain.pem \
  -inkey /etc/letsencrypt/live/ssl.example.com/privkey.pem \
  -out /etc/letsencrypt/live/ssl.example.com/fullchain_and_key.p12 \
  -name tomcat

The command has been separated by \ and a carraige return for clarity.

It is important to note that the tomcat alias must be provided as-is.

Now we create the Java Keystore (don’t forget to substitute your password for "changeit" and your URl for "ssl.example.com" below):

$ sudo keytool -importkeystore \
  -deststorepass changeit \
  -destkeypass changeit \
  -destkeystore /etc/letsencrypt/live/ssl.example.com/keystore.jks \
  -srckeystore /etc/letsencrypt/live/ssl.example.com/fullchain_and_key.p12 \
  -srcstoretype PKCS12 \
  -srcstorepass changeit \
  -alias tomcat

Creating the Truststore

A truststore contains certificates from other parties that you expect to communicate with, or from Certificate Authorities that you trust to identify other parties. Again, the examples herein use the password changeit.

The first step is to export the certificate that we’ll be trusting (as in the above examples, replace ssl.example.com with your URL) (You will be prompted to enter your keystore password, from above):

$ sudo keytool -export \
  -alias tomcat \
  -file /etc/letsencrypt/live/ssl.example.com/tomcat.cer \
  -keystore /etc/letsencrypt/live/ssl.example.com/keystore.jks

The command has been separated by \ and a carraige return for clarity.

A successful response will be: Certificate stored in file </etc/letsencrypt/live/ssl.example.com/tomcat.cer>

Then, to import the truststore (only necessary if using Let's Encrypt):

First:

$ sudo keytool -import -file /etc/letsencrypt/live/ssl.example.com/tomcat.cer -alias tomcat -keystore truststore.jks -storepass changeit -noprompt

Second:

$ sudo keytool -import -trustcacerts -alias tomcat \
 -file /etc/letsencrypt/live/ssl.example.com/tomcat.cer \
 -keystore /etc/letsencrypt/live/ssl.example.com/truststore.jks \
 -storepass changeit -noprompt

A successful response will be: Certificate was added to keystore.

As a verification step, your letsencrypt directory (/etc/letsencrypt/live/ssl.example.com/) should contain these files:

$ sudo ls /etc/letsencrypt/live/ssl.example.com/
cert.pem   fullchain_and_key.p12  keystore.jks  tomcat.cer
chain.pem  fullchain.pem          privkey.pem   truststore.jks

the truststore.jks and keystore.jks are used by Red5 Pro.


GoDaddy Process

To use an ssl cert provided by GoDaddy:

  1. Create your key:
keytool -keysize 2048 -genkey -alias red5 -keyalg RSA -keystore keystore
Enter keystore password:
Re-enter new password:
What is your first and last name?
  [Unknown]:  ssl.red5.org
What is the name of your organizational unit?
  [Unknown]:  Dev
What is the name of your organization?
  [Unknown]:  Red5
What is the name of your City or Locality?
  [Unknown]:  Henderson
What is the name of your State or Province?
  [Unknown]:  Nevada
What is the two-letter country code for this unit?
  [Unknown]:  US
Is CN=ssl.red5.org, OU=Dev, O=Red5, L=Henderson, ST=Nevada, C=US correct?
  [no]:  yes

Enter key password for <red5>
        (RETURN if same as keystore password):
  1. Create a CSR
$ keytool -certreq -keyalg RSA -alias red5 -file red5.csr -keystore keystore
  1. Submit your CSR to GoDaddy.
  1. Once available, download your SSL cert files (server type = Tomcat). The zip file from GoDaddy will contain three files:
    • Your root certificate file will be a serial number (e.g. 123abc678901def.crt)
    • The cross certificate file is gdig2.crt
    • The intermediate cetificate file is gd_bundle-g2-g1.crt
  1. Import the root certificate
$ keytool -import -alias root -keystore keystore.jks -trustcacerts -file 123abc678901def.crt
  1. Import the cross certificates
$ keytool -import -alias cross -keystore keystore.jks -trustcacerts -file gdig2.crt
  1. Import the intermediate certificates
$ keytool -import -alias intermed -keystore keystore.jks -trustcacerts -file gd_bundle-g2-g1.crt
  1. Import your certificate
$ keytool -import -alias red5 -keystore keystore.jks -trustcacerts -file ssl.red5.org.crt
  1. Now you need to export the certificate so that it can be added to the truststore as the trusted certificate:
$ keytool -export -alias red5 -file red5.cer -keystore keystore.jks
  1. The certificate file, red5.cer, can now be used to populate your truststore. The password for the truststore must be provided. It can be the same, or different, to the one for the keystore. You will need this password for the `red5.properties` file.
$ keytool -import -v -trustcacerts -alias red5 -file red5.cer -keystore truststore.jks

Configuring Red5 Pro

This configuration of Red5 Pro will make the assumption that you want to handle HTTP and HTTPS on their standard ports of 80 and 443, respectively; normally the Red5 server is configured to use 5080 and 5443 for HTTP and HTTPS.

The port configuration used in the examples are not the only available option and any open ports may be used, but using standard ports prevents the need to specify alternatives when making HTTP and HTTPS connections.

Red5 Server Properties

The red5.properties file is located in the conf directory where you installed Red5 Pro. We will make six changes to this file.

  1.  Open the file and change the `http.port` and `https.port` as shown below:

Original:

http.port=5080
https.port=5443

Modified:

http.port=80
https.port=443
  1.  Scroll down and locate the `rtmps.keystorepass` property. Modify the following four properties as shown:

Original:

rtmps.keystorepass=password
rtmps.keystorefile=conf/keystore.jks
rtmps.truststorepass=password
rtmps.truststorefile=conf/truststore.jks

Modified:

rtmps.keystorepass=changeit
rtmps.keystorefile=/etc/letsencrypt/live/ssl.example.com/keystore.jks
rtmps.truststorepass=changeit
rtmps.truststorefile=/etc/letsencrypt/live/ssl.example.com/truststore.jks
  1.  Save and close the red5.properties file.

JEE Container Configuration

The jee-container.xml file is also located in the conf directory along with the red5.properties file. We will make one large change to this file.

  1.  Open the file and locate the first tomcat.server xml node (Tomcat without SSL enabled).
  2.  Comment out the node.
  3.  Locate the second tomcat.server xml node (Tomcat with SSL enabled) and remove the comment tags. The file content should now resemble the xml shown below:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd">

    <!-- War deployer -->
    <bean id="warDeployer" class="org.red5.server.tomcat.WarDeployer">
        <property name="checkInterval" value="${war.deploy.server.check.interval}"/>
        <property name="webappFolder" value="${red5.root}/webapps"/>
        <!-- Expand war files prior to startup of the remaining services -->
        <property name="expandWars" value="true" />
    </bean>

    <!-- Tomcat without SSL enabled -->
<!--
    <bean id="tomcat.server" class="org.red5.server.tomcat.TomcatLoader" depends-on="context.loader,warDeployer" lazy-init="true">

        <property name="webappFolder" value="${red5.root}/webapps" />

        <property name="connectors">
            <list>
                <bean name="httpConnector" class="org.red5.server.tomcat.TomcatConnector">
                    <property name="protocol" value="org.apache.coyote.http11.Http11NioProtocol" />
                    <property name="address" value="${http.host}:${http.port}" />
                    <property name="redirectPort" value="${https.port}" />
                </bean>
            </list>
        </property>

        <property name="baseHost">
           <bean class="org.apache.catalina.core.StandardHost">
               <property name="name" value="${http.host}" />
           </bean>
        </property>

        <property name="valves">
            <list>
                <bean id="valve.access" class="org.apache.catalina.valves.AccessLogValve">
                    <property name="directory" value="log" />
                    <property name="prefix" value="${http.host}_access." />
                    <property name="suffix" value=".log" />
                    <property name="pattern" value="common" />
                    <property name="rotatable" value="true" />
                </bean>
            </list>
        </property>

    </bean>
-->

    <!-- Tomcat with SSL enabled -->
    <bean id="tomcat.server" class="org.red5.server.tomcat.TomcatLoader" depends-on="context.loader" lazy-init="true">

        <property name="webappFolder" value="${red5.root}/webapps" />

        <property name="connectors">
            <list>
                <bean name="httpConnector" class="org.red5.server.tomcat.TomcatConnector">
                    <property name="protocol" value="org.apache.coyote.http11.Http11NioProtocol" />
                    <property name="address" value="${http.host}:${http.port}" />
                    <property name="redirectPort" value="${https.port}" />
                </bean>
                <bean name="httpsConnector" class="org.red5.server.tomcat.TomcatConnector">
                    <property name="secure" value="true" />
                    <property name="protocol" value="org.apache.coyote.http11.Http11NioProtocol" />
                    <property name="address" value="${http.host}:${https.port}" />
                    <property name="redirectPort" value="${http.port}" />
                    <property name="connectionProperties">
                        <map>
                            <entry key="port" value="${https.port}" />
                            <entry key="redirectPort" value="${http.port}" />
                            <entry key="SSLEnabled" value="true" />
                            <entry key="sslProtocol" value="TLS" />
                            <entry key="keystoreFile" value="${rtmps.keystorefile}" />
                            <entry key="keystorePass" value="${rtmps.keystorepass}" />
                            <entry key="keystoreType" value="JKS" />
                            <entry key="truststoreFile" value="${rtmps.truststorefile}" />
                            <entry key="truststorePass" value="${rtmps.truststorepass}" />
                            <entry key="clientAuth" value="false" />
                            <entry key="allowUnsafeLegacyRenegotiation" value="true" />
                            <entry key="maxKeepAliveRequests" value="${http.max_keep_alive_requests}"/>
                            <entry key="useExecutor" value="true"/>
                            <entry key="maxThreads" value="${http.max_threads}"/>
                            <entry key="acceptorThreadCount" value="${http.acceptor_thread_count}"/>
                            <entry key="processorCache" value="${http.processor_cache}"/>
                        </map>
                    </property>
                </bean>
            </list>
        </property>

        <property name="baseHost">
            <bean class="org.apache.catalina.core.StandardHost">
                <property name="name" value="${http.host}" />
            </bean>
        </property>

    </bean>

    <!-- This entry enabled websocket support on port 8081 at localhost -->
    <bean id="webSocketTransport" class="org.red5.net.websocket.WebSocketTransport">
        <property name="addresses">
            <list>
                <value>${ws.host}:${ws.port}</value>
            </list>
        </property>
    </bean>

</beans>
  1.  Save and close the jee-container.xml file.

Securing Websockets

This piece is essential if you are running WebRTC for Red5 Pro.

The first step is to decide which port you’ll be using for your wss connections. We suggest using 8083 if you’re using 8081 for regular WebSocket connections. This is also a suggestion as there is no “standard” or IETF specified port that must be used at this time. Once you’ve decided on a port, open the jee-container.xml file in your red5pro/conf directory. Scroll to the bottom and you should find the webSocketTransport bean shown below:

 <bean id="webSocketTransport" class="org.red5.net.websocket.WebSocketTransport">
        <property name="addresses">
            <list>
                <value>${ws.host}:${ws.port}</value>
            </list>
        </property>
    </bean>

Add the additional bean definition as shown and a webSocketTransportSecure instance will be created when you restart Red5 Pro.

<bean id="webSocketTransport" class="org.red5.net.websocket.WebSocketTransport">
        <property name="addresses">
            <list>
                <value>${ws.host}:${ws.port}</value>
            </list>
        </property>
    </bean>
    <bean id="webSocketTransportSecure" class="org.red5.net.websocket.WebSocketTransport">
        <property name="secureConfig">
            <bean id="webSocketSecureConfig" class="org.red5.net.websocket.SecureWebSocketConfiguration">
                <property name="keystoreType" value="JKS"/>
                <property name="keystoreFile" value="${rtmps.keystorefile}"/>
                <property name="keystorePassword" value="${rtmps.keystorepass}"/>
                <property name="truststoreFile" value="${rtmps.truststorefile}"/>
                <property name="truststorePassword" value="${rtmps.truststorepass}"/>
            </bean>
        </property>
        <property name="addresses">
            <list>
                <value>${wss.host}:${wss.port}</value>
            </list>
        </property>
    </bean>

Those references to rtmp.keystorefile etc are not typos, in these docs we’re using the same parameters for HTTPS, RTMPS, and WSS for simplicity. You are not required to do this and may use parameters of your own.

Now we will add the port and host for wss in the red5.properties file, in your red5pro/conf directory. Open the file and locate the section below:

# WebSocket
ws.host=0.0.0.0
ws.port=8081

Add the two lines for wss as shown and then save the file:

# WebSocket
ws.host=0.0.0.0
ws.port=8081
wss.host=0.0.0.0
wss.port=8083

Secure RTMP

To enable RTMPS, we need to first open the red5-core.xml located in the red5pro/conf directory. Once you’ve got it in your editor, scroll down to the section shown below:

    <!-- RTMPS -->
    <!-- Notes to self:
         https://www.openssl.org/docs/apps/ciphers.html#TLS-v1.2-cipher-suites
         https://www.sslshopper.com/article-how-to-disable-weak-ciphers-and-ssl-2-in-tomcat.html -->
    <bean id="rtmpsMinaIoHandler" class="org.red5.server.net.rtmps.RTMPSMinaIoHandler">
        <property name="handler" ref="rtmpHandler" />
        <property name="codecFactory" ref="rtmpCodecFactory" />
        <property name="keystorePassword" value="${rtmps.keystorepass}" />
        <property name="keystoreFile" value="${rtmps.keystorefile}" />
        <property name="truststorePassword" value="${rtmps.truststorepass}" />
        <property name="truststoreFile" value="${rtmps.truststorefile}" />
        <property name="useClientMode" value="false" />
        <property name="needClientAuth" value="false" />
        <property name="cipherSuites">
            <array>
                <value>TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256</value>
                <value>TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384</value>
                <value>TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA</value>
                <value>TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384</value>
                <value>TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA</value>
                <value>TLS_ECDHE_RSA_WITH_RC4_128_SHA</value>
                <value>TLS_RSA_WITH_AES_128_CBC_SHA256</value>
                <value>TLS_RSA_WITH_AES_128_CBC_SHA</value>
                <value>TLS_RSA_WITH_AES_256_CBC_SHA256</value>
                <value>TLS_RSA_WITH_AES_256_CBC_SHA</value>
                <value>SSL_RSA_WITH_RC4_128_SHA</value>
            </array>
        </property>
        <property name="protocols">
            <array>
                <value>TLSv1</value>
                <value>TLSv1.1</value>
                <value>TLSv1.2</value>
            </array>
        </property>
    </bean>

    <bean id="rtmpsTransport" class="org.red5.server.net.rtmp.RTMPMinaTransport" init-method="start" destroy-method="stop">
        <property name="ioHandler" ref="rtmpsMinaIoHandler" />
        <property name="addresses">
            <list>
                 <value>${rtmps.host}:${rtmps.port}</value>
            </list>
        </property>
        <property name="ioThreads" value="${rtmp.io_threads}" />
        <property name="tcpNoDelay" value="${rtmp.tcp_nodelay}" />
    </bean>

Uncomment-out the rtmpsMinaIoHandler and rtmpsTransport beans. Any properties that you may want to change are once again located in the red5.properties file in the RTMPS labeled section. RTMPS will be available on 8443 if you are using the defaults. To use RTMPS in your flash client, just ensure that you supply the port in the uri like so:

  nc = new NetConnection();
  nc.objectEncoding = ObjectEncoding.AMF3;
  nc.client = this;
  nc.proxyType = "best";
  nc.addEventListener(NetStatusEvent.NET_STATUS, nc.client.onStatus);
  var uri:String = "rtmps://ssl.example.com:8443/live";
  nc.connect(uri, null);

Setup RTMPS in your red5/conf/red5-core.xml. You may notice that some of the rtmp variables are used here, that is only for ease of setup; you could set them to whatever you prefer.

<bean id="rtmpsMinaIoHandler"
        class="org.red5.server.net.rtmps.RTMPSMinaIoHandler">
        <property name="handler" ref="rtmpHandler" />
        <property name="codecFactory" ref="rtmpCodecFactory" />
        <property name="rtmpConnManager" ref="rtmpMinaConnManager" />
        <property name="keyStorePassword" value="${rtmps.keystorepass}" />
        <property name="keystoreFile" value="conf/keystore" />
    </bean>
    <bean id="rtmpsTransport" class="org.red5.server.net.rtmp.RTMPMinaTransport" init-method="start" destroy-method="stop">
        <property name="ioHandler" ref="rtmpsMinaIoHandler" />
        <property name="connectors">
            <list>
                <bean class="java.net.InetSocketAddress">
                    <constructor-arg index="0" type="java.lang.String" value="${rtmps.host}" />
                    <constructor-arg index="1" type="int" value="${rtmps.port}" />
                </bean>
            </list>
        </property>
        <property name="receiveBufferSize" value="${rtmp.receive_buffer_size}" />
        <property name="sendBufferSize" value="${rtmp.send_buffer_size}" />
        <property name="eventThreadsCore" value="${rtmp.event_threads_core}" />
        <property name="eventThreadsMax" value="${rtmp.event_threads_max}" />
        <property name="eventThreadsQueue" value="${rtmp.event_threads_queue}" />
        <property name="eventThreadsKeepalive" value="${rtmp.event_threads_keepalive}" />
        <property name="jmxPollInterval" value="1000" />
        <property name="tcpNoDelay" value="${rtmp.tcp_nodelay}" />
    </bean>

Enabling all these beans / configs should provide your users with SSL secured connections, making the web just a little bit safer for us all.

The final step is to start or restart your Red5 Pro server.

Here's some more details on RTMPS and Red5 (open source).


Other Notes

DigiCert

If you have a certificate from DigiCert, then you will need to do the following:

Export the cert after their ssl information is put in the keystore:

$ keytool -export -alias tomcat -file red5pro.cer -keystore keystore.jks

Create the truststore.jks from the exported cert:

$ keytool -import -trustcacerts -alias tomcat -file red5pro.cer -keystore truststore.jks -storepass password -noprompt

Helpful SSL Commands

http://shib.kuleuven.be/docs/ssl_commands.shtml

Additional Information on Keystore/Truststore

https://pubs.vmware.com/continuent/tungsten-replicator-2.1/deployment-ssl-stores.html


Testing the Configuration

To perform a quick test of your SSL configuration, you can use SSLLabs free service by substituting your domain in the uri below:

https://www.ssllabs.com/ssltest/analyze.html?d=my.example.com

The OpenSSL client can also be used to test the configuration with this command:

$ openssl s_client -connect my.example.com:443