Use secure RTP in Wowza Streaming Engine

Wowza Streaming Engine™ media server software supports Secure Real-time Transport Protocol (secure RTP or SRTP) for publishing and playback. The encryption keys can be passed either in the SDP data (RTSP SDP exchange or SDP file) or through the Wowza Streaming Engine Java API. The open-source tools FFmpeg and FFplay can be used for SRTP testing.

Notes:
  • Wowza Streaming Engine™ 4.3.0 or later is required.
     
  • Advanced knowledge of Java and cryptography is required to complete these tasks.

SRTP for publishing


FFmpeg can be used to publish an SRTP stream to Wowza Streaming Engine. In this case, the crypto keys are passed in the SDP data. The following is an example command line (using FFmpeg) for re-streaming the video track of sample.mp4 as a live stream:

ffmpeg -re -i sample.mp4 -an -vcodec copy -f rtp -srtp_out_suite AES_CM_128_HMAC_SHA1_80 -srtp_out_params "NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj" "srtp://localhost:10000"

You'll see the SDP information in the tool's console output. It should look something like this:

v=0
o=- 0 0 IN IP4 127.0.0.1
s=Big Buck Bunny, Sunflower version
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 56.26.101
m=video 10000 RTP/AVP 96
b=AS:640
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z0LAFdoCAJbARAAAAwAEAAADAPA8WLqA,aM4yyA==; profile-level-id=42C015
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj

Copy this information and paste into an SDP (.sdp) file, and then place the file in the Wowza Streaming Engine content folder ([install-dir]/content). Then use Wowza Streaming Engine Manager to start this SDP file. You should see the stream being properly published to Wowza Streaming Engine. In this case, the crypto information is in the SDP data. The following line includes the crypto key used for decryption:

a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj

Note: FFmpeg supports only one stream with RTP/SDP. To include audio, you must connect to the audio source to generate the audio SDP information and merge it into the .sdp file with the video information, and then use the merged SDP file for playback.

Other use cases might demand this data to be handled outside the SDP data. In this case, there's a Java API for intercepting incoming RTP sessions and injecting the crypto information. The following is an example of a module that injects this same key information through the Java API:

import java.security.*;
import java.util.*;

import com.wowza.util.*;
import com.wowza.wms.module.*;
import com.wowza.wms.rtp.model.*;
import com.wowza.wms.util.*;

public class ModuleSRTPPlaybackTest extends ModuleBase
{
  private MyRTPEncryptionProvider encryptionProvider = new MyRTPEncryptionProvider();
  private Random rnd = new SecureRandom();

  class MyRTPEncryptionProvider implements IRTPEncryptionProvider
  {
    public void onTrack(RTPSession rtpSession, RTPTrack rtpTrack)
    {
      while(true)
      {
        RTPStream rtpStream = rtpSession.getRTSPStream();
        if (rtpStream == null)
        {
          getLogger().warn("ModuleSRTPPlaybackTest.onRTPSessionCreate: No RTPStream");
          break;
        }

        byte[] keyBytes = new byte[SRTPUtils.KEY_CIPHER_BITLEN/8];
        byte[] saltBytes = new byte[SRTPUtils.KEY_SALT_BITLEN/8];

        byte[] cryptoBytes = Base64.decode("NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj");

        System.arraycopy(cryptoBytes, 0, keyBytes, 0, keyBytes.length);
        System.arraycopy(cryptoBytes, keyBytes.length, saltBytes, 0, saltBytes.length);

        int mkiLen = 0;
        long mki = SRTPKeyContext.MKI_NONE;
        int cryptoType = SRTPUtils.SRTP_AES128_CM_HMAC_SHA1_80_ID;
        int keyDerivationRate = 0;

        int encMethod = SRTPUtils.cryptoTypeToEncMethod(cryptoType);
        int authMethod = SRTPUtils.cryptoTypeToAuthMethod(cryptoType);
        int authLen = SRTPUtils.cryptoTypeToAuthLen(cryptoType);

        SRTPKeyContext srtpKeyContext = new SRTPKeyContext(encMethod, authMethod, authLen);
        SRTPMasterKey srtpMasterKey = new SRTPMasterKey(keyBytes, saltBytes);

        srtpKeyContext.setMKILen(mkiLen);
        srtpKeyContext.putMasterKey(mki, srtpMasterKey);
        srtpKeyContext.setKeyDerivationRate(keyDerivationRate);

        getLogger().warn("ModuleSRTPPlaybackTest.onRTPSessionCreate: Set encryption info ["+rtpTrack.getTrackId()+"]: keyBytes:"+BufferUtils.encodeHexString(keyBytes)+" saltBytes:"+BufferUtils.encodeHexString(saltBytes));

        rtpTrack.setSRTPKeyContextIn(srtpKeyContext);
        break;
      }
    }
  }

  public void onRTPSessionCreate(RTPSession rtpSession)
  {
    getLogger().info("ModuleSRTPPlaybackTest.onRTPSessionCreate");

    rtpSession.setEncryptionProvider(encryptionProvider);

  }
}

SRTP for playback


For playback, the crypto keys must be provided through the Wowza Streaming Engine Java API. The following is an example of a module that sets random key data for all RTP sessions:

import java.security.*;
import java.util.*;

import com.wowza.util.*;
import com.wowza.wms.module.*;
import com.wowza.wms.rtp.model.*;
import com.wowza.wms.util.*;

public class ModuleSRTPPlaybackTest extends ModuleBase
{
  private MyRTPEncryptionProvider encryptionProvider = new MyRTPEncryptionProvider();
  private Random rnd = new SecureRandom();

  class MyRTPEncryptionProvider implements IRTPEncryptionProvider
  {
    public void onTrack(RTPSession rtpSession, RTPTrack rtpTrack)
    {
      while(true)
      {
        RTPStream rtpStream = rtpSession.getRTSPStream();
        if (rtpStream == null)
        {
          getLogger().warn("ModuleSRTPPlaybackTest.onRTPSessionCreate: No RTPStream");
          break;
        }

        byte[] keyBytes = new byte[SRTPUtils.KEY_CIPHER_BITLEN/8];
        byte[] saltBytes = new byte[SRTPUtils.KEY_SALT_BITLEN/8];

        rnd.nextBytes(keyBytes);
        rnd.nextBytes(saltBytes);

        int mkiLen = 0;
        long mki = SRTPKeyContext.MKI_NONE;
        int cryptoType = SRTPUtils.SRTP_AES128_CM_HMAC_SHA1_80_ID;
        int keyDerivationRate = 0;

        int encMethod = SRTPUtils.cryptoTypeToEncMethod(cryptoType);
        int authMethod = SRTPUtils.cryptoTypeToAuthMethod(cryptoType);
        int authLen = SRTPUtils.cryptoTypeToAuthLen(cryptoType);

        //System.out.println("cryptoType: "+cryptoType);
        //System.out.println("keyBytes: "+BufferUtils.encodeHexString(keyBytes));
        //System.out.println("saltBytes: "+BufferUtils.encodeHexString(saltBytes));
        //System.out.println("encMethod: "+encMethod);
        //System.out.println("authMethod: "+authMethod);
        //System.out.println("authLen: "+authLen);

        SRTPKeyContext srtpKeyContext = new SRTPKeyContext(encMethod, authMethod, authLen);
        SRTPMasterKey srtpMasterKey = new SRTPMasterKey(keyBytes, saltBytes);

        srtpKeyContext.setMKILen(mkiLen);
        srtpKeyContext.putMasterKey(mki, srtpMasterKey);
        srtpKeyContext.setKeyDerivationRate(keyDerivationRate);

        getLogger().warn("ModuleSRTPPlaybackTest.onRTPSessionCreate: Set encryption info ["+rtpTrack.getTrackId()+"]: keyBytes:"+BufferUtils.encodeHexString(keyBytes)+" saltBytes:"+BufferUtils.encodeHexString(saltBytes));

        rtpTrack.setSRTPKeyContextOut(srtpKeyContext);
        break;
      }
    }
  }

  public void onRTPSessionCreate(RTPSession rtpSession)
  {
    getLogger().info("ModuleSRTPPlaybackTest.onRTPSessionCreate");

    rtpSession.setEncryptionProvider(encryptionProvider);

  }
}

In this case, the crypto information will be presented in the RTSP SDP exchange or SDP file data. Use FFplay to test playback.