1. Initialize Windows Sockets
The following initialization code is found in the program's
main
function.
if(WSAStartup(0x0101, &WsaData) == SOCKET_ERROR)
{
printf("Error %d returned by WSAStartup\n", GetLastError());
goto cleanup;
}
Initializing Windows Sockets is critical. Failing to initialize
Windows Sockets will cause all other socket function calls to
fail.
This sample utilizes Windows Sockets version 1.1. Using newer
versions would require modifications to the code. An application
that calls WSAStartup
must call
WSACleanup
when it is done using the Windows Socket
services (see step 10).
2. Get a client certificate if needed
If a user is specified try to find a client certificate in the
"MY" store. A valid client certificate must be installed on
the client machine:
hMyCertStore = CertOpenSystemStore(0, "MY");
.
.
.
pCertContext = CertFindCertificateInStore(hMyCertStore,
X509_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR_A,
pszUserName,
NULL);
To view the certificates on your system open Microsoft Internet
Explorer and select Tools->Internet
Options->Content->Certificate
If a user is not specified then just return an empty client
credential. A client certificate is only necessary if the
server requests it.
3. Create a local socket
The call to create a socket looks like this:
Socket = socket(PF_INET, SOCK_STREAM, 0);
4. Connect to a local port
To connect the socket to a local port, call the following
function:
connect(Socket, (struct sockaddr *)&sin, sizeof(sin))
When calling connect, the system takes care of binding the
socket.
The sin
parameter is the SOCKADDR
representation of the local name to bind to. This name consists of
an address family (for TCP/IP, always AF_INET
), a host
address, and a port number.
In this sample, the calls to socket
and
connect
are both done in the function
ConnectToServer
.
5. Perform the SSL handshake
The InitializeSecurityContextA
funtion is
used to handle the required messages and generate a security token
that the client will provide to the server:
scRet = g_pSSPI->InitializeSecurityContextA(phCreds,
phContext,
NULL,
dwSSPIFlags,
0,
SECURITY_NATIVE_DREP,
&InBuffer,
0,
NULL,
&OutBuffer,
&dwSSPIOutFlags,
&tsExpiry);
Client messages are sent using the send
function. Responses are then read using the
recv
function:
cbData = send(Socket,
OutBuffers[0].pvBuffer,
OutBuffers[0].cbBuffer,
0);
.
.
.
cbData = recv(Socket,
IoBuffer + cbIoBuffer,
IO_BUFFER_SIZE - cbIoBuffer,
0);
These calls are done in the functions
PerformClientHandshake
and
ClientHandshakeLoop
.
6. Validate the server certificate
Although the client certificate is optional, the server
certificate is required. However, if the certificate is not
trusted, this sample prints an error message but allows the
communication to continue. A real application would terminate
the connection. See the section above titled "Client and
Server Certificates" for information on how to grant trust to the
server certificate and avoid the error message.
Get the server's certificate with this function:
Status = g_pSSPI->QueryContextAttributes(&hContext,
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
(PVOID)&pRemoteCertContext);
Then validate the returned certificate using the
VerifyServerCertificate
function:
CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL,
pChainContext,
&PolicyPara,
&PolicyStatus))
These calls are made in main.
7. Encrypt and transmit data
This sample sends an HTTP request that, when connected to a web
server, would return the specified file. However, with
a loopback plug inserted on the device server the text of the HTTP
request will simply be echoed back to the client program. The
data is assembled and then encrypted:
scRet = g_pSSPI->EncryptMessage(phContext,
0,
&Message,
0);
The encrypted data is then sent to the server using
send
:
cbData = send(Socket,
pbIoBuffer,
Buffers[0].cbBuffer +
Buffers[1].cbBuffer +
Buffers[2].cbBuffer,
0);
The Buffers
array is copied into a member of
the Message
struct before these functions are
called. This code is found in the HttpsGetFile
function.
8. Receive and decrypt data
To retrieve data received at the local address, call
recv
.
cbData = recv(Socket,
pbIoBuffer + cbIoBuffer,
cbIoBufferLength - cbIoBuffer,
0);
The data is then decrypted:
scRet = g_pSSPI->DecryptMessage(phContext,
&Message,
0,
NULL);
A Hex dump of the sent and received data is displayed throughout
each step of the communication with calls to the function
PrintHexDump
.
9. Disconnect from the server and close the
socket
Send a close notify to the server using
InitializeSecurityContextA
and
send
then close the socket:
closesocket(Socket);
See the function DisconnectFromServer
for
this step.
10. Cleanup
Before exiting the program free up memory and handles and
call:
WSACleanup();
And close the certificate store:
CertCloseStore(hMyCertStore, 0);