HOWTO Use SslStream and NegotiateStream in same TcpListener.

  • Thread starter Thread starter J-S-B
  • Start date Start date
J

J-S-B

Guest
Please help,

I am trying to add NTLM to my web server app, which is based off TcpListener, to have both SSL and NTLM.
I've succeded with adding SSL, but now I need NTLM, and sadly, there is no documentation on how to have both.
Furthermore, I've not found a clean sample on even how to apply the NTLM, even if you do not have SSL.

To keep the issue in focus, below I have extracted code from my server, which works greate with and without SSL. But it is missing the NTLM, and I do not know what to do to add it in.

Please help me get the answer to the code at the 2 spots commented with: // What do we do here???




using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;

namespace MyNameSpace
{
/*----------------------------------------------------------------------------------------*\
| |
\*----------------------------------------------------------------------------------------*/

static class Program
{
/*----------------------------------------------------------------------------------------*\
| |
\*----------------------------------------------------------------------------------------*/

static void Main()
{
string csHostName = Dns.GetHostName().Trim();
byte[] zbCertificate;
zbCertificate = File.ReadAllBytes("Path to some SSL Certificate");
SslCertificate srcCertificate = new SslCertificate
{
Type = SslCertificate.CertificateTypes.X509Certificate2,
Data = new X509Certificate2
(
zbCertificate, "Encryption String to work with SSL Certificate"
),
ClientCertificateRequired = false,
CheckCertificateRevocation = false
};

HttpServer HttpServer_MyServer = new HttpServer
{
HostName = HostName,
Port = 50000,
Certificate = srcCertificate,
ReuseAddress = bReuseAddress
};
HttpServer_MyServer.RunListener();
}
}

/*----------------------------------------------------------------------------------------*\
| |
\*----------------------------------------------------------------------------------------*/

public class HttpServer
{
public string HostName;
public int Port = 50000;
public TcpListener TcpListener_This;
public bool ReuseAddress { get; set; } = false;
public SslCertificate Certificate { get; set; } = null;

/*----------------------------------------------------------------------------------------*\
| |
\*----------------------------------------------------------------------------------------*/

public void RunListener()
{
IPAddress ip = Dns.GetHostAddresses(HostName).Where
(
address => address.AddressFamily == AddressFamily.InterNetwork
).First();
TcpListener_This = new TcpListener(ip, (int) Port);
bool bIsActive = true;

while (bIsActive)
{
TcpClient TcpClient_This = TcpListener_This.AcceptTcpClient();
HttpProcessor HttpProcessor_This = new HttpProcessor(TcpClient_This, this);
Thread Thread_That = new Thread(new ThreadStart(HttpProcessor_This.Process));

if (!ThreadApartmentState.IsNull())
{
Thread_That.SetApartmentState((ApartmentState) ThreadApartmentState);
}

Thread_That.Start();
Thread.Sleep(1);
}
}
}

/*----------------------------------------------------------------------------------------*\
| |
\*----------------------------------------------------------------------------------------*/

public class HttpProcessor
{
public TcpClient Client { get; set; } = null;
public HttpServer Server { get; set; }
public Stream Stream_Input;
public SslStream SslStream_Input = null;
public BinaryWriter BinaryWriter_Output;

public HttpProcessor(TcpClient TcpClient_Param, HttpServer HttpServer_Param)
{
Client = TcpClient_Param;
Server = HttpServer_Param;
}

/*------------------------------------------------------------------------------------------*\
| |
\*------------------------------------------------------------------------------------------*/

public override void Process()
{
// Note, ultimately NTLM and its negotiation/handhake goes to functionality in SECUR32.dll.
// The SECUR32.dll function AcceptSecurityContext function in particular handles the
// process. This process is possibly wrapped by System.Net.Security.NegotiateStream, but
// below
//
// What do we do here, to get an System.Security.Principal.WindowsIdentity object, that
// we can use to impersonate the NTLM authenitcated user.

WindowsIdentity wiAuthenticatedUser = null;

if (Server.Certificate != null)
{
SslStream_Input = new SslStream(Client.GetStream());
SslStream_Input.AuthenticateAsServer
(
Certificate.X509Certificate,
Certificate.ClientCertificateRequired,
Certificate.SslProtocols,
Certificate.CheckCertificateRevocation
);
Stream_Input = new BufferedStream(SslStream_Input);
BinaryWriter_Output = new BinaryWriter(SslStream_Input);

// MISSING NTLM AUTHENTICATION WITH SSL....
//
// What do we do here???
// How do we get wiAuthenticatedUser populated...
//
// This point would do the NTLM handshake like above, but the trick here is that the SSL
// stream has had to kick in 1st, to deal with the SSL encryption.
//
// Note, this sample code works, up to this point for SSL.
//
// Something extra needs to happen here, to ensure the SSL Stream is worked-with, qand not broken.
}
else
{
Stream_Input = new BufferedStream(Client.GetStream());
BinaryWriter_Output = new BinaryWriter(new BufferedStream(Client.GetStream()));

// MISSING NTLM AUTHENTICATION WITH NO SSL....
//
// What do we do here???
// How do we get wiAuthenticatedUser populated...
//
// Note, NTLM has a handshake process, which will require returning 401, and waiting
// for the client to follow the negotiation handshake, with possibly, challenges from the
// server to the client.
}

// At this point, we have a Input and Output stream, we can use to get all HTTP parameters
// and any HTTP posted data, process as desired, and return HTTP response as desired.
//
// For this demo, we'll just return HTTP 200.

BinaryWriter_Output.Write("HTTP/1.0 200 OK\n");
BinaryWriter_Output.Write("Content-Type: " + content_type + "\n");
BinaryWriter_Output.Write("Connection: close\n");
BinaryWriter_Output.Write("\n");
}
}

/*----------------------------------------------------------------------------------------*\
| |
\*----------------------------------------------------------------------------------------*/

public class SslCertificate
{
/*----------------------------------------------------------------------------------------*\
| |
\*----------------------------------------------------------------------------------------*/

public enum CertificateTypes
{
[CSC.Names(new string[] { "X509Certificate" })]
X509Certificate,

[CSC.Names(new string[] { "X509Certificate2" })]
X509Certificate2
}

public CertificateTypes Type { get; set; }
public object Data { get; set; }
public bool ClientCertificateRequired { get; set; } = true;
public SslProtocols SslProtocols { get; set; } = SslProtocols.Default;
public bool CheckCertificateRevocation { get; set; } = true;
public X509Certificate X509Certificate
{
get
{
if (Data.IsNull()) return null;
X509Certificate xcerRET = null;
ML.IGNORE(() => xcerRET = (X509Certificate) Data);
return xcerRET;
}
}
}
}

Continue reading...
 
Back
Top