Samples that use MS SSPI SSL:
Samples that use OpenSSL:
Tested on Windows 2000.
Contents:
SSPI Overview and Steps
SSPI stands for Security Support Provider Interface. It is an abstraction layer over the security services provided by windows. SSL/TLS itself is implemented in Secure Channel security provider and SSPI abstracts it for us. SSPI works by taking and returning data blobs to be sent to remote party. This way it allows us most flexibility in choosing what protocol (for ex., tcp/ip) to use and what to do with encrypted/decrypted data. One thing to be aware of is making sure the data is a stream and nothing is out of sequence. SSPI provides a number of built-in protocols Kerberos, NTLM, SSL/TLS. There is almost exactly the same sequence of function calls for either of them. In SSL/TLS case there is a little more to be done.
Client Side:
- Open Certificate store. Choose what certificate to use. Call
AcquireCredentialsHandle
.
- When you need to connect. Call
InitializeSecurityContext
. Send out data blob that is returned.
- Go into handshake loop with remote party by calling
InitializeSecurityContext
passing in received data blobs and sending out returned data blobs until success is returned.
- When need to send data out, call
EncryptMessage
and send returned encrypted data blob to remote party.
- When need to decrypt received data, call
DecryptMessage
and process decrypted data blob
- When done, call
ApplyControlToken
with SCHANNEL_SHUTDOWN
. Call InitializeSecurityContext
and send out returned data blob.
Server Side:
- Open Certificate store. Choose what certificate to use. Call
AcquireCredentialsHandle
.
- When you receive new connection from client, call
AcceptSecurityContext
. Send out data blob that is returned.
- Go into handshake loop with remote party by calling
AcceptSecurityContext
passing in received data blobs and sending out returned data blobs until success is returned.
- When need to send data out, call
EncryptMessage
and send returned encrypted data blob to remote party.
- When need to decrypt received data, call
DecryptMessage
and process decrypted data blob
- When done, call
ApplyControlToken
with SCHANNEL_SHUTDOWN
. Call AcceptSecurityContext
and send out returned data blob.
These basically are the steps that have to be taken to use SSL/TLS with SSPI. There are some details that need to be taken care of in the code that is not listed above. Such as respond appropriately to any error codes returned by SSPI functions. For example, renegotiation, disconnection, having read incomplete message/too much data (that is one complete message and extra data from next message).
OpenSSL
OpenSSL is an open source project for commercial-grade, full featured implementation of SSL/TLS. You can read more about it at: ttp://www.openssl.org/ (Documentation at: http://www.openssl.org/docs, Source at: http://www.openssl.org/source)
First let's compile the OpenSSL library. Download the latest distribution for example openssl-0.9.7b.tar.gz
- Extract the files. Read
INSTALL.W32
file in the OpenSSL root directory.
- Open "
Visual Studio .NET Command Prompt
" window.
- Go into openssl-0.9.7b directory.
- Type "
perl Configure VC-WIN32
" in the OpenSSL root directory. (Install ActivePerl
for windows if you don't have it)
- Type "
ms\do_ms
" in the OpenSSL root directory.
- Now type "
nmake -f ms\nt.mak
" (This is for static version of OpenSSL library)
Everything should build nicely and .libs should be outputed into openssl-0.9.7b\out32
directory.
- Set this directory in your "
VC++ Directories
" option for the "Library Files
".
- Set
openssl-0.9.7b\inc32\
directory in your "VC++ Directories
" option for the "Include Files
".
- Rename the generated libraries to
libeay32r.lib
and ssleay32r.lib
for Release mode. Build separate one
- for Debug mode with /MDd. They are NOT interchangable.
At this point the Managed C++ project should compile without problems.
- Read the OpenSSL FAQ on how to create certificates with openssl.exe or you can search google for more information and examples. For example if you're using MS CA you can download CA certificate from http://131.107.152.153/ save it to CA.cer file and use it in the sample code for CA file parameter.
Now let's issue a certificate request to this CA.
- First generate the key as in the above FAQ: "
openssl genrsa -des3 -out privkey.pem 2048
"
- Now generate the request "
openssl req -new -key privkey.pem -out cert.csr
"
- Using the "
advanced certificate request
" form on the MS CA web site, choose "Submit a certificate request by using a base-64-encoded CMC or PKCS #10 file, or submit a renewal request by using a base-64-encoded PKCS #7 file." link. Tell it where to find the .csr file and select submit. Now download the generated certificate.
- Convert it to .pem "
openssl x509 -inform DER -in certnew.cer -out clientcert.pem
"
- Combine certificate and private key into one file. "
type clientcert.pem privkey.pem >client.pem
"
Now you're ready to use this client.pem for the client side. Do same steps for the server side. This is just an example.
Explore the options that suits you better.
So you need at least 2 things to get the sample project working with OpenSSL.
- Generate client/server certificate and key into .pem format
- Download CA certificate
There are other ways to get working certificates for testing purposes. You can explore them on your own.
How to get Certificates:
When you're ready to deploy your app you can order a real/verifiable certificate from http://www.verisign.com/ or http://www.thawte.com/
However if you just want to play/test with SSL you have at least 3 other options.
- Best option is: Microsoft's free test Certificate Authority Server located at: http://131.107.152.153/
Just click on Request a certificate. Web Browser Certificate. Use advanced Certificate form. Fill out the fields. Generate both Client and Server Authentication Certificates. Leave the defaults except for Key size, change it to bigger one if you want. You can also generate SSL test certificate for your IIS server there if you want.
- You can explorer the options offered by OpenSSL. Check this FAQ for more info:
http://www.openssl.org/docs/HOWTO/certificates.txt
- Another option is using the built-in CA of windows 2000 Server edition. You can find step by step example in ATL7 SecureSOAP Sample's readme that comes with VS.NET MSDN.
- Yet another option is to use makecert.exe that comes with .NET SDK/PSDK. New version that comes with current PSDK supports exportable private keys (-pe option).
To find out the Certificate's hash/thumbprint open the .cer file in explorer. Go to Details | Field | Thumbprint. Select it. Then just copy paste the byte values into your code.
Viewing Installed Certificates:
To View your current certificates on NT: Run mmc.exe. Go to Console | Add/Remove Snap-in | Add | Certificates. Your personal or "MY" store will be under Personal | Certificates tree items.
Client class:
class SSLConnection
{
public:
void InitiateHandShake(String* ipAddress, Byte thumbPrint[],
Common::Misc::SecurityProviderProtocol prot,
Object* state);
void EncryptSend(Byte data[], int ActualLen, Object* state);
void DecryptData(Byte data[], Int32 ActualSize, Object* state);
bool Disconnect(Object* state);
void Dispose();
void LoadNewClientCredentials(Byte sha1hash[]);
public:
__property int get_MaxDataChunkSize();
__property int get_MaxInitialChunkSize();
public:
WriteSSL* DoWrite;
PlainData* DoPlainData;
NewCertificate* DoRenegotiate;
VerifyServCert* DoServerCertVerify;
HandShakeSuccess* DoHandShakeSuccess;
...
};
Quick Example:
Socket sock = ...
SSLConnection conn = new SSLConnection();
conn.DoWrite = new SSL.Client.WriteSSL(Send);
conn.DoPlainData = new SSL.Client.PlainData(OnPlainData);
conn.DoRenegotiate = new SSL.Client.NewCertificate(Renegotiate);
conn.DoServerCertVerify = new SSL.Client.VerifyServCert(ServerCertVerify);
conn.DoHandShakeSuccess = new SSL.Client.HandShakeSuccess(HandShakeSuccess);
sock.Connect(....);
conn.InitiateHandShake(url, null,
SSL.Common.Misc.SecurityProviderProtocol.PROT_TLS1,
Guid.Empty);
while(!connected)
{
}
string Request = "GET / HTTP/1.1\r\n" + "Host: localhost\r\n\r\n";
byte[] data = ASCIIEncoding.ASCII.GetBytes(Request);
conn.EncryptSend(data, data.Length, null);
conn.Disconnect(null);
conn.Dispose();
bool Send(byte[] data, object state)
{
sock.Send(data, 0, data.Length, SocketFlags.None);
}
void OnPlainData(Byte[] data, object state)
{
}
void Renegotiate(SSL.Client.SSLConnection conn)
{
byte[] ThumbPrint = new Byte[] {0xBE,0xD7,0x44,0xF4,0x57,
0x54,0xF2,0x08,0x7F,0x06,
0x03,0x0B,0x01,0x33,0xE5,
0x60,0x78,0x3D,0xAF,0x35};
conn.LoadNewClientCredentials(ThumbPrint);
}
void HandShakeSuccess(){connected = true;}
void ServerCertVerify(SSL.Common.Misc.CeriticateInfo ServCertInfo)
{
X509Certificate cert = new X509Certificate(ServCertInfo.CertData);
}
Server class:
class SSLServer
{
public:
void DisconnectFromClient(Guid ClientID, Object* state);
void Dispose();
void EncryptSend(Byte data[], int ActualLen, Guid ClientID,
Object* state);
void DecryptData(Byte data[], Int32 ActualLen, Guid ClientID,
Object* state);
void RemoveClient(Guid ClientID);
int MaxDataChunkSize(Guid ClientID);
void SetupCredentials(Byte certThumbPrint[],
Common::Misc::SecurityProviderProtocol prot);
void AskForRenegotiate(Guid ClientID, Object* state);
public:
__property int get_MaxInitialChunkSize();
__property void set_AskClientForAuth(bool value);
public:
WriteSSL* DoWrite;
PlainData* DoPlainData;
VerifyClientCert* DoClientCertVerify;
HandShakeSuccess* DoHandShakeSuccess;
private:
...
};
Quick Example:
SSLServer SSL = new SSLServer();
SSL.DoPlainData = new SSL.Server.PlainData(OnPlainData);
SSL.DoWrite = new SSL.Server.WriteSSL(OnSend);
SSL.DoClientCertVerify
= new SSL.Server.VerifyClientCert(OnClientCertInfo);
SSL.DoHandShakeSuccess
= new SSL.Server.HandShakeSuccess(OnClientHandShakeSuccess);
SSL.AskClientForAuth = false;
byte[] ServerCertThumbPrint = new byte[]{0xA4, 0x01, 0xC2, 0xB1, 0x73,
0xD9, 0xD9, 0xF4, 0x77, 0x68,
0x60, 0xE5, 0xAD, 0x25, 0x34,
0xEE, 0x59, 0xEB, 0x0E, 0x8D};
SSL.SetupCredentials(ServerCertThumbPrint,
SSL.Common.Misc.SecurityProviderProtocol.PROT_TLS1);
Guid clientID = Guid.NewGuid();
SSL.DecryptData(buff, bytesRead, clientID, null);
SSL.DisconnectFromClient(clientID);
SSL.Dispose();
void OnPlainData(byte[] data, Guid ClientID, object o)
{
}
bool OnSend(byte[] data, Guid ClientID, object o)
{
}
void OnClientCertInfo(SSL.Common.Misc.CeriticateInfo clientCert)
{
X509Certificate cert = new X509Certificate(clientCert.CertData);
}
void OnClientHandShakeSuccess(Guid ClientID)
{
}
SSL Tunnel Sample:
NOTE: Never Never try this with your real/production SQL server. Try AT YOUR OWN RISK with your test installation of db or something else that goes through known TCP/IP connection. Also, go through code see what it does, make sure it suits your specific purposes.
To use it simply build the client and server .exes. Make sure the .config file is where the .exe is. You can test how it work by securing MS SQL connection with SSL/TLS. Here are the steps to do it: Make sure you installed your own certificate and updated the hash/thumbprints in code.
Open the MS SQL Client Network utility. Go to Alias | Add, then set the options as below:
- Server Alias: SSL
- Network Libraries: TCP/IP
- Server Name: Your Client.exe's IP (for ex., "localhost")
- Port Number: Your Client's listening Port (that's the "PortToListenForRedirect" value in Client.exe.config)
In Client.exe.config "SSLDestIP" and "SSLDestPort" will be your Server.exe's IP, and port that it listens on for redirect.
In Server.exe.config "PortToListenForSSL" is port Server.exe listens on for Client.exe, and "DestIP" "DestPort" are MS SQL Servers real IP and Port where everything from client will be sent. So the Cnnection in between client and exe will be secure. Client.exe will run on local machine for example and Server.exe will run on machine closer to SQL Server.
Run the SSL Client/Server applications.
Open the MS SQL Enterprise Manager console. Choose "New SQL Server registration". For server name put SSL, fill in the login info. Click OK. Now, you should be connected through SSL to your SQL Server.
References:
MS SChannel related:
Disclaimer: THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.