Skip to content

Commit

Permalink
Add RTSPS Server support. Change program.cs in RtspCameraExample to e…
Browse files Browse the repository at this point in the history
…nable.

Tested with ffmpeg pulling in video via a rtsps:// URL
  • Loading branch information
RogerHardiman committed Sep 10, 2024
1 parent 73c92c2 commit 4111616
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 6 deletions.
32 changes: 29 additions & 3 deletions RTSP/RTSPTcpTlsTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

namespace Rtsp
{
Expand All @@ -11,9 +13,11 @@ namespace Rtsp
public class RtspTcpTlsTransport : RtspTcpTransport
{
private readonly RemoteCertificateValidationCallback? _userCertificateValidationCallback;
private readonly bool _isServer = false;
private readonly String _pfxFile = "";

/// <summary>
/// Initializes a new instance of the <see cref="RtspTcpTlsTransport"/> class.
/// Initializes a new instance of the <see cref="RtspTcpTlsTransport"/> class as a SSL/TLS Client
/// </summary>
/// <param name="tcpConnection">The underlying TCP connection.</param>
/// <param name="userCertificateValidationCallback">The user certificate validation callback, <see langword="null"/> if default should be used.</param>
Expand All @@ -23,7 +27,7 @@ public RtspTcpTlsTransport(TcpClient tcpConnection, RemoteCertificateValidationC
}

/// <summary>
/// Initializes a new instance of the <see cref="RtspTcpTransport"/> class.
/// Initializes a new instance of the <see cref="RtspTcpTransport"/> class as a SSL/TLS Client.
/// </summary>
/// <param name="uri">The RTSP uri to connect to.</param>
/// <param name="userCertificateValidationCallback">The user certificate validation callback, <see langword="null"/> if default should be used.</param>
Expand All @@ -32,6 +36,19 @@ public RtspTcpTlsTransport(Uri uri, RemoteCertificateValidationCallback? userCer
{
}

/// <summary>
/// Initializes a new instance of the <see cref="RtspTcpTlsTransport"/> class as a SSL/TLS Server with PFX File
/// </summary>
/// <param name="tcpConnection">The underlying TCP connection.</param>
/// <param name="pfxFile">The filename of the PFX file for the TLS Server.</param>
/// <param name="userCertificateValidationCallback">The user certificate validation callback, <see langword="null"/> if default should be used.</param>
public RtspTcpTlsTransport(TcpClient tcpConnection, String pfxFile, RemoteCertificateValidationCallback? userCertificateValidationCallback = null)
: this(tcpConnection, userCertificateValidationCallback)
{
_isServer = true;
_pfxFile = pfxFile;
}

/// <summary>
/// Gets the stream of the transport.
/// </summary>
Expand All @@ -40,7 +57,16 @@ public override Stream GetStream()
{
var sslStream = new SslStream(base.GetStream(), leaveInnerStreamOpen: true, _userCertificateValidationCallback);

sslStream.AuthenticateAsClient(RemoteAddress);
// Use presence of PFX file to select if this is the SSL/TLS Server or the SSL/TLS Client
if (_isServer)
{
var certificate = new X509Certificate2(_pfxFile);
sslStream.AuthenticateAsServer(certificate, false, SslProtocols.Tls12, false);
}
else
{
sslStream.AuthenticateAsClient(RemoteAddress);
}
return sslStream;
}
}
Expand Down
26 changes: 24 additions & 2 deletions RtspCameraExample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,22 @@ class Demo
private readonly int port = 8554;
private readonly string username = "user"; // or use NUL if there is no username
private readonly string password = "password"; // or use NUL if there is no password
private readonly bool useRTSPS = false; // Set True if you want to accept RTSPS connections
private readonly string pfxFile = "c:\\tls_certificate\\server.pfx"; // PFX file used by RTSPS Server

// You can make a Self Signed PFX Certificate file with these steps
// 1) Run "mkdir c:\tls_certificate"
// "cd c:\tls_certificate"

// 2) Run "\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\MakeCert.exe" -r -pe -n "CN=192.168.26.94" -sky exchange -sv server.pvk server.cer
// NOTES 1) The CN wants to be the IP address or Hostname of the RTSPS server. This is only a basic example. A proper certificate would use SubjectAltNames
// 2) When MAkeCert it asks for the PASSWORD in the Pop-up windows, click on the 'NONE' button.
// (would be better to have a password on the certificate, but I could not make it work)

// 3) Run "\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\pvk2pfx.exe" -pvk server.pvk -spc server.cer -pfx server.pfx

// 4) You now have a file called server.pfx which can be used by this application


private readonly int width = 192;
private readonly int height = 128;
Expand All @@ -54,7 +70,10 @@ public Demo(ILoggerFactory loggerFactory)
/////////////////////////////////////////
// Step 1 - Start the RTSP Server
/////////////////////////////////////////
rtspServer = new RtspServer(port, username, password, loggerFactory);
if (!useRTSPS)
rtspServer = new RtspServer(port, username, password, loggerFactory);
else
rtspServer = new RtspServer(port, username, password, pfxFile, loggerFactory); // rtsps:// needs a PFX File
try
{
rtspServer.StartListen();
Expand All @@ -65,7 +84,10 @@ public Demo(ILoggerFactory loggerFactory)
throw;
}

Console.WriteLine($"RTSP URL is rtsp://{username}:{password}@hostname:{port}");
if (!useRTSPS)
Console.WriteLine($"RTSP URL is rtsp://{username}:{password}@hostname:{port}");
else
Console.WriteLine($"Encrypted RTSP URL is rtsps://{username}:{password}@hostname:{port}");

/////////////////////////////////////////
// Step 2 - Create the H264 Encoder. It will feed NALs into the RTSP server
Expand Down
28 changes: 27 additions & 1 deletion RtspCameraExample/RtspServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public class RtspServer : IDisposable
private readonly NetworkCredential credential;
private readonly Authentication? auth;

private bool _useRTSPS = false;
private string _pfxFile = "";

/// <summary>
/// Initializes a new instance of the <see cref="RTSPServer"/> class.
/// </summary>
Expand Down Expand Up @@ -83,6 +86,24 @@ public RtspServer(int portNumber, string username, string password, ILoggerFacto
_logger = loggerFactory.CreateLogger<RtspServer>();
}

/// <summary>
/// Initializes a new instance of the <see cref="RTSPServer"/> class in RTSPS (TLS) Mode.
/// </summary>
/// <param name="portNumber">A numero port.</param>
/// <param name="username">username.</param>
/// <param name="password">password.</param>
/// <param name="pfxFile">pfxFile used for RTSPS TLS Server Certificate.</param>
public RtspServer(int portNumber, string username, string password, string pfxFile, ILoggerFactory loggerFactory)
: this(portNumber, username, password, loggerFactory)
{
if (String.IsNullOrEmpty(pfxFile))
{
throw new ArgumentOutOfRangeException("PFX File must not be null or empty for RTSPS mode");
}
_useRTSPS = true;
_pfxFile = pfxFile;
}

/// <summary>
/// Starts the listen.
/// </summary>
Expand All @@ -109,7 +130,12 @@ private void AcceptConnection()
_logger.LogDebug("Connection from {remoteEndPoint}", oneClient.Client.RemoteEndPoint);

// Hand the incoming TCP connection over to the RTSP classes
var rtsp_socket = new RtspTcpTransport(oneClient);
IRtspTransport rtsp_socket;
if (!_useRTSPS)
rtsp_socket = new RtspTcpTransport(oneClient);
else
rtsp_socket = new RtspTcpTlsTransport(oneClient, _pfxFile); // NOTE - we can add a callback where we can validate the TLS Certificates here

RtspListener newListener = new(rtsp_socket, _loggerFactory.CreateLogger<RtspListener>());
newListener.MessageReceived += RTSPMessageReceived;

Expand Down

0 comments on commit 4111616

Please sign in to comment.