hi,
I am using the example code for SslTcpServer from the msdn with openssl. I took the following steps to set up things :
- Create a Private Key using the openssl command : openssl genrsa -out privkey.pem 2048
- Create a certificate using the open ssl command: openssl req -newkey rsa:1024 -nodes -x509 -keyout privkey.pem -out cacert.pem -days 365 -config openssl.cnf
- Convert the certificate to PKCS#12 format using openssl command (where I specify the certificate name as TESTCERT) : openssl pkcs12 -export -in cacert.crt -inkey privkey.key -out bundle.p12
- Launch MMC and import the bundle.p12 into the Personal store (With the "make keys exportable" selected and NO PASSWORD). And now I can see the TESTCERT in the Personal store.
- I then export the certificate with the private key to a .pfx file (although I never use it)
- Run the server (example code from msdn ) passing it the certificate name as parameter from command line (See the simple Server code below): SslTcpServer.exe bundle.p12 (The server starts successfully and waits for client connections) NOTE: Client authentication set to FALSE
- Run the client (example code from msdn ) on a different machine to connect to the SSL server (See simple client code below) from command line: SslTcpClient.exe TESTCERT
PROBLEM : When I try to connect to the server using the openssl client :
openssl s_client -connect 192.168.0.1:47935, it works fine. But when I try the Windows client (code below) it says :
>SslTcpClient.exe 192.168.0.1 TESTCERT
Client connected.
Certificate error: RemoteCertificateChainErrors
Exception: The remote certificate is invalid according to the validation procedure.
Authentication failed - closing the connection. SERVER CODE (from MSDN)
using System;<br/>
using System.Collections;<br/>
using System.Net;<br/>
using System.Net.Sockets;<br/>
using System.Net.Security;<br/>
using System.Security.Authentication;<br/>
using System.Text;<br/>
using System.Security.Cryptography.X509Certificates;<br/>
using System.IO;<br/>
<br/>
namespace Enable<br/>
{<br/>
public sealed class SslTcpServer<br/>
{<br/>
static X509Certificate serverCertificate = null;<br/>
// The certificate parameter specifies the name of the file <br/>
// containing the machine certificate.<br/>
public static void RunServer(string certificate)<br/>
{<br/>
serverCertificate = X509Certificate.CreateFromCertFile(certificate);<br/>
// Create a TCP/IP (IPv4) socket and listen for incoming connections.<br/>
TcpListener listener = new TcpListener(IPAddress.Any, 47935);<br/>
listener.Start();<br/>
while (true)<br/>
{<br/>
Console.WriteLine("Waiting for a client to connect...");<br/>
// Application blocks while waiting for an incoming connection.<br/>
// Type CNTL-C to terminate the server.<br/>
TcpClient client = listener.AcceptTcpClient();<br/>
ProcessClient(client);<br/>
}<br/>
}<br/>
static void ProcessClient(TcpClient client)<br/>
{<br/>
// A client has connected. Create the <br/>
// SslStream using the client's network stream.<br/>
SslStream sslStream = new SslStream(<br/>
client.GetStream(), false);<br/>
// Authenticate the server but don't require the client to authenticate.<br/>
try<br/>
{<br/>
sslStream.AuthenticateAsServer(serverCertificate,<br/>
false, SslProtocols.Tls, true);<br/>
// Display the properties and settings for the authenticated stream.<br/>
DisplaySecurityLevel(sslStream);<br/>
DisplaySecurityServices(sslStream);<br/>
DisplayCertificateInformation(sslStream);<br/>
DisplayStreamProperties(sslStream);<br/>
<br/>
// Set timeouts for the read and write to 5 seconds.<br/>
sslStream.ReadTimeout = 5000;<br/>
sslStream.WriteTimeout = 5000;<br/>
// Read a message from the client. <br/>
Console.WriteLine("Waiting for client message...");<br/>
<br/>
string messageData = string.Empty;<br/>
<br/>
try<br/>
{<br/>
messageData = ReadMessage(sslStream);<br/>
Console.WriteLine("Received: {0}", messageData);<br/>
}<br/>
catch { }<br/>
<br/>
<br/>
<br/>
// Write a message to the client.<br/>
byte[] message = Encoding.UTF8.GetBytes("Hello from the server.<EOF>");<br/>
Console.WriteLine("Sending hello message.");<br/>
sslStream.Write(message);<br/>
}<br/>
catch (AuthenticationException e)<br/>
{<br/>
Console.WriteLine("Exception: {0}", e.Message);<br/>
if (e.InnerException != null)<br/>
{<br/>
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);<br/>
}<br/>
Console.WriteLine("Authentication failed - closing the connection.");<br/>
sslStream.Close();<br/>
client.Close();<br/>
return;<br/>
}<br/>
finally<br/>
{<br/>
// The client stream will be closed with the sslStream<br/>
// because we specified this behavior when creating<br/>
// the sslStream.<br/>
sslStream.Close();<br/>
client.Close();<br/>
}<br/>
}<br/>
static string ReadMessage(SslStream sslStream)<br/>
{<br/>
// Read the message sent by the client.<br/>
// The client signals the end of the message using the<br/>
// "<EOF>" marker.<br/>
byte[] buffer = new byte[2048];<br/>
StringBuilder messageData = new StringBuilder();<br/>
int bytes = -1;<br/>
do<br/>
{<br/>
// Read the client's test message.<br/>
bytes = sslStream.Read(buffer, 0, buffer.Length);<br/>
<br/>
// Use Decoder class to convert from bytes to UTF8<br/>
// in case a character spans two buffers.<br/>
Decoder decoder = Encoding.UTF8.GetDecoder();<br/>
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];<br/>
decoder.GetChars(buffer, 0, bytes, chars, 0);<br/>
messageData.Append(chars);<br/>
// Check for EOF or an empty message.<br/>
if (messageData.ToString().IndexOf("<EOF>") != -1)<br/>
{<br/>
break;<br/>
}<br/>
} while (bytes != 0);<br/>
<br/>
return messageData.ToString();<br/>
}<br/>
static void DisplaySecurityLevel(SslStream stream)<br/>
{<br/>
Console.WriteLine("Cipher: {0} strength {1}", stream.CipherAlgorithm, stream.CipherStrength);<br/>
Console.WriteLine("Hash: {0} strength {1}", stream.HashAlgorithm, stream.HashStrength);<br/>
Console.WriteLine("Key exchange: {0} strength {1}", stream.KeyExchangeAlgorithm, stream.KeyExchangeStrength);<br/>
Console.WriteLine("Protocol: {0}", stream.SslProtocol);<br/>
}<br/>
static void DisplaySecurityServices(SslStream stream)<br/>
{<br/>
Console.WriteLine("Is authenticated: {0} as server? {1}", stream.IsAuthenticated, stream.IsServer);<br/>
Console.WriteLine("IsSigned: {0}", stream.IsSigned);<br/>
Console.WriteLine("Is Encrypted: {0}", stream.IsEncrypted);<br/>
}<br/>
static void DisplayStreamProperties(SslStream stream)<br/>
{<br/>
Console.WriteLine("Can read: {0}, write {1}", stream.CanRead, stream.CanWrite);<br/>
Console.WriteLine("Can timeout: {0}", stream.CanTimeout);<br/>
}<br/>
static void DisplayCertificateInformation(SslStream stream)<br/>
{<br/>
Console.WriteLine("Certificate revocation list checked: {0}", stream.CheckCertRevocationStatus);<br/>
<br/>
X509Certificate localCertificate = stream.LocalCertificate;<br/>
if (stream.LocalCertificate != null)<br/>
{<br/>
Console.WriteLine("Local cert was issued to {0} and is valid from {1} until {2}.",<br/>
localCertificate.Subject,<br/>
localCertificate.GetEffectiveDateString(),<br/>
localCertificate.GetExpirationDateString());<br/>
}<br/>
else<br/>
{<br/>
Console.WriteLine("Local certificate is null.");<br/>
}<br/>
// Display the properties of the client's certificate.<br/>
X509Certificate remoteCertificate = stream.RemoteCertificate;<br/>
if (stream.RemoteCertificate != null)<br/>
{<br/>
Console.WriteLine("Remote cert was issued to {0} and is valid from {1} until {2}.",<br/>
remoteCertificate.Subject,<br/>
remoteCertificate.GetEffectiveDateString(),<br/>
remoteCertificate.GetExpirationDateString());<br/>
}<br/>
else<br/>
{<br/>
Console.WriteLine("Remote certificate is null.");<br/>
}<br/>
}<br/>
private static void DisplayUsage()<br/>
{<br/>
Console.WriteLine("To start the server specify:");<br/>
Console.WriteLine("serverSync certificateFile.cer");<br/>
Environment.Exit(1);<br/>
}<br/>
public static void Main(string[] args)<br/>
{<br/>
string certificate = null;<br/>
if (args == null || args.Length < 1)<br/>
{<br/>
DisplayUsage();<br/>
}<br/>
certificate = args[0];<br/>
SslTcpServer.RunServer(certificate);<br/>
//return certificate;<br/>
}<br/>
}<br/>
}<br/>
CLIENT CODE (from MSDN)
using System;<br/>
using System.Collections;<br/>
using System.Net;<br/>
using System.Net.Security;<br/>
using System.Net.Sockets;<br/>
using System.Security.Authentication;<br/>
using System.Text;<br/>
using System.Security.Cryptography.X509Certificates;<br/>
using System.IO;<br/>
<br/>
namespace Enable<br/>
{<br/>
public class SslTcpClient<br/>
{<br/>
private static Hashtable certificateErrors = new Hashtable();<br/>
<br/>
// The following method is invoked by the RemoteCertificateValidationDelegate.<br/>
public static bool ValidateServerCertificate(<br/>
object sender,<br/>
X509Certificate certificate,<br/>
X509Chain chain,<br/>
SslPolicyErrors sslPolicyErrors)<br/>
{<br/>
if (sslPolicyErrors == SslPolicyErrors.None)<br/>
return true;<br/>
<br/>
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);<br/>
<br/>
// Do not allow this client to communicate with unauthenticated servers.<br/>
return false;<br/>
}<br/>
public static void RunClient(string machineName, string serverName)<br/>
{<br/>
// Create a TCP/IP client socket.<br/>
// machineName is the host running the server application.<br/>
TcpClient client = new TcpClient(machineName, 47935);<br/>
Console.WriteLine("Client connected.");<br/>
// Create an SSL stream that will close the client's stream.<br/>
SslStream sslStream = new SslStream(<br/>
client.GetStream(),<br/>
false,<br/>
new RemoteCertificateValidationCallback(ValidateServerCertificate),<br/>
null<br/>
);<br/>
// The server name must match the name on the server certificate.<br/>
try<br/>
{<br/>
sslStream.AuthenticateAsClient(serverName);<br/>
}<br/>
catch (AuthenticationException e)<br/>
{<br/>
Console.WriteLine("Exception: {0}", e.Message);<br/>
if (e.InnerException != null)<br/>
{<br/>
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);<br/>
}<br/>
Console.WriteLine("Authentication failed - closing the connection.");<br/>
client.Close();<br/>
return;<br/>
}<br/>
// Encode a test message into a byte array.<br/>
// Signal the end of the message using the "<EOF>".<br/>
byte[] messsage = Encoding.UTF8.GetBytes("Hello from the client.<EOF>");<br/>
// Send hello message to the server. <br/>
sslStream.Write(messsage);<br/>
sslStream.Flush();<br/>
// Read message from the server.<br/>
string serverMessage = ReadMessage(sslStream);<br/>
Console.WriteLine("Server says: {0}", serverMessage);<br/>
// Close the client connection.<br/>
client.Close();<br/>
Console.WriteLine("Client closed.");<br/>
}<br/>
static string ReadMessage(SslStream sslStream)<br/>
{<br/>
// Read the message sent by the server.<br/>
// The end of the message is signaled using the<br/>
// "<EOF>" marker.<br/>
byte[] buffer = new byte[2048];<br/>
StringBuilder messageData = new StringBuilder();<br/>
int bytes = -1;<br/>
do<br/>
{<br/>
bytes = sslStream.Read(buffer, 0, buffer.Length);<br/>
<br/>
// Use Decoder class to convert from bytes to UTF8<br/>
// in case a character spans two buffers.<br/>
Decoder decoder = Encoding.UTF8.GetDecoder();<br/>
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];<br/>
decoder.GetChars(buffer, 0, bytes, chars, 0);<br/>
messageData.Append(chars);<br/>
// Check for EOF.<br/>
if (messageData.ToString().IndexOf("<EOF>") != -1)<br/>
{<br/>
break;<br/>
}<br/>
} while (bytes != 0);<br/>
<br/>
return messageData.ToString();<br/>
}<br/>
private static void DisplayUsage()<br/>
{<br/>
Console.WriteLine("To start the client specify:");<br/>
Console.WriteLine("clientSync machineName [serverName]");<br/>
Environment.Exit(1);<br/>
}<br/>
public static int Main(string[] args)<br/>
{<br/>
string serverCertificateName = null;<br/>
string machineName = null;<br/>
<br/>
if (args == null || args.Length < 1)<br/>
{<br/>
DisplayUsage();<br/>
}<br/>
// User can specify the machine name and server name.<br/>
// Server name must match the name on the server's certificate. <br/>
machineName = args[0];<br/>
if (args.Length < 2)<br/>
{<br/>
serverCertificateName = machineName;<br/>
}<br/>
else<br/>
{<br/>
serverCertificateName = args[1];<br/>
}<br/>
SslTcpClient.RunClient(machineName, serverCertificateName);<br/>
return 0;<br/>
}<br/>
}<br/>
}<br/>
I am also testing it with the following C++ client (code copied from IBM site see below) and I get the following error :
>StandAloneTCPClient.exe
Client: socket() is OK.
Client: connect() is OK.
Client: gonna call SSL_set_fd
Client: SSL_set_fd called
Client: gonna call SSL_connect
SSL error #1 in accept,program terminated The C++ client code is :
#include <tpf/tpfeq.h>
#include <tpf/tpfio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stdafx.h"
// #include <sys/socket.h>
#include "Winsock2.h"
#include <errno.h>
#include <sys/types.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
/* define HOME to be dir for key and certificate files... */
#define HOME "/certs/"
/* Make these what you want for certificate & key files */
// DON“T USE DEFINES!!!!!!!!!!!!!!!!
// #define CERT_FILE HOME "C:\\some\\openssl\\dir\\mycert2.pem"
// #define KEY_FILE HOME "C:\\some\\openssl\\dir\\mycert2.pem"
/*Cipher list to be used*/
// #define CIPHER_LIST "AES128-SHA"
// AES128-SHA
// void QSSN(void)
void exampleSSL(void)
{
int err, count;
char buff[32];
/*SSL PART*/
SSL_METHOD *meth;
SSL_CTX *ctx;
// WSA initialize
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
/* SSL Part*/
SSL_library_init();
SSL_load_error_strings();
SSLeay_add_ssl_algorithms();
meth = SSLv23_client_method();
/*Create a new context block*/
ctx = SSL_CTX_new(meth);
if (!ctx)
{
printf("Error creating the context.\n");
exit(0);
}
/*Set cipher list*/
// #define CIPHER_LIST "AES128-SHA"
const char* cipherlist = "AES128-SHA";
if (SSL_CTX_set_cipher_list(ctx, cipherlist) <= 0)
{
printf("Error setting the cipher list.\n");
exit(0);
}
/* Set for server verification*/
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
/* Connect to the server, TCP/IP layer,*/
// SOCKET socketfd;
// socketfd = socket(AF_INET, SOCK_STREAM, 0);
SOCKET socketfd;
socketfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (socketfd == INVALID_SOCKET)
{
printf("Client: Error at socket(): %ld.\n", WSAGetLastError());
WSACleanup();
return;
}
else
printf("Client: socket() is OK.\n");
// sockaddr
sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr("192.168.0.201");
// inet_addr("127.0.0.1");
// inet_addr("87.194.161.236");
clientService.sin_port = htons(47935); // htons(55555);
// Connect to server.
err = connect(socketfd, (struct sockaddr*)&clientService, sizeof(clientService));
if (err < 0)
{
// printf("Socket returned error #%d,program terminated\n",sock_errno());
int error = WSAGetLastError();
printf("Socket returned error #%d,program terminated\n", error);
SSL_CTX_free(ctx);
exit(0);
}
else
{
printf("Client: connect() is OK.\n");
}
// And now the bloody SSL thing
// Create new ssl object
SSL* myssl;
myssl = (SSL*) SSL_new(ctx);
if(!myssl)
{
printf("Error creating SSL structure.\n");
exit(0);
}
// Bind the socket to the SSL structure
printf("Client: gonna call SSL_set_fd \n");
SSL_set_fd(myssl, socketfd);
printf("Client: SSL_set_fd called \n");
// ...
// Connect to the server, SSL layer.
printf("Client: gonna call SSL_connect \n");
err = SSL_connect(myssl);
// Check for error in connect.
if (err < 1) {
err = SSL_get_error(myssl, err);
printf("SSL error #%d in accept,program terminated\n", err);
if(err == 5)
{
// printf("sockerrno is:%d\n",sock_errno());
int error = WSAGetLastError();
printf("sockerrno is:%d\n", error);
}
// close(socketfd);
SSL_free(myssl);
SSL_CTX_free(ctx);
exit(0);
}
else
{
printf("Client: SSL connect() is OK.\n");
}
printf("Client: SSL_connect called \n");
/*Print out connection details*/
printf("SSL connection on socket %x,Version: %s, Cipher: %s\n",
socketfd,
SSL_get_version(myssl),
SSL_get_cipher(myssl));
/*Send message to the server.*/
err=SSL_write(myssl,"Hello there!!!!",sizeof("Hello there!!!!")+1);
/*Check for error in write.*/
if(err<1)
{
err=SSL_get_error(myssl,err);
printf("Error #%d in write,program terminated\n",err);
/********************************/
/* If err=6 it means the Server */
/* issued an SSL_shutdown. You */
/* must respond with a shutdown */
/* to complete a graceful */
/* shutdown */
/********************************/
if(err==6)
{
SSL_shutdown(myssl);
}
SSL_free(myssl);
// close(socketfd);
SSL_CTX_free(ctx);
exit(0);
}
/*Read servers response.*/
err = SSL_read (myssl, buff, sizeof(buff));
/*Check for error in read.*/
if (err < 1)
{
err=SSL_get_error(myssl,err);
printf("Error #%d in read,program terminated\n",err);
/********************************/
/* If err=6 it means the client */
/* issued an SSL_shutdown. You */
/* must respond with a shutdown */
/* to complete a graceful */
/* shutdown */
/********************************/
if(err==6)
{
SSL_shutdown(myssl);
}
SSL_free(myssl);
// close(socketfd);
SSL_CTX_free(ctx);
exit(0);
}
printf("Server said: %s\n",buff);
err=SSL_shutdown(myssl);
count = 1;
/***********************************/
/* Try SSL_shutdown() 5 times to */
/* wait for the remote application */
/* to issue SSL_shutdown(). */
/***********************************/
while(true)
{
}
err=SSL_shutdown(myssl);
// close(socketfd);
SSL_free(myssl);
SSL_CTX_free(ctx);
exit(0);
}