-storeName
, certificateName
, -useMachineStore
-useDefaults
and -requireClientCerts
.
try { CSChannelServerCommandLine commandLine; if (commandLine.Parse()) { CCredentials credentials( CreateCredentials( commandLine.CertificateStoreName(), commandLine.CertificateName(), commandLine.UseMachineStore(), commandLine.UseDefaultsToLocateCertificate())); CSharedCriticalSectionFactory lockFactory( commandLine.NumberOfLocks(), CSharedCriticalSectionFactory::DefaultMultiplier, commandLine.SpinCount());
static CCredentials CreateCredentials( const _tstring &storeName, const _tstring &certificateName, const bool useMachineStore, const bool attemptDefaults) { PCCERT_CONTEXT pCertContext = GetCert(storeName, certificateName, useMachineStore); if (!pCertContext && attemptDefaults) { const _tstring computerName = GetComputerName(); if (computerName != certificateName || storeName != _T("MY")) { pCertContext = GetCert(storeName, computerName, false); if (!pCertContext) { pCertContext = GetCert(storeName, computerName, true); } } if (!pCertContext) { const _tstring userName = GetUserName(); if (userName != certificateName || storeName != _T("MY")) { pCertContext = GetCert(storeName, userName, false); if (!pCertContext) { pCertContext = GetCert(storeName, computerName, true); } } } } if (!pCertContext) { throw CException(_T("CreateCredentials()"), _T("No certificate available")); } CCredentials::Data data; data.cCreds = 1; data.paCred = &pCertContext; CCredentials credentials(data, SECPKG_CRED_INBOUND); if(pCertContext) { CertFreeCertificateContext(pCertContext); } return credentials; }
CSharedCriticalSectionFactory lockFactory( commandLine.NumberOfLocks(), CSharedCriticalSectionFactory::DefaultMultiplier, commandLine.SpinCount()); CIOPool ioPool( commandLine.NumberOfIOThreads(), commandLine.DisplayDebug()); ioPool.Start(); CSequencedStreamSocketAllocator socketAllocator( lockFactory, commandLine.SocketPoolSize(), commandLine.DisplayDebug()); CBufferAllocator bufferAllocator( commandLine.BufferSize(), commandLine.BufferPoolSize(), commandLine.DisplayDebug()); const CFullAddress address( commandLine.Server(), commandLine.Port()); const ListenBacklog listenBacklog = commandLine.ListenBacklog(); const unsigned long recvBufferSize = commandLine.RecvBufferSize(); const unsigned long sendBufferSize = commandLine.SendBufferSize(); const CStreamSocketConnectionManager::ZeroByteReadConfiguration zeroByteReadConfiguration = commandLine.GetZeroByteReadConfiguration(); CSocketServer server( credentials, commandLine.RequireClientCerts(), address, listenBacklog, ioPool, socketAllocator, bufferAllocator, recvBufferSize, sendBufferSize, zeroByteReadConfiguration);
virtual void OnSecureConnectionEstablished( JetByteTools::Socket::IStreamSocket &socket, const JetByteTools::Socket::IAddress &address); virtual void OnSecureSocketReleased( JetByteTools::Win32::IIndexedOpaqueUserData &userData); virtual void FatalError( JetByteTools::Socket::IStreamSocket &socket, const SECURITY_STATUS status); virtual bool Verify( JetByteTools::Socket::IStreamSocket &socket, const JetByteTools::Win32::_tstring &targetName, const CContext &context);
CSocketServer::CSocketServer( CCredentials &credentials, const bool verifyPeer, const IFullAddress &address, const ListenBacklog listenBacklog, IIOPool &pool, IAllocateSequencedStreamSockets &socketAllocator, IAllocateBuffers &bufferAllocator, const SocketBufferSize recvBufferSize, const SocketBufferSize sendBufferSize, const ZeroByteReadConfiguration zeroByteReadConfiguration) : JetByteTools::SChannel::CStreamSocketServer(credentials, verifyPeer, address, listenBacklog, *this, pool, socketAllocator, bufferAllocator, recvBufferSize, sendBufferSize, zeroByteReadConfiguration) { }
credentials
and verifyPeer</code flag through to the SSL enabled base class.
A major part of our design descisions for the SChannel Tools Library were to make sure that the integration of SSL into a server was painless and didn't affect the design of the server. In fact, if you use the correct constructors for the JetByteTools::SChannel::CStreamSocketServer class you use the same server for both SSL enabled and clear-text servers.
bool CSocketServer::Verify(
IStreamSocket &socket,
const _tstring &targetName,
const CContext &context)
{
bool ok = true;
Output(_T("CSocketServer::Verify() - do manual certificate verification here"));
CERT_CONTEXT *pCertContext = context.GetRemoteCertificate();
if (pCertContext)
{
Output(_T("Verify client certificate here"));
DisplayCertChain(pCertContext);
// Attempt to validate client certificate.
const SECURITY_STATUS status = VerifyClientCertificate(
pCertContext,
targetName);
if (status != SEC_E_OK)
{
// The client certificate did not validate correctly. At this
// point, we cannot tell if we are connecting to the correct
// client, or if we are connecting to a "man in the middle"
// attack server.
// It is therefore best if we abort the connection.
Output(_T("Error ") + GetLastErrorMessage(status));
ok = false;
}
::CertFreeCertificateContext(pCertContext);
}
else
{
Output(_T("Client has no certificate"));
if (RequiresPeerVerification(socket))
{
Output(_T("Client certificate is required"));
ok = false;
}
}
return ok;
}
The certificate validation code is straight forward SChannel code and should be adjusted to validate the client certificate in the most appropriate way for your usage requirements.