From 4111616d24ba84ef40c90461d8524c872e74ab03 Mon Sep 17 00:00:00 2001 From: Roger Hardiman Date: Tue, 10 Sep 2024 22:02:54 +0100 Subject: [PATCH] Add RTSPS Server support. Change program.cs in RtspCameraExample to enable. Tested with ffmpeg pulling in video via a rtsps:// URL --- RTSP/RTSPTcpTlsTransport.cs | 32 +++++++++++++++++++++++++++++--- RtspCameraExample/Program.cs | 26 ++++++++++++++++++++++++-- RtspCameraExample/RtspServer.cs | 28 +++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 6 deletions(-) diff --git a/RTSP/RTSPTcpTlsTransport.cs b/RTSP/RTSPTcpTlsTransport.cs index 1a60476..bba86c4 100644 --- a/RTSP/RTSPTcpTlsTransport.cs +++ b/RTSP/RTSPTcpTlsTransport.cs @@ -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 { @@ -11,9 +13,11 @@ namespace Rtsp public class RtspTcpTlsTransport : RtspTcpTransport { private readonly RemoteCertificateValidationCallback? _userCertificateValidationCallback; + private readonly bool _isServer = false; + private readonly String _pfxFile = ""; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class as a SSL/TLS Client /// /// The underlying TCP connection. /// The user certificate validation callback, if default should be used. @@ -23,7 +27,7 @@ public RtspTcpTlsTransport(TcpClient tcpConnection, RemoteCertificateValidationC } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class as a SSL/TLS Client. /// /// The RTSP uri to connect to. /// The user certificate validation callback, if default should be used. @@ -32,6 +36,19 @@ public RtspTcpTlsTransport(Uri uri, RemoteCertificateValidationCallback? userCer { } + /// + /// Initializes a new instance of the class as a SSL/TLS Server with PFX File + /// + /// The underlying TCP connection. + /// The filename of the PFX file for the TLS Server. + /// The user certificate validation callback, if default should be used. + public RtspTcpTlsTransport(TcpClient tcpConnection, String pfxFile, RemoteCertificateValidationCallback? userCertificateValidationCallback = null) + : this(tcpConnection, userCertificateValidationCallback) + { + _isServer = true; + _pfxFile = pfxFile; + } + /// /// Gets the stream of the transport. /// @@ -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; } } diff --git a/RtspCameraExample/Program.cs b/RtspCameraExample/Program.cs index 983f64f..9226134 100644 --- a/RtspCameraExample/Program.cs +++ b/RtspCameraExample/Program.cs @@ -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; @@ -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(); @@ -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 diff --git a/RtspCameraExample/RtspServer.cs b/RtspCameraExample/RtspServer.cs index b325a4d..b5b4588 100644 --- a/RtspCameraExample/RtspServer.cs +++ b/RtspCameraExample/RtspServer.cs @@ -50,6 +50,9 @@ public class RtspServer : IDisposable private readonly NetworkCredential credential; private readonly Authentication? auth; + private bool _useRTSPS = false; + private string _pfxFile = ""; + /// /// Initializes a new instance of the class. /// @@ -83,6 +86,24 @@ public RtspServer(int portNumber, string username, string password, ILoggerFacto _logger = loggerFactory.CreateLogger(); } + /// + /// Initializes a new instance of the class in RTSPS (TLS) Mode. + /// + /// A numero port. + /// username. + /// password. + /// pfxFile used for RTSPS TLS Server Certificate. + 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; + } + /// /// Starts the listen. /// @@ -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()); newListener.MessageReceived += RTSPMessageReceived;