12.2.1 安全连接.htm
来自「Windows2000后台服务程序开发手册」· HTM 代码 · 共 514 行 · 第 1/5 页
HTM
514 行
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>也可以使用QuerySecurityContextToken函数从安全性环境中要求权杖:</FONT></P>
<DIV style="LINE-HEIGHT: 25px; BACKGROUND-COLOR: #d7d7d7"><FONT
style="LINE-HEIGHT: 25px" face=Arial size=3><PRE style="LINE-HEIGHT: 25px">SECURITY_STATUS QuerySecurityContextToken( PCtxtHandle phContext, HANDLE *phToken);</PRE></FONT></DIV>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>使用QuerySecurityContextToken函数,可以让您取得客户端的权杖,并将它与关联的资讯一起储存,而不用做第一次模拟客户端的动作。</FONT></P>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>模拟对服务器来说是管理安全性的重要方法,管理安全性在第十一章有详细的讨论。SSPI可让您在<FONT
style="LINE-HEIGHT: 25px" face=arial color=#3e80d7 size=2><B
style="LINE-HEIGHT: 25px"> 任何 </B></FONT>通讯机制上模拟客户端。您不再被限定于管道或RPC的方法。假如它为您软件的需求服务时,可以使用通讯端、IPX/SPX或序列连接的方式模拟客户端。</FONT></P>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>您可以在本章稍后讨论的SSPIChat范例应用程序中找到完整的客户端模拟及服务器SSPI程序。但是现在我们要谈论更多关于讯息签章及加密的部份。</FONT></P>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#3e72d7
size=4><B style="LINE-HEIGHT: 25px">讯息签章及加密<BR
style="LINE-HEIGHT: 25px"> </B></FONT></P>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>一旦您在客户端及服务器之间协商验证时,客户端及服务器可以如同客户端/服务器一般在预期通讯,并开始有系统的交换讯息。要完全利用这个交换,应该使用签章或加密后传递的讯息。以下是应遵循的方针:</FONT></P><FONT
style="LINE-HEIGHT: 25px" face=arial color=#000000 size=2>
<OL style="LINE-HEIGHT: 25px">
<LI style="LINE-HEIGHT: 25px">当不需要私密性但要确信讯息具有完整性时(经常会需要),需要对讯息做签章。
<LI style="LINE-HEIGHT: 25px">当需要私密性时,则要对讯息做加密(有加密就不需要签章)。
</LI></OL></FONT>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>签章及加密以相同的方法使用与验证类似的函数,但是这个机制对客户端及服务器来说是相同的。建立签章讯息的成对函数是MakeSignature及VerifySignature。MakeSignature函数的定义如下:</FONT></P>
<DIV style="LINE-HEIGHT: 25px; BACKGROUND-COLOR: #d7d7d7"><FONT
style="LINE-HEIGHT: 25px" face=Arial size=3><PRE style="LINE-HEIGHT: 25px">SECURITY_STATUS MakeSignature( <BR> PCtxtHandle phContext, <BR> ULONG lQOP, <BR> PSecBufferDesc pMessage, <BR> ULONG MessageSeqNo);</PRE></FONT></DIV>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>传递资料缓冲器及完整的环境给MakeSignature,函数会传回您跨越线路所传递(与资料缓冲器一起)的签章。通讯的接收端会取得这个资讯并使用这个签章来验证资料缓冲器。再次提醒,您应该把MakeSignature传回的资料视为打算只用来做通讯用的不透明Blob。</FONT></P>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>LQOP参数是特殊的安全性通讯协定,而且可让您指定使用签章演算法的细节。通常会传递0给这个参数。假如您要记录讯息序号时,应该传递讯息的序号给MessageSeqNo参数。如果传递0,表示顺序与您无关。</FONT></P>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>如我们处理过的其他SSPI函数一般,资料会经由资料缓冲器传递到MakeSignature及接收。至于MakeSignature的部份,可以使用单一缓冲器描述项传递二个缓冲器,所以您必须建立二个SecBuffer变数阵列。设定其中一个为SECBUFFER_TOKEN类型,它将接收讯息的签章。第二个缓冲器为SECBUFFER_DATA类型,指出资料本身。这个函数会对一个资料缓冲器做签章,并使用虚拟通讯函数SendData传送它:</FONT></P>
<DIV style="LINE-HEIGHT: 25px; BACKGROUND-COLOR: #d7d7d7"><FONT
style="LINE-HEIGHT: 25px" face=Arial size=3><PRE style="LINE-HEIGHT: 25px">BOOL SendSignedMessage(CtxtHandle* phContext, PVOID pvData, ULONG lSize) <BR>{ <BR> BOOL fSuccess = FALSE; <BR> __try{ <BR> SECURITY_STATUS ss; <BR> // 找出某些重要的最大缓冲器大小资讯 <BR> SecPkgContext_Sizes sizes; <BR> ss = QueryContextAttributes(phContext, SECPKG_ATTR_SIZES, &sizes ); <BR> if(ss != SEC_E_OK){ <BR> __leave; <BR> } <BR> PVOID pvSignature = alloca(sizes.cbMaxSignature); <BR> SecBuffer secBuffer[2]; <BR> // 设定缓冲器接收签章 <BR> secBuffer[0].BufferType = SECBUFFER_TOKEN; <BR> secBuffer[0].cbBuffer = sizes.cbMaxSignature; <BR> secBuffer[0].pvBuffer = pvSignature; <BR> // 设定缓冲器指向讯息资料 <BR> secBuffer[1].BufferType = SECBUFFER_DATA; <BR> secBuffer[1].cbBuffer = lSize; <BR> secBuffer[1].pvBuffer = pvData; <BR> // 设定缓冲器描述项 <BR> SecBufferDesc secBufferDesc; <BR> secBufferDesc.cBuffers = 2; <BR> secBufferDesc.pBuffers = secBuffer; <BR> secBufferDesc.ulVersion = SECBUFFER_VERSION; <BR> // 制作签章 <BR> ss = MakeSignature(phContext, 0, &secBufferDesc, 0 ); <BR> if(ss != SEC_E_OK){ <BR> __leave; <BR> } <BR> // 传送签章 <BR> SendData(&secBuffer[0].cbBuffer, sizeof(ULONG)); <BR> SendData(secBuffer[0].pvBuffer, secBuffer[0].cbBuffer); <BR> // 传送讯息 <BR> SendData(&secBuffer[1].cbBuffer, sizeof(ULONG)); <BR> SendData(secBuffer[1].pvBuffer, secBuffer[1].cbBuffer); <BR> fSuccess = TRUE; <BR> }__finally{ <BR> } <BR> return (fSuccess); <BR>}</PRE></FONT></DIV>
<HR style="LINE-HEIGHT: 25px">
<P><FONT style="LINE-HEIGHT: 25px" face=Arial color=#3e77d7 size=3
Black><B style="LINE-HEIGHT: 25px">说明</B></FONT> </P>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>和InitializeSecurityContext及AcceptSecurityContext
不同,MakeSignature没有个别的输入及输出缓冲器,它也不会为输入分配缓冲器。这意味着您必须提供够大的缓冲器给产生的签章。您可以使用具有SECPKG_ATTR_SIZES属性的QueryContextAttributes函数找出最大的签章大小。QueryContextAttributes函数的定义如下:</FONT></P>
<DIV style="LINE-HEIGHT: 25px; BACKGROUND-COLOR: #d7d7d7"><FONT
style="LINE-HEIGHT: 25px" face=Arial size=3><PRE style="LINE-HEIGHT: 25px">SECURITY_STATUS QueryContextAttributes( <BR> PCtxtHandle phContext, <BR> ULONG lAttribute, <BR> PVOID pBuffer);</PRE></FONT></DIV>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>在这个情况下,您需要传递一个函数中的SecPkgContext_Sizes结构实体。以下是结构的定义:</FONT></P>
<DIV style="LINE-HEIGHT: 25px; BACKGROUND-COLOR: #d7d7d7"><FONT
style="LINE-HEIGHT: 25px" face=Arial size=3><PRE style="LINE-HEIGHT: 25px">typedef struct _SecPkgContext_Sizes { <BR> ULONG cbMaxToken; <BR> ULONG cbMaxSignature; <BR> ULONG cbBlockSize; <BR> ULONG cbSecurityTrailer; } SecPkgContext_Sizes;</PRE></FONT></DIV>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>SecPkgContext_Sizes结构适当地包含了某些有关最大大小的资讯,包括与MakeSignature一起使用的cbMaxSignature,这些皆显示在前面的程序代码范例中。</FONT></P>
<HR style="LINE-HEIGHT: 25px">
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>交易的另一端必需读取签章及讯息资料,并将它们传递到VerifySignature中:</FONT></P>
<DIV style="LINE-HEIGHT: 25px; BACKGROUND-COLOR: #d7d7d7"><FONT
style="LINE-HEIGHT: 25px" face=Arial size=3><PRE style="LINE-HEIGHT: 25px">SECURITY_STATUS VerifySignature( <BR> PCtxtHandle phContext, <BR> PSecBufferDesc pMessage, <BR> ULONG MessageSeqNo, <BR> PULONG plQOP);</PRE></FONT></DIV>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>VerifySignature函数与MakeSignature相似,因为它会取得相同的缓冲器及类型。然而,VerifySignature只确保讯息为被修改以及没有修改缓冲器。以下是一个范例应用程序,它会接收签章及讯息,并呼叫VerifySignature函数:</FONT></P>
<DIV style="LINE-HEIGHT: 25px; BACKGROUND-COLOR: #d7d7d7"><FONT
style="LINE-HEIGHT: 25px" face=Arial size=3><PRE style="LINE-HEIGHT: 25px">PVOID GetSignedMessage(CtxtHandle* phContext, PULONG plSize) <BR>{ <BR> PVOID pvMessage = NULL; <BR> __try{ <BR> SECURITY_STATUS ss; <BR> ULONG lSigLen; <BR> PVOID pvDataSig; <BR> // 取得签章长度 <BR> ULONG lTempSize = sizeof(lSigLen); <BR> ReceiveData(&lSigLen, &lTempSize); <BR> pvDataSig = alloca(lSigLen); <BR> // 取得签章 <BR> ReceiveData(pvDataSig, &lSigLen); <BR> ULONG lMsgLen; <BR> PVOID pvDataMsg; <BR> // 取得讯息长度 <BR> lTempSize = sizeof(lMsgLen); <BR> ReceiveData(&lMsgLen, &lTempSize); <BR> pvDataMsg = alloca(lMsgLen); <BR> // 取得讯息 <BR> ReceiveData(pvDataMsg, &lMsgLen); <BR> SecBuffer secBuffer[2]; <BR> // 设定签章缓冲器 <BR> secBuffer[0].BufferType = SECBUFFER_TOKEN; <BR> secBuffer[0].cbBuffer = lSigLen; <BR> secBuffer[0].pvBuffer = pvDataSig; <BR> // 设定讯息缓冲器 <BR> secBuffer[1].BufferType = SECBUFFER_DATA; <BR> secBuffer[1].cbBuffer = lMsgLen; <BR> secBuffer[1].pvBuffer = pvDataMsg; <BR> // 设定缓冲器描述项 <BR> SecBufferDesc secBufferDesc; <BR> secBufferDesc.cBuffers = 2; <BR> secBufferDesc.pBuffers = secBuffer; <BR> secBufferDesc.ulVersion = SECBUFFER_VERSION; <BR> ULONG lQual=0; <BR> // 验证签章 <BR> ss = VerifySignature(phContext, &secBufferDesc, 0, &lQual); <BR> if (ss != SEC_E_OK){ <BR> __leave; <BR> } <BR> // 传回必须被释放的缓冲器,包含讯息 <BR> pvMessage = LocalAlloc(LPTR, secBuffer[1].cbBuffer); <BR> if (pvMessage != NULL){ <BR> CopyMemory(pvMessage, secBuffer[1].pvBuffer, <BR> secBuffer[1].cbBuffer); <BR> } <BR> }__finally{}; <BR> return pvMessage; <BR>}</PRE></FONT></DIV>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>假如讯息在传输过程中被修改,则VerifySignature将会传回SEC_E_MESSAGE_
ALTERED或SEC_E_OUT_OF_SEQUENCE值。</FONT></P>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#3e74d7
size=3><B style="LINE-HEIGHT: 25px">加密讯息<BR
style="LINE-HEIGHT: 25px"> </B></FONT></P>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>加解密讯息的过程和签章及验证讯息的情形非常相似,讯息会在适当的地方被加解密。这意味着您所传递的资料缓冲器将被这些函数修改。因为某些加密演算法要求加密的资料须有多种特定区块的大小(例如8位元或16位元),所以检查区块大小是必要的,包括「加密溢位」类型的第叁个缓冲器,它也必须跨越线路而被传送。我们要使用的函数为EncryptMessage及DecryptMessage。EncryptMessage函数定义如下:</FONT></P>
<DIV style="LINE-HEIGHT: 25px; BACKGROUND-COLOR: #d7d7d7"><FONT
style="LINE-HEIGHT: 25px" face=Arial size=3><PRE style="LINE-HEIGHT: 25px">SECURITY_STATUS EncryptMessage( <BR> PCtxtHandle phContext, <BR> ULONG lQOP, <BR> PSecBufferDesc pMessage, <BR> ULONG MessageSeqNo);</PRE></FONT></DIV>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>以下为DecryptMessage函数的定义:</FONT></P>
<DIV style="LINE-HEIGHT: 25px; BACKGROUND-COLOR: #d7d7d7"><FONT
style="LINE-HEIGHT: 25px" face=Arial size=3><PRE style="LINE-HEIGHT: 25px">SECURITY_STATUS DecryptMessage( <BR> PCtxtHandle phContext, <BR> PSecBufferDesc pMessage, <BR> ULONG MessageSeqNo, <BR> PULONG plQOP <BR>);</PRE></FONT></DIV>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>以下为一个范例函数,它会对讯息加密并经由SendData的使用,跨越线路传送讯息:</FONT></P>
<DIV style="LINE-HEIGHT: 25px; BACKGROUND-COLOR: #d7d7d7"><FONT
style="LINE-HEIGHT: 25px" face=Arial size=3><PRE style="LINE-HEIGHT: 25px">BOOL SendEncryptedMessage(CtxtHandle* phContext, <BR> PVOID pvData, ULONG lSize){ <BR> BOOL fSuccess = FALSE; <BR> __try{ <BR> SECURITY_STATUS ss; <BR> // 取得某些重要大小资讯 <BR> SecPkgContext_Sizes sizes; <BR> ss = QueryContextAttributes(phContext, SECPKG_ATTR_SIZES, &sizes); <BR> if(ss != SEC_E_OK){ <BR> __leave; <BR> } <BR> // 分配我们的缓冲器 <BR> PVOID pvPadding = alloca(sizes.cbBlockSize); <BR> PVOID pvSignature = alloca(sizes.cbSecurityTrailer); <BR> // 最好复制讯息缓冲器,因为它会在适当的地方被加密 <BR> PVOID pvMessage = alloca(lSize); <BR> CopyMemory(pvMessage, pvData, lSize); <BR> SecBuffer secBuffer[3] == {0}; <BR> // 设定签章缓冲器 <BR> secBuffer[0].BufferType = SECBUFFER_TOKEN; <BR> secBuffer[0].cbBuffer = sizes.cbSecurityTrailer; <BR> secBuffer[0].pvBuffer = pvSignature; <BR> // 设定讯息缓冲器 <BR> secBuffer[1].BufferType = SECBUFFER_DATA; <BR> secBuffer[1].cbBuffer = lSize; <BR> secBuffer[1].pvBuffer = pvMessage; <BR> // 设定填充缓冲器 <BR> secBuffer[2].BufferType = SECBUFFER_PADDING; <BR> secBuffer[2].cbBuffer = sizes.cbBlockSize; <BR> secBuffer[2].pvBuffer = pvPadding; <BR> // 设定缓冲器描述项 <BR> SecBufferDesc secBufferDesc; <BR> secBufferDesc.cBuffers = 3; <BR> secBufferDesc.pBuffers = secBuffer; <BR> secBufferDesc.ulVersion = SECBUFFER_VERSION; <BR> // 加密讯息 <BR> ss = EncryptMessage(phContext, 0, &secBufferDesc, 0 ); <BR> if(ss != SEC_E_OK){ <BR> __leave; <BR> } <BR> // 传送权杖 <BR> SendData(&secBuffer[0].cbBuffer, sizeof(ULONG)); <BR> SendData(secBuffer[0].pvBuffer, secBuffer[0].cbBuffer); <BR> // 传送讯息 <BR> SendData(&secBuffer[1].cbBuffer, sizeof(ULONG)); <BR> SendData(secBuffer[1].pvBuffer, secBuffer[1].cbBuffer); <BR> // 传送填充 <BR> SendData(&secBuffer[2].cbBuffer, sizeof(ULONG)); <BR> SendData(secBuffer[2].pvBuffer, secBuffer[2].cbBuffer); <BR> fSuccess = TRUE; <BR> }__finally{} <BR> return fSuccess; <BR>}</PRE></FONT></DIV>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>请注意,这个程序代码与您已经见过的讯息签章程序代码非常相似。列于下方的函数为GetEncryptedMessage,它是一个与前述函数相配范例函数,用来将讯息解密并传回缓冲器:</FONT></P>
<DIV style="LINE-HEIGHT: 25px; BACKGROUND-COLOR: #d7d7d7"><FONT
style="LINE-HEIGHT: 25px" face=Arial size=3><PRE style="LINE-HEIGHT: 25px">PVOID GetEncryptedMessage(CtxtHandle* phContext, PULONG plSize){ <BR> PVOID pvMessage = NULL; <BR> __try{ <BR> SECURITY_STATUS ss; <BR> ULONG lSigLen; <BR> PVOID pvDataSig; <BR> // 取得签章长度 <BR> ULONG lTempSize = sizeof(lSigLen); <BR> ReceiveData(&lSigLen, &lTempSize); <BR> pvDataSig = alloca(lSigLen); <BR> // 取得签章 <BR> ReceiveData(pvDataSig, &lSigLen); <BR> ULONG lMsgLen; <BR> PVOID pvDataMsg; <BR> // 取得讯息长度 <BR> lTempSize = sizeof(lMsgLen); <BR> ReceiveData(&lMsgLen, &lTempSize); <BR> pvDataMsg = alloca(lMsgLen); <BR> // 取得讯息 <BR> ReceiveData(pvDataMsg, &lMsgLen); <BR> ULONG lPadLen; <BR> PVOID pvDataPad; <BR> // 取得填充长度 <BR> lTempSize = sizeof(lPadLen); <BR> ReceiveData(&lPadLen, &lTempSize); <BR> pvDataPad = alloca(lPadLen); <BR> // 取得填充内容 <BR> ReceiveData(pvDataPad, &lPadLen); <BR> SecBuffer secBuffer[3] = {0}; <BR> // 设定签章缓冲器 <BR> secBuffer[0].BufferType = SECBUFFER_TOKEN; <BR> secBuffer[0].cbBuffer = lSigLen; <BR> secBuffer[0].pvBuffer = pvDataSig; <BR> // 设定讯息缓冲器 <BR> secBuffer[1].BufferType = SECBUFFER_DATA; <BR> secBuffer[1].cbBuffer = lMsgLen; <BR> secBuffer[1].pvBuffer = pvDataMsg; <BR> // 设定填充缓冲器 <BR> secBuffer[2].BufferType = SECBUFFER_PADDING; <BR> secBuffer[2].cbBuffer = lPadLen; <BR> secBuffer[2].pvBuffer = pvDataPad; <BR> // 设定缓冲器描述项 <BR> SecBufferDesc secBufferDesc; <BR> secBufferDesc.cBuffers = 3; <BR> secBufferDesc.pBuffers = secBuffer; <BR> secBufferDesc.ulVersion = SECBUFFER_VERSION; <BR> ULONG lQual=0; <BR> ss = DecryptMessage( phContext, &secBufferDesc, 0, &lQual ); <BR> if (ss != SEC_E_OK){ <BR> __leave; <BR> } <BR> // 传回一个必须被释放的缓冲器,包含讯息 <BR> pvMessage = LocalAlloc(LPTR, secBuffer[1].cbBuffer); <BR> if (pvMessage != NULL){ <BR> CopyMemory(pvMessage, secBuffer[1].pvBuffer, <BR> secBuffer[1].cbBuffer); <BR> } <BR> }__finally{} <BR> return (pvMessage); <BR>}</PRE></FONT></DIV>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>现在,您已经熟悉了对讯息加密及签章的部份,并且也拥有实作完整工作阶段和SSPI的工具。所以您应该知道如何去协商验证,利用服务器模拟客户端以及如何以安全的方法传递及接收资料的部份。</FONT></P>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#3e72d7
size=4><B style="LINE-HEIGHT: 25px">SSPIChat范例应用程序<BR
style="LINE-HEIGHT: 25px"> </B></FONT></P>
<P><FONT style="LINE-HEIGHT: 25px" face=arial color=#000000
size=2>SSPIChat范例应用程序(「12
SSPIChat.exe」)示范了截至目前为止,所有我们讨论过的SSPI相关技巧,包括客户端及服务器验证协商、模拟、讯息签章及加密的部份。这个范例应用程序的原始程序代码及资源档存放在随书光碟上的12-SSPIChat目录中。图12-6显示了SSPIChat范例应用程序的使用者介面。</FONT></P>
<CENTER style="LINE-HEIGHT: 25px">
<P><INPUT id=5 style="LINE-HEIGHT: 25px" type=image height=560
width=650 src="12.2.1 安全连接.files/12-6.gif" border=0
onclick="imgclick"> </P></CENTER>
<CENTER style="LINE-HEIGHT: 25px">
<P></P>
<P class=content_page><STRONG><FONT
color=#ff0033>[1]</FONT></STRONG> <A
href="http://www.acejoy.com/Html/Article/network/6720061103184118_P2.html">[2]</A> <A
href="http://www.acejoy.com/Html/Article/network/6720061103184118_P3.html">[3]</A> <A
href="http://www.acejoy.com/Html/Article/network/6720061103184118_P2.html">下一页</A></P></DIV><BR><BR></CENTER></DIV></TD></TR></TBODY></TABLE>
<TABLE style="CLEAR: both" cellSpacing=0 cellPadding=0 width="97%">
<TBODY>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?