BOOL SendEncryptedMessage(CtxtHandle* phContext,
PVOID pvData, ULONG lSize){
BOOL fSuccess = FALSE;
__try{
SECURITY_STATUS ss;
// 取得某些重要大小资讯
SecPkgContext_Sizes sizes;
ss = QueryContextAttributes(phContext, SECPKG_ATTR_SIZES, &sizes);
if(ss != SEC_E_OK){
__leave;
}
// 分配我们的缓冲器
PVOID pvPadding = alloca(sizes.cbBlockSize);
PVOID pvSignature = alloca(sizes.cbSecurityTrailer);
// 最好复制讯息缓冲器,因为它会在适当的地方被加密
PVOID pvMessage = alloca(lSize);
CopyMemory(pvMessage, pvData, lSize);
SecBuffer secBuffer[3] == {0};
// 设定签章缓冲器
secBuffer[0].BufferType = SECBUFFER_TOKEN;
secBuffer[0].cbBuffer = sizes.cbSecurityTrailer;
secBuffer[0].pvBuffer = pvSignature;
// 设定讯息缓冲器
secBuffer[1].BufferType = SECBUFFER_DATA;
secBuffer[1].cbBuffer = lSize;
secBuffer[1].pvBuffer = pvMessage;
// 设定填充缓冲器
secBuffer[2].BufferType = SECBUFFER_PADDING;
secBuffer[2].cbBuffer = sizes.cbBlockSize;
secBuffer[2].pvBuffer = pvPadding;
// 设定缓冲器描述项
SecBufferDesc secBufferDesc;
secBufferDesc.cBuffers = 3;
secBufferDesc.pBuffers = secBuffer;
secBufferDesc.ulVersion = SECBUFFER_VERSION;
// 加密讯息
ss = EncryptMessage(phContext, 0, &secBufferDesc, 0 );
if(ss != SEC_E_OK){
__leave;
}
// 传送权杖
SendData(&secBuffer[0].cbBuffer, sizeof(ULONG));
SendData(secBuffer[0].pvBuffer, secBuffer[0].cbBuffer);
// 传送讯息
SendData(&secBuffer[1].cbBuffer, sizeof(ULONG));
SendData(secBuffer[1].pvBuffer, secBuffer[1].cbBuffer);
// 传送填充
SendData(&secBuffer[2].cbBuffer, sizeof(ULONG));
SendData(secBuffer[2].pvBuffer, secBuffer[2].cbBuffer);
fSuccess = TRUE;
}__finally{}
return fSuccess;
}
请注意,这个程序代码与您已经见过的讯息签章程序代码非常相似。列于下方的函数为GetEncryptedMessage,它是一个与前述函数相配范例函数,用来将讯息解密并传回缓冲器:
PVOID GetEncryptedMessage(CtxtHandle* phContext, PULONG plSize){
PVOID pvMessage = NULL;
__try{
SECURITY_STATUS ss;
ULONG lSigLen;
PVOID pvDataSig;
// 取得签章长度
ULONG lTempSize = sizeof(lSigLen);
ReceiveData(&lSigLen, &lTempSize);
pvDataSig = alloca(lSigLen);
// 取得签章
ReceiveData(pvDataSig, &lSigLen);
ULONG lMsgLen;
PVOID pvDataMsg;
// 取得讯息长度
lTempSize = sizeof(lMsgLen);
ReceiveData(&lMsgLen, &lTempSize);
pvDataMsg = alloca(lMsgLen);
// 取得讯息
ReceiveData(pvDataMsg, &lMsgLen);
ULONG lPadLen;
PVOID pvDataPad;
// 取得填充长度
lTempSize = sizeof(lPadLen);
ReceiveData(&lPadLen, &lTempSize);
pvDataPad = alloca(lPadLen);
// 取得填充内容
ReceiveData(pvDataPad, &lPadLen);
SecBuffer secBuffer[3] = {0};
// 设定签章缓冲器
secBuffer[0].BufferType = SECBUFFER_TOKEN;
secBuffer[0].cbBuffer = lSigLen;
secBuffer[0].pvBuffer = pvDataSig;
// 设定讯息缓冲器
secBuffer[1].BufferType = SECBUFFER_DATA;
secBuffer[1].cbBuffer = lMsgLen;
secBuffer[1].pvBuffer = pvDataMsg;
// 设定填充缓冲器
secBuffer[2].BufferType = SECBUFFER_PADDING;
secBuffer[2].cbBuffer = lPadLen;
secBuffer[2].pvBuffer = pvDataPad;
// 设定缓冲器描述项
SecBufferDesc secBufferDesc;
secBufferDesc.cBuffers = 3;
secBufferDesc.pBuffers = secBuffer;
secBufferDesc.ulVersion = SECBUFFER_VERSION;
ULONG lQual=0;
ss = DecryptMessage( phContext, &secBufferDesc, 0, &lQual );
if (ss != SEC_E_OK){
__leave;
}
// 传回一个必须被释放的缓冲器,包含讯息
pvMessage = LocalAlloc(LPTR, secBuffer[1].cbBuffer);
if (pvMessage != NULL){
CopyMemory(pvMessage, secBuffer[1].pvBuffer,
secBuffer[1].cbBuffer);
}
}__finally{}
return (pvMessage);
}现在,您已经熟悉了对讯息加密及签章的部份,并且也拥有实作完整工作阶段和SSPI的工具。所以您应该知道如何去协商验证,利用服务器模拟客户端以及如何以安全的方法传递及接收资料的部份。
SSPIChat范例应用程序
SSPIChat范例应用程序(「12 SSPIChat.exe」)示范了截至目前为止,所有我们讨论过的SSPI相关技巧,包括客户端及服务器验证协商、模拟、讯息签章及加密的部份。这个范例应用程序的原始程序代码及资源档存放在随书光碟上的12-SSPIChat目录中。图12-6显示了SSPIChat范例应用程序的使用者介面。
| 图12-6 SSPIChat范例应用程序的使用者介面 |
使用这个范例时,您应该连线到网路的同一台机器或不同机器上执行一次以上。这个范例应用程序使用TCP/IP通讯的方式。您可以在初始会谈之前选择要使用的安全性提供者,也可以选择是否要执行彼此验证、加密或委派的动作。
委派的特色使服务器建立了第二个假定为客户端身分识别的交谈视窗。然后新的视窗可以被用来对将它看做原始客户端的第叁个服务器扮演客户端(假如服务器机器未被委派信任,这个特色将会失败)。
由于通讯层独立的精神,使这个范例应用程序中的通讯功能被抽象化为CTransport类别,它包括了SendData及ReceiveData函数。这两个函数非常像我在本章的程序代码片段中使用的虚拟函数。
在此强烈地建议您在试图使用SSL(在本章稍后讨论)编写SSPI程序代码前,先熟悉这个应用程序的范例程序代码。SSPI程序设计模组是很复杂的,而它也带来了少数的说明。所以适应全部的方法将会使SSL令人感到更愉快。
CryptoAPI
