InitializeSecurityContext(Schannel)意外返回SEC_I_INCOMPLETE_CREDENTIALS

时间:2017-11-22 22:12:37

标签: windows winapi sspi schannel

我已经实现了使用SSPI创建安全连接的代码。我用它通过SMTP发送邮件。它适用于smtp.gmail.com以及我尝试过的其他一些服务器。但它不适用于smtp.live.comInitializeSecurityContext()的第二个电话会给我SEC_I_INCOMPLETE_CREDENTIALS。据我所知,这意味着我需要提供客户端证书。但这是一个公共免费邮件服务器。它需要什么样的客户证书?或者我可能使用了一些错误的参数或标志?

我连接到smtp.live.com:587最初连接是未加密的纯文本。首先,我发送EHLO命令,然后发送STARTTLS命令。之后,我使用下面的代码执行SSL握手

以下是代码:

SCHANNEL_CRED schCred;
memset(&schCred,0,sizeof(schCred));
schCred.dwVersion=SCHANNEL_CRED_VERSION; // == 4
schCred.dwFlags=SCH_CRED_NO_DEFAULT_CREDS;

// acquire credentials
SECURITY_STATUS status=
    AcquireCredentialsHandle(NULL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NULL,
                &schCred,NULL,NULL,&Creds,NULL);

// then I create a context 
const DWORD dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   |
                ISC_REQ_REPLAY_DETECT     |
                ISC_REQ_CONFIDENTIALITY   |
                ISC_RET_EXTENDED_ERROR    |
                ISC_REQ_ALLOCATE_MEMORY   |
                ISC_REQ_STREAM;

SECURITY_STATUS scRet =
          InitializeSecurityContextW(&Creds, NULL, targetName, dwSSPIFlags,
                0, 0 , NULL, 0, &Context,&outBuffer, &dwSSPIOutFlags, &tsExpiry); 

// scRet==SEC_I_CONTINUE_NEEDED

SendOutBuffer(outBuffers);
ReceiveInputBuffers(inBuffers);

// Then I call InitializeSecurityContext for the second time
scRet = InitializeSecurityContext(&Creds, &Context, NULL, dwSSPIFlags,
              0, 0, &inBuffer, 0, NULL, &outBuffer, &dwSSPIOutFlags, &tsExpiry);


if( ( SEC_E_OK == scRet || SEC_I_CONTINUE_NEEDED == scRet ||
 FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR) ) 
    && BufferNotEmpty(outBuffers)) {        

    SendOutBuffer(outBuffers);                
} 

if(SEC_I_INCOMPLETE_CREDENTIALS == scRet) {                  
 // This is where I am when connecting to smtp.live.com
} 

1 个答案:

答案 0 :(得分:0)

回答我自己的问题。我使用Wireshark捕获了流量,是的, smtp.live.com 在TLS握手期间确实要求提供客户端证书。但它足以发送一个空的(零长度)证书来继续握手。为此,在获得SEC_I_INCOMPLETE_CREDENTIALS之后,我只需使用相同的参数再次调用InitializeSecurityContext()。换句话说,在获得SEC_I_INCOMPLETE_CREDENTIALS之后,我继续握手循环但跳过接收输入缓冲区以进行下一次迭代。如果我第二次收到SEC_I_INCOMPLETE_CREDENTIALS,我退出时会出错,因为我的客户端不支持使用证书进行身份验证。 (实际上,我不确定是否可以获得SEC_I_INCOMPLETE_CREDENTIALS两次)