Both the client side and the server side of a transport application can use the MakeSignature function to generate a signed message to send to the other side. The receiving side then uses the VerifySignature function to verify that a signature matches the received message. If the application wants to generate signed messages, the client must have specified the ISC_REQ_REPLAY_DETECT or ISC_REQ_SEQUENCE_DETECT flag in its first call to the InitializeSecurityContext function.
When the client calls MakeSignature or VerifySignature, it uses the ClientContext handle it obtained from its first call to the InitializeSecurityContext function. When the server calls these functions, it uses the ServerContext handle it obtained from its call to the AcceptSecurityContext function.
The following example shows the client side generating a signed message to send to the server. Before calling MakeSignature, the client calls the QueryContextAttributes function with a SecPkgContext_Sizes structure to determine the length of the buffer needed to hold the message signature. If the cbMaxSignature member is zero, the security package does not support signing messages; otherwise, this member indicates the size of the buffer to allocate to receive the signature.
SecPkgContext_Sizes ContextSizes;
SecStatus = QueryContextAttributes(
&ClientContext,
SECPKG_ATTR_SIZES,
&ContextSizes
);
//
// Now, assume that the message is in the variable MessageBuffer, and
// its length is in MessageBufferSize. Build up the buffer descriptors
// to pass to the MakeSignature call.
//
SecBufferDesc InputBufferDescriptor;
SecBuffer InputSecurityToken[2];
//
// Build the input buffer descriptor.
//
InputBufferDescriptor.cBuffers = 2
InputBufferDescriptor.pBuffers = InputSecurityToken;
InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
//
// Build a security buffer for the message itself. If we added
// the SECBUFFER_READONLY attribute, this buffer would not get
// signed.
//
InputSecurityToken[0].BufferType = SECBUFFER_DATA;
InputSecurityToken[0].cbBuffer = MessageBufferSize;
InputSecurityToken[0].pvBuffer = MessageBuffer;
//
// Allocate and build a security buffer for the message
// signature.
//
InputSecurityToken[1].BufferType = SECBUFFER_TOKEN;
InputSecurityToken[1].cbBuffer = ContextSizes.cbMaxSignature;
InputSecurityToken[1].pvBuffer =
LocalAlloc(0, ContextSizes.cbMaxSignature);
//
// Check for memory allocation failure here
//
//
// Call MakeSignature now.
// You need not specify the sequence number because NTLM will
// provide one.
// The quality of service is ignored.
//
SecStatus = MakeSignature(
&ClientContext,
0, // no quality of service
&InputBufferDescriptor, // input message descriptor
0 // no sequence number
);
The MakeSignature function returns successfully if the context was set up to allow signing messages and the input buffer descriptor is correctly formatted. If the function is successful, the client sends the message buffer and its size, along with the signature buffer and its size, to the server.
The server calls the the QueryContextAttributes function with a SecPkgContext_Sizes structure to determine whether the client wants messages to be signed and verified. A nonzero value for the cbMaxSignature member indicates that messages need to be verified.
The following example shows the server code to verify a signed message. The example is built on the assumption that the signature buffer and size are in SignatureBuffer and SignatureBufferSize, and the message buffer and size are in MessageBuffer and MessageBufferSize.
SecBufferDesc InputBufferDescriptor;
SecBuffer InputSecurityToken[2];
ULONG fQOP;
//
// Build the input buffer descriptor.
//
IputBufferDescriptor.cBuffers = 2
InputBufferDescriptor.pBuffers = InputSecurityToken;
InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
//
// Build a security buffer for the message itself. If we added
// the SECBUFFER_READONLY attribute, this buffer would not get
// signed.
//
InputSecurityToken[0].BufferType = SECBUFFER_DATA;
InputSecurityToken[0].cbBuffer = MessageBufferSize;
InputSecurityToken[0].pvBuffer = MessageBuffer;
//
// Allocate and build a security buffer for the message
// signature.
//
InputSecurityToken[1].BufferType = SECBUFFER_TOKEN;
InputSecurityToken[1].cbBuffer = SignatureBufferSize;
InputSecurityToken[1].pvBuffer = SignatureBuffer;
//
// Call VerifySignature now.
// You need not specify the sequence number because NTLM will
// provide one.
// The quality of service is ignored.
//
SecStatus = VerifySignature(
&ServerContext,
&InputBufferDescriptor, // input message descriptor
0, // no sequence number
&fQOP // Quality of protection
);
NTLMSSP does not fill in the fQOP parameter, so it should be ignored. If the messages have been received in the correct order and have not been modified, the VerifySignature function returns SEC_E_OK. Otherwise, VerifySignature may return SEC_E_OUT_OF_SEQUENCE or SEC_E_MESSAGE_ALTERED.
To delete the security contexts after the client and server have finished communicating, both sides can call the DeleteSecurityContext function with their respective context handles. The client should also call the FreeCredentialsHandle function when it has finished communicating with any server or has finished using the additional credentials passed to the AcquireCredentialsHandle function. The server should call DeleteSecurityContext when it is ready to shut down, but before unloading the DLL.