Windows C++ TLS 实现连接163邮箱
生活随笔
收集整理的這篇文章主要介紹了
Windows C++ TLS 实现连接163邮箱
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1 // Compiles with Visual Studio 2008 for Windows
2
3 // This C example is designed as more of a guide than a library to be plugged into an application
4 // That module required a couple of major re-writes and is available upon request
5 // The Basic example has tips to the direction you should take
6 // This will work with connections on port 587 that upgrade a plain text session to an encrypted session with STARTTLS as covered here.
7
8 // TLSclient.c - SSPI Schannel gmail TLS connection example
9
10 #define _CRT_SECURE_NO_WARNINGS
11
12 #define SECURITY_WIN32
13 #define IO_BUFFER_SIZE 0x10000
14 #define DLL_NAME TEXT("Secur32.dll")
15 #define NT4_DLL_NAME TEXT("Security.dll")
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <windows.h>
20 #include <winsock.h>
21 #include <wincrypt.h>
22 #include <wintrust.h>
23 #include <schannel.h>
24 #include <security.h>
25 #include <sspi.h>
26
27 #pragma comment(lib, "WSock32.Lib")
28 #pragma comment(lib, "Crypt32.Lib")
29 #pragma comment(lib, "user32.lib")
30 #pragma comment(lib, "MSVCRTD.lib")
31
32 // Globals.
33 BOOL fVerbose = FALSE; // FALSE; // TRUE;
34
35
36 INT iPortNumber = 465; // gmail TLS
37 LPCSTR pszServerName = "smtp.163.com"; // DNS name of server
38 LPCSTR pszUser = 0; // if specified, a certificate in "MY" store is searched for
39
40 DWORD dwProtocol = SP_PROT_TLS1; // SP_PROT_TLS1; // SP_PROT_PCT1; SP_PROT_SSL2; SP_PROT_SSL3; 0=default
41 ALG_ID aiKeyExch = 0; // = default; CALG_DH_EPHEM; CALG_RSA_KEYX;
42
43 BOOL fUseProxy = FALSE;
44 LPCSTR pszProxyServer = "proxy";
45 INT iProxyPort = 80;
46
47 HCERTSTORE hMyCertStore = NULL;
48 HMODULE g_hSecurity = NULL;
49
50 SCHANNEL_CRED SchannelCred;
51 PSecurityFunctionTable g_pSSPI;
52
53
54
55 /*****************************************************************************/
56 static void DisplayWinVerifyTrustError(DWORD Status)
57 {
58 LPCSTR pszName = NULL;
59
60 switch (Status)
61 {
62 case CERT_E_EXPIRED: pszName = "CERT_E_EXPIRED"; break;
63 case CERT_E_VALIDITYPERIODNESTING: pszName = "CERT_E_VALIDITYPERIODNESTING"; break;
64 case CERT_E_ROLE: pszName = "CERT_E_ROLE"; break;
65 case CERT_E_PATHLENCONST: pszName = "CERT_E_PATHLENCONST"; break;
66 case CERT_E_CRITICAL: pszName = "CERT_E_CRITICAL"; break;
67 case CERT_E_PURPOSE: pszName = "CERT_E_PURPOSE"; break;
68 case CERT_E_ISSUERCHAINING: pszName = "CERT_E_ISSUERCHAINING"; break;
69 case CERT_E_MALFORMED: pszName = "CERT_E_MALFORMED"; break;
70 case CERT_E_UNTRUSTEDROOT: pszName = "CERT_E_UNTRUSTEDROOT"; break;
71 case CERT_E_CHAINING: pszName = "CERT_E_CHAINING"; break;
72 case TRUST_E_FAIL: pszName = "TRUST_E_FAIL"; break;
73 case CERT_E_REVOKED: pszName = "CERT_E_REVOKED"; break;
74 case CERT_E_UNTRUSTEDTESTROOT: pszName = "CERT_E_UNTRUSTEDTESTROOT"; break;
75 case CERT_E_REVOCATION_FAILURE: pszName = "CERT_E_REVOCATION_FAILURE"; break;
76 case CERT_E_CN_NO_MATCH: pszName = "CERT_E_CN_NO_MATCH"; break;
77 case CERT_E_WRONG_USAGE: pszName = "CERT_E_WRONG_USAGE"; break;
78 default: pszName = "(unknown)"; break;
79 }
80 printf("Error 0x%x (%s) returned by CertVerifyCertificateChainPolicy!
", Status, pszName);
81 }
82
83
84 /*****************************************************************************/
85 static void DisplayWinSockError(DWORD ErrCode)
86 {
87 LPCSTR pszName = NULL; // http://www.sockets.com/err_lst1.htm#WSANO_DATA
88
89 switch (ErrCode) // http://msdn.microsoft.com/en-us/library/ms740668(VS.85).aspx
90 {
91 case 10035: pszName = "WSAEWOULDBLOCK "; break;
92 case 10036: pszName = "WSAEINPROGRESS "; break;
93 case 10037: pszName = "WSAEALREADY "; break;
94 case 10038: pszName = "WSAENOTSOCK "; break;
95 case 10039: pszName = "WSAEDESTADDRREQ "; break;
96 case 10040: pszName = "WSAEMSGSIZE "; break;
97 case 10041: pszName = "WSAEPROTOTYPE "; break;
98 case 10042: pszName = "WSAENOPROTOOPT "; break;
99 case 10043: pszName = "WSAEPROTONOSUPPORT"; break;
100 case 10044: pszName = "WSAESOCKTNOSUPPORT"; break;
101 case 10045: pszName = "WSAEOPNOTSUPP "; break;
102 case 10046: pszName = "WSAEPFNOSUPPORT "; break;
103 case 10047: pszName = "WSAEAFNOSUPPORT "; break;
104 case 10048: pszName = "WSAEADDRINUSE "; break;
105 case 10049: pszName = "WSAEADDRNOTAVAIL "; break;
106 case 10050: pszName = "WSAENETDOWN "; break;
107 case 10051: pszName = "WSAENETUNREACH "; break;
108 case 10052: pszName = "WSAENETRESET "; break;
109 case 10053: pszName = "WSAECONNABORTED "; break;
110 case 10054: pszName = "WSAECONNRESET "; break;
111 case 10055: pszName = "WSAENOBUFS "; break;
112 case 10056: pszName = "WSAEISCONN "; break;
113 case 10057: pszName = "WSAENOTCONN "; break;
114 case 10058: pszName = "WSAESHUTDOWN "; break;
115 case 10059: pszName = "WSAETOOMANYREFS "; break;
116 case 10060: pszName = "WSAETIMEDOUT "; break;
117 case 10061: pszName = "WSAECONNREFUSED "; break;
118 case 10062: pszName = "WSAELOOP "; break;
119 case 10063: pszName = "WSAENAMETOOLONG "; break;
120 case 10064: pszName = "WSAEHOSTDOWN "; break;
121 case 10065: pszName = "WSAEHOSTUNREACH "; break;
122 case 10066: pszName = "WSAENOTEMPTY "; break;
123 case 10067: pszName = "WSAEPROCLIM "; break;
124 case 10068: pszName = "WSAEUSERS "; break;
125 case 10069: pszName = "WSAEDQUOT "; break;
126 case 10070: pszName = "WSAESTALE "; break;
127 case 10071: pszName = "WSAEREMOTE "; break;
128 case 10091: pszName = "WSASYSNOTREADY "; break;
129 case 10092: pszName = "WSAVERNOTSUPPORTED"; break;
130 case 10093: pszName = "WSANOTINITIALISED "; break;
131 case 11001: pszName = "WSAHOST_NOT_FOUND "; break;
132 case 11002: pszName = "WSATRY_AGAIN "; break;
133 case 11003: pszName = "WSANO_RECOVERY "; break;
134 case 11004: pszName = "WSANO_DATA "; break;
135 }
136 printf("Error 0x%x (%s)
", ErrCode, pszName);
137 }
138
139 /*****************************************************************************/
140 static void DisplaySECError(DWORD ErrCode)
141 {
142 LPCSTR pszName = NULL; // WinError.h
143
144 switch (ErrCode)
145 {
146 case SEC_E_BUFFER_TOO_SMALL:
147 pszName = "SEC_E_BUFFER_TOO_SMALL - The message buffer is too small. Used with the Digest SSP.";
148 break;
149
150 case SEC_E_CRYPTO_SYSTEM_INVALID:
151 pszName = "SEC_E_CRYPTO_SYSTEM_INVALID - The cipher chosen for the security context is not supported. Used with the Digest SSP.";
152 break;
153 case SEC_E_INCOMPLETE_MESSAGE:
154 pszName = "SEC_E_INCOMPLETE_MESSAGE - The data in the input buffer is incomplete. The application needs to read more data from the server and call DecryptMessage (General) again.";
155 break;
156
157 case SEC_E_INVALID_HANDLE:
158 pszName = "SEC_E_INVALID_HANDLE - A context handle that is not valid was specified in the phContext parameter. Used with the Digest and Schannel SSPs.";
159 break;
160
161 case SEC_E_INVALID_TOKEN:
162 pszName = "SEC_E_INVALID_TOKEN - The buffers are of the wrong type or no buffer of type SECBUFFER_DATA was found. Used with the Schannel SSP.";
163 break;
164
165 case SEC_E_MESSAGE_ALTERED:
166 pszName = "SEC_E_MESSAGE_ALTERED - The message has been altered. Used with the Digest and Schannel SSPs.";
167 break;
168
169 case SEC_E_OUT_OF_SEQUENCE:
170 pszName = "SEC_E_OUT_OF_SEQUENCE - The message was not received in the correct sequence.";
171 break;
172
173 case SEC_E_QOP_NOT_SUPPORTED:
174 pszName = "SEC_E_QOP_NOT_SUPPORTED - Neither confidentiality nor integrity are supported by the security context. Used with the Digest SSP.";
175 break;
176
177 case SEC_I_CONTEXT_EXPIRED:
178 pszName = "SEC_I_CONTEXT_EXPIRED - The message sender has finished using the connection and has initiated a shutdown.";
179 break;
180
181 case SEC_I_RENEGOTIATE:
182 pszName = "SEC_I_RENEGOTIATE - The remote party requires a new handshake sequence or the application has just initiated a shutdown.";
183 break;
184
185 case SEC_E_ENCRYPT_FAILURE:
186 pszName = "SEC_E_ENCRYPT_FAILURE - The specified data could not be encrypted.";
187 break;
188
189 case SEC_E_DECRYPT_FAILURE:
190 pszName = "SEC_E_DECRYPT_FAILURE - The specified data could not be decrypted.";
191 break;
192
193 }
194 printf("Error 0x%x %s
", ErrCode, pszName);
195 }
196
197
198
199 /*****************************************************************************/
200 static void DisplayCertChain(PCCERT_CONTEXT pServerCert, BOOL fLocal)
201 {
202 CHAR szName[1000];
203 PCCERT_CONTEXT pCurrentCert, pIssuerCert;
204 DWORD dwVerificationFlags;
205
206 printf("
");
207
208 // display leaf name
209 if (!CertNameToStr(pServerCert->dwCertEncodingType,
210 &pServerCert->pCertInfo->Subject,
211 CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
212 szName, sizeof(szName)))
213 {
214 printf("**** Error 0x%x building subject name
", GetLastError());
215 }
216
217 if (fLocal) printf("Client subject: %s
", szName);
218 else printf("Server subject: %s
", szName);
219
220 if (!CertNameToStr(pServerCert->dwCertEncodingType,
221 &pServerCert->pCertInfo->Issuer,
222 CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
223 szName, sizeof(szName)))
224 {
225 printf("**** Error 0x%x building issuer name
", GetLastError());
226 }
227
228 if (fLocal) printf("Client issuer: %s
", szName);
229 else printf("Server issuer: %s
", szName);
230
231
232 // display certificate chain
233 pCurrentCert = pServerCert;
234 while (pCurrentCert != NULL)
235 {
236 dwVerificationFlags = 0;
237 pIssuerCert = CertGetIssuerCertificateFromStore(pServerCert->hCertStore, pCurrentCert, NULL, &dwVerificationFlags);
238 if (pIssuerCert == NULL)
239 {
240 if (pCurrentCert != pServerCert) CertFreeCertificateContext(pCurrentCert);
241 break;
242 }
243
244 if (!CertNameToStr(pIssuerCert->dwCertEncodingType,
245 &pIssuerCert->pCertInfo->Subject,
246 CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
247 szName, sizeof(szName)))
248 {
249 printf("**** Error 0x%x building subject name
", GetLastError());
250 }
251
252 printf("CA subject: %s
", szName);
253
254 if (!CertNameToStr(pIssuerCert->dwCertEncodingType,
255 &pIssuerCert->pCertInfo->Issuer,
256 CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
257 szName, sizeof(szName)))
258 {
259 printf("**** Error 0x%x building issuer name
", GetLastError());
260 }
261
262 printf("CA issuer: %s
", szName);
263
264 if (pCurrentCert != pServerCert) CertFreeCertificateContext(pCurrentCert);
265 pCurrentCert = pIssuerCert;
266 pIssuerCert = NULL;
267 }
268 }
269
270 /*****************************************************************************/
271 static void DisplayConnectionInfo(CtxtHandle *phContext)
272 {
273
274 SECURITY_STATUS Status;
275 SecPkgContext_ConnectionInfo ConnectionInfo;
276
277 Status = g_pSSPI->QueryContextAttributes(phContext, SECPKG_ATTR_CONNECTION_INFO, (PVOID)&ConnectionInfo);
278 if (Status != SEC_E_OK) { printf("Error 0x%x querying connection info
", Status); return; }
279
280 printf("
");
281
282 switch (ConnectionInfo.dwProtocol)
283 {
284 case SP_PROT_TLS1_CLIENT:
285 printf("Protocol: TLS1
");
286 break;
287
288 case SP_PROT_SSL3_CLIENT:
289 printf("Protocol: SSL3
");
290 break;
291
292 case SP_PROT_PCT1_CLIENT:
293 printf("Protocol: PCT
");
294 break;
295
296 case SP_PROT_SSL2_CLIENT:
297 printf("Protocol: SSL2
");
298 break;
299
300 default:
301 printf("Protocol: 0x%x
", ConnectionInfo.dwProtocol);
302 }
303
304 switch (ConnectionInfo.aiCipher)
305 {
306 case CALG_RC4:
307 printf("Cipher: RC4
");
308 break;
309
310 case CALG_3DES:
311 printf("Cipher: Triple DES
");
312 break;
313
314 case CALG_RC2:
315 printf("Cipher: RC2
");
316 break;
317
318 case CALG_DES:
319 case CALG_CYLINK_MEK:
320 printf("Cipher: DES
");
321 break;
322
323 case CALG_SKIPJACK:
324 printf("Cipher: Skipjack
");
325 break;
326
327 default:
328 printf("Cipher: 0x%x
", ConnectionInfo.aiCipher);
329 }
330
331 printf("Cipher strength: %d
", ConnectionInfo.dwCipherStrength);
332
333 switch (ConnectionInfo.aiHash)
334 {
335 case CALG_MD5:
336 printf("Hash: MD5
");
337 break;
338
339 case CALG_SHA:
340 printf("Hash: SHA
");
341 break;
342
343 default:
344 printf("Hash: 0x%x
", ConnectionInfo.aiHash);
345 }
346
347 printf("Hash strength: %d
", ConnectionInfo.dwHashStrength);
348
349 switch (ConnectionInfo.aiExch)
350 {
351 case CALG_RSA_KEYX:
352 case CALG_RSA_SIGN:
353 printf("Key exchange: RSA
");
354 break;
355
356 case CALG_KEA_KEYX:
357 printf("Key exchange: KEA
");
358 break;
359
360 case CALG_DH_EPHEM:
361 printf("Key exchange: DH Ephemeral
");
362 break;
363
364 default:
365 printf("Key exchange: 0x%x
", ConnectionInfo.aiExch);
366 }
367
368 printf("Key exchange strength: %d
", ConnectionInfo.dwExchStrength);
369 }
370
371
372 /*****************************************************************************/
373 static void PrintHexDump(DWORD length, PBYTE buffer)
374 {
375 DWORD i, count, index;
376 CHAR rgbDigits[] = "0123456789abcdef";
377 CHAR rgbLine[100];
378 char cbLine;
379
380 for (index = 0; length; length -= count, buffer += count, index += count)
381 {
382 count = (length > 16) ? 16 : length;
383 sprintf(rgbLine, "%4.4x ", index);
384 cbLine = 6;
385
386 for (i = 0; i < count; i++)
387 {
388 rgbLine[cbLine++] = rgbDigits[buffer[i] >> 4];
389 rgbLine[cbLine++] = rgbDigits[buffer[i] & 0x0f];
390 if (i == 7) rgbLine[cbLine++] = ':';
391 else rgbLine[cbLine++] = ' ';
392 }
393 for (; i < 16; i++)
394 {
395 rgbLine[cbLine++] = ' ';
396 rgbLine[cbLine++] = ' ';
397 rgbLine[cbLine++] = ' ';
398 }
399 rgbLine[cbLine++] = ' ';
400
401 for (i = 0; i < count; i++)
402 {
403 if (buffer[i] < 32 || buffer[i] > 126 || buffer[i] == '%') rgbLine[cbLine++] = '.';
404 else rgbLine[cbLine++] = buffer[i];
405 }
406 rgbLine[cbLine++] = 0;
407 printf("%s
", rgbLine);
408 }
409 }
410
411 /*****************************************************************************/
412 static void PrintText(DWORD length, PBYTE buffer) // handle unprintable charaters
413 {
414 int i; //
415
416 printf("
"); // "length = %d bytes
", length);
417 for (i = 0; i < (int)length; i++)
418 {
419 if (buffer[i] == 10 || buffer[i] == 13)
420 printf("%c", (char)buffer[i]);
421 else if (buffer[i] < 32 || buffer[i] > 126 || buffer[i] == '%')
422 printf("%c", '.');
423 else
424 printf("%c", (char)buffer[i]);
425 }
426 printf("
");
427 }
428
429
430
431 /*****************************************************************************/
432 static void WriteDataToFile(PSTR pszData, PBYTE pbData, DWORD cbData)
433 {
434 FILE *file;
435
436 file = fopen(pszData, "wb");
437 if (file == NULL)
438 {
439 printf("**** Error opening file '%s'
", pszData); return;
440 }
441
442 if (fwrite(pbData, 1, cbData, file) != cbData)
443 {
444 printf("**** Error writing to file
"); return;
445 }
446
447 fclose(file);
448 }
449
450
451
452
453
454 /*****************************************************************************/
455 BOOL LoadSecurityLibrary(void) // load SSPI.DLL, set up a special table - PSecurityFunctionTable
456 {
457 INIT_SECURITY_INTERFACE pInitSecurityInterface;
458 // QUERY_CREDENTIALS_ATTRIBUTES_FN pQueryCredentialsAttributes;
459 OSVERSIONINFO VerInfo;
460 UCHAR lpszDLL[MAX_PATH];
461
462
463 // Find out which security DLL to use, depending on
464 // whether we are on Win2K, NT or Win9x
465 VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
466
467 if (!GetVersionEx(&VerInfo)) return FALSE;
468
469 if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && VerInfo.dwMajorVersion == 4)
470 {
471 strcpy((char*)lpszDLL, NT4_DLL_NAME); // NT4_DLL_NAME TEXT("Security.dll")
472 }
473 else if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
474 VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
475 {
476 strcpy((char*)lpszDLL, DLL_NAME); // DLL_NAME TEXT("Secur32.dll")
477 }
478 else
479 {
480 printf("System not recognized
"); return FALSE;
481 }
482
483
484 // Load Security DLL
485 g_hSecurity = LoadLibrary((char*)lpszDLL);
486 if (g_hSecurity == NULL) { printf("Error 0x%x loading %s.
", GetLastError(), lpszDLL); return FALSE; }
487
488 pInitSecurityInterface = (INIT_SECURITY_INTERFACE)GetProcAddress(g_hSecurity, "InitSecurityInterfaceA");
489 if (pInitSecurityInterface == NULL) { printf("Error 0x%x reading InitSecurityInterface entry point.
", GetLastError()); return FALSE; }
490
491 g_pSSPI = pInitSecurityInterface(); // call InitSecurityInterfaceA(void);
492 if (g_pSSPI == NULL) { printf("Error 0x%x reading security interface.
", GetLastError()); return FALSE; }
493
494 return TRUE; // and PSecurityFunctionTable
495 }
496
497
498 /*****************************************************************************/
499 void UnloadSecurityLibrary(void)
500 {
501 FreeLibrary(g_hSecurity);
502 g_hSecurity = NULL;
503 }
504
505
506 /*****************************************************************************/
507 static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PSTR pszServerName, DWORD dwCertFlags)
508 {
509 HTTPSPolicyCallbackData polHttps;
510 CERT_CHAIN_POLICY_PARA PolicyPara;
511 CERT_CHAIN_POLICY_STATUS PolicyStatus;
512 CERT_CHAIN_PARA ChainPara;
513 PCCERT_CHAIN_CONTEXT pChainContext = NULL;
514 DWORD cchServerName, Status;
515 LPSTR rgszUsages[] = { (char*)szOID_PKIX_KP_SERVER_AUTH,
516 (char*)szOID_SERVER_GATED_CRYPTO,
517 (char*)szOID_SGC_NETSCAPE };
518
519 DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
520
521 PWSTR pwszServerName = NULL;
522
523
524 if (pServerCert == NULL)
525 {
526 Status = SEC_E_WRONG_PRINCIPAL; goto cleanup;
527 }
528
529 // Convert server name to unicode.
530 if (pszServerName == NULL || strlen(pszServerName) == 0)
531 {
532 Status = SEC_E_WRONG_PRINCIPAL; goto cleanup;
533 }
534
535 cchServerName = MultiByteToWideChar(CP_ACP, 0, pszServerName, -1, NULL, 0);
536 pwszServerName = (PWSTR)LocalAlloc(LMEM_FIXED, cchServerName * sizeof(WCHAR));
537 if (pwszServerName == NULL)
538 {
539 Status = SEC_E_INSUFFICIENT_MEMORY; goto cleanup;
540 }
541
542 cchServerName = MultiByteToWideChar(CP_ACP, 0, pszServerName, -1, pwszServerName, cchServerName);
543 if (cchServerName == 0)
544 {
545 Status = SEC_E_WRONG_PRINCIPAL; goto cleanup;
546 }
547
548
549 // Build certificate chain.
550 ZeroMemory(&ChainPara, sizeof(ChainPara));
551 ChainPara.cbSize = sizeof(ChainPara);
552 ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
553 ChainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages;
554 ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
555
556 if (!CertGetCertificateChain(NULL,
557 pServerCert,
558 NULL,
559 pServerCert->hCertStore,
560 &ChainPara,
561 0,
562 NULL,
563 &pChainContext))
564 {
565 Status = GetLastError();
566 printf("Error 0x%x returned by CertGetCertificateChain!
", Status);
567 goto cleanup;
568 }
569
570
571 // Validate certificate chain.
572 ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
573 polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
574 polHttps.dwAuthType = AUTHTYPE_SERVER;
575 polHttps.fdwChecks = dwCertFlags;
576 polHttps.pwszServerName = pwszServerName;
577
578 memset(&PolicyPara, 0, sizeof(PolicyPara));
579 PolicyPara.cbSize = sizeof(PolicyPara);
580 PolicyPara.pvExtraPolicyPara = &polHttps;
581
582 memset(&PolicyStatus, 0, sizeof(PolicyStatus));
583 PolicyStatus.cbSize = sizeof(PolicyStatus);
584
585 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL,
586 pChainContext,
587 &PolicyPara,
588 &PolicyStatus))
589 {
590 Status = GetLastError();
591 printf("Error 0x%x returned by CertVerifyCertificateChainPolicy!
", Status);
592 goto cleanup;
593 }
594
595 if (PolicyStatus.dwError)
596 {
597 Status = PolicyStatus.dwError;
598 DisplayWinVerifyTrustError(Status);
599 goto cleanup;
600 }
601
602 Status = SEC_E_OK;
603
604
605 cleanup:
606 if (pChainContext) CertFreeCertificateChain(pChainContext);
607 if (pwszServerName) LocalFree(pwszServerName);
608
609 return Status;
610 }
611
612
613 /*****************************************************************************/
614 static SECURITY_STATUS CreateCredentials(LPSTR pszUser, PCredHandle phCreds)
615 { // in out
616 TimeStamp tsExpiry;
617 SECURITY_STATUS Status;
618 DWORD cSupportedAlgs = 0;
619 ALG_ID rgbSupportedAlgs[16];
620 PCCERT_CONTEXT pCertContext = NULL;
621
622
623 // Open the "MY" certificate store, where IE stores client certificates.
624 // Windows maintains 4 stores -- MY, CA, ROOT, SPC.
625 if (hMyCertStore == NULL)
626 {
627 hMyCertStore = CertOpenSystemStore(0, "MY");
628 if (!hMyCertStore)
629 {
630 printf("**** Error 0x%x returned by CertOpenSystemStore
", GetLastError());
631 return SEC_E_NO_CREDENTIALS;
632 }
633 }
634
635
636 // If a user name is specified, then attempt to find a client
637 // certificate. Otherwise, just create a NULL credential.
638 if (pszUser)
639 {
640 // Find client certificate. Note that this sample just searches for a
641 // certificate that contains the user name somewhere in the subject name.
642 // A real application should be a bit less casual.
643 pCertContext = CertFindCertificateInStore(hMyCertStore, // hCertStore
644 X509_ASN_ENCODING, // dwCertEncodingType
645 0, // dwFindFlags
646 CERT_FIND_SUBJECT_STR_A,// dwFindType
647 pszUser, // *pvFindPara
648 NULL); // pPrevCertContext
649
650
651 if (pCertContext == NULL)
652 {
653 printf("**** Error 0x%x returned by CertFindCertificateInStore
", GetLastError());
654 if (GetLastError() == CRYPT_E_NOT_FOUND) printf("CRYPT_E_NOT_FOUND - property doesn't exist
");
655 return SEC_E_NO_CREDENTIALS;
656 }
657 }
658
659
660 // Build Schannel credential structure. Currently, this sample only
661 // specifies the protocol to be used (and optionally the certificate,
662 // of course). Real applications may wish to specify other parameters as well.
663 ZeroMemory(&SchannelCred, sizeof(SchannelCred));
664
665 SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
666 if (pCertContext)
667 {
668 SchannelCred.cCreds = 1;
669 SchannelCred.paCred = &pCertContext;
670 }
671
672 SchannelCred.grbitEnabledProtocols = dwProtocol;
673
674 if (aiKeyExch) rgbSupportedAlgs[cSupportedAlgs++] = aiKeyExch;
675
676 if (cSupportedAlgs)
677 {
678 SchannelCred.cSupportedAlgs = cSupportedAlgs;
679 SchannelCred.palgSupportedAlgs = rgbSupportedAlgs;
680 }
681
682 SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
683
684 // The SCH_CRED_MANUAL_CRED_VALIDATION flag is specified because
685 // this sample verifies the server certificate manually.
686 // Applications that expect to run on WinNT, Win9x, or WinME
687 // should specify this flag and also manually verify the server
688 // certificate. Applications running on newer versions of Windows can
689 // leave off this flag, in which case the InitializeSecurityContext
690 // function will validate the server certificate automatically.
691 SchannelCred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
692
693
694 // Create an SSPI credential.
695 Status = g_pSSPI->AcquireCredentialsHandleA(NULL, // Name of principal
696 (char*)UNISP_NAME_A, // Name of package
697 SECPKG_CRED_OUTBOUND, // Flags indicating use
698 NULL, // Pointer to logon ID
699 &SchannelCred, // Package specific data
700 NULL, // Pointer to GetKey() func
701 NULL, // Value to pass to GetKey()
702 phCreds, // (out) Cred Handle
703 &tsExpiry); // (out) Lifetime (optional)
704
705 if (Status != SEC_E_OK) printf("**** Error 0x%x returned by AcquireCredentialsHandle
", Status);
706
707 // cleanup: Free the certificate context. Schannel has already made its own copy.
708 if (pCertContext) CertFreeCertificateContext(pCertContext);
709
710 return Status;
711 }
712
713 /*****************************************************************************/
714 static INT ConnectToServer(LPSTR pszServerName, INT iPortNumber, SOCKET * pSocket)
715 { // in in out
716 SOCKET Socket;
717 struct sockaddr_in sin;
718 struct hostent *hp;
719
720
721 Socket = socket(PF_INET, SOCK_STREAM, 0);
722 if (Socket == INVALID_SOCKET)
723 {
724 printf("**** Error %d creating socket
", WSAGetLastError());
725 DisplayWinSockError(WSAGetLastError());
726 return WSAGetLastError();
727 }
728
729
730 if (fUseProxy)
731 {
732 sin.sin_family = AF_INET;
733 sin.sin_port = ntohs((u_short)iProxyPort);
734 if ((hp = gethostbyname(pszProxyServer)) == NULL)
735 {
736 printf("**** Error %d returned by gethostbyname using Proxy
", WSAGetLastError());
737 DisplayWinSockError(WSAGetLastError());
738 return WSAGetLastError();
739 }
740 else
741 memcpy(&sin.sin_addr, hp->h_addr, 4);
742 }
743
744 else // No proxy used
745 {
746 sin.sin_family = AF_INET;
747 sin.sin_port = htons((u_short)iPortNumber);
748 if ((hp = gethostbyname(pszServerName)) == NULL)
749 {
750 printf("**** Error returned by gethostbyname
");
751 DisplayWinSockError(WSAGetLastError());
752 return WSAGetLastError();
753 }
754 else
755 memcpy(&sin.sin_addr, hp->h_addr, 4);
756 }
757
758
759 if (connect(Socket, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
760 {
761 printf("**** Error %d connecting to "%s" (%s)
", WSAGetLastError(), pszServerName, inet_ntoa(sin.sin_addr));
762 closesocket(Socket);
763 DisplayWinSockError(WSAGetLastError());
764 return WSAGetLastError();
765 }
766
767
768 if (fUseProxy)
769 {
770 BYTE pbMessage[200];
771 DWORD cbMessage;
772
773 // Build message for proxy server
774 strcpy((char*)pbMessage, "CONNECT ");
775 strcat((char*)pbMessage, pszServerName);
776 strcat((char*)pbMessage, ":");
777 _itoa(iPortNumber, (char*)pbMessage + strlen((char*)pbMessage), 10);
778 strcat((char*)pbMessage, " HTTP/1.0
User-Agent: webclient
");
779 cbMessage = (DWORD)strlen((char*)pbMessage);
780
781 // Send message to proxy server
782 if (send(Socket, (char*)pbMessage, cbMessage, 0) == SOCKET_ERROR)
783 {
784 printf("**** Error %d sending message to proxy!
", WSAGetLastError());
785 DisplayWinSockError(WSAGetLastError());
786 return WSAGetLastError();
787 }
788
789 // Receive message from proxy server
790 cbMessage = recv(Socket, (char*)pbMessage, 200, 0);
791 if (cbMessage == SOCKET_ERROR)
792 {
793 printf("**** Error %d receiving message from proxy
", WSAGetLastError());
794 DisplayWinSockError(WSAGetLastError());
795 return WSAGetLastError();
796 }
797 // this sample is limited but in normal use it
798 // should continue to receive until CR LF CR LF is received
799 }
800 *pSocket = Socket;
801
802 return SEC_E_OK;
803 }
804
805 /*****************************************************************************/
806 static LONG DisconnectFromServer(SOCKET Socket, PCredHandle phCreds, CtxtHandle * phContext)
807 {
808 PBYTE pbMessage;
809 DWORD dwType, dwSSPIFlags, dwSSPIOutFlags, cbMessage, cbData, Status;
810 SecBufferDesc OutBuffer;
811 SecBuffer OutBuffers[1];
812 TimeStamp tsExpiry;
813
814
815 dwType = SCHANNEL_SHUTDOWN; // Notify schannel that we are about to close the connection.
816
817 OutBuffers[0].pvBuffer = &dwType;
818 OutBuffers[0].BufferType = SECBUFFER_TOKEN;
819 OutBuffers[0].cbBuffer = sizeof(dwType);
820
821 OutBuffer.cBuffers = 1;
822 OutBuffer.pBuffers = OutBuffers;
823 OutBuffer.ulVersion = SECBUFFER_VERSION;
824
825 Status = g_pSSPI->ApplyControlToken(phContext, &OutBuffer);
826 if (FAILED(Status)) { printf("**** Error 0x%x returned by ApplyControlToken
", Status); goto cleanup; }
827
828
829 // Build an SSL close notify message.
830 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
831 ISC_REQ_REPLAY_DETECT |
832 ISC_REQ_CONFIDENTIALITY |
833 ISC_RET_EXTENDED_ERROR |
834 ISC_REQ_ALLOCATE_MEMORY |
835 ISC_REQ_STREAM;
836
837 OutBuffers[0].pvBuffer = NULL;
838 OutBuffers[0].BufferType = SECBUFFER_TOKEN;
839 OutBuffers[0].cbBuffer = 0;
840
841 OutBuffer.cBuffers = 1;
842 OutBuffer.pBuffers = OutBuffers;
843 OutBuffer.ulVersion = SECBUFFER_VERSION;
844
845 Status = g_pSSPI->InitializeSecurityContextA(phCreds,
846 phContext,
847 NULL,
848 dwSSPIFlags,
849 0,
850 SECURITY_NATIVE_DREP,
851 NULL,
852 0,
853 phContext,
854 &OutBuffer,
855 &dwSSPIOutFlags,
856 &tsExpiry);
857
858 if (FAILED(Status)) { printf("**** Error 0x%x returned by InitializeSecurityContext
", Status); goto cleanup; }
859
860 pbMessage = (PBYTE)OutBuffers[0].pvBuffer;
861 cbMessage = OutBuffers[0].cbBuffer;
862
863
864 // Send the close notify message to the server.
865 if (pbMessage != NULL && cbMessage != 0)
866 {
867 cbData = send(Socket, (char*)pbMessage, cbMessage, 0);
868 if (cbData == SOCKET_ERROR || cbData == 0)
869 {
870 Status = WSAGetLastError();
871 printf("**** Error %d sending close notify
", Status);
872 DisplayWinSockError(WSAGetLastError());
873 goto cleanup;
874 }
875 printf("Sending Close Notify
");
876 printf("%d bytes of handshake data sent
", cbData);
877 if (fVerbose) { PrintHexDump(cbData, pbMessage); printf("
"); }
878 g_pSSPI->FreeContextBuffer(pbMessage); // Free output buffer.
879 }
880
881
882 cleanup:
883 g_pSSPI->DeleteSecurityContext(phContext); // Free the security context.
884 closesocket(Socket); // Close the socket.
885
886 return Status;
887 }
888
889
890
891 /*****************************************************************************/
892 static void GetNewClientCredentials(CredHandle *phCreds, CtxtHandle *phContext)
893 {
894
895 CredHandle hCreds;
896 SecPkgContext_IssuerListInfoEx IssuerListInfo;
897 PCCERT_CHAIN_CONTEXT pChainContext;
898 CERT_CHAIN_FIND_BY_ISSUER_PARA FindByIssuerPara;
899 PCCERT_CONTEXT pCertContext;
900 TimeStamp tsExpiry;
901 SECURITY_STATUS Status;
902
903
904 // Read list of trusted issuers from schannel.
905 Status = g_pSSPI->QueryContextAttributes(phContext, SECPKG_ATTR_ISSUER_LIST_EX, (PVOID)&IssuerListInfo);
906 if (Status != SEC_E_OK) { printf("Error 0x%x querying issuer list info
", Status); return; }
907
908 // Enumerate the client certificates.
909 ZeroMemory(&FindByIssuerPara, sizeof(FindByIssuerPara));
910
911 FindByIssuerPara.cbSize = sizeof(FindByIssuerPara);
912 FindByIssuerPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
913 FindByIssuerPara.dwKeySpec = 0;
914 FindByIssuerPara.cIssuer = IssuerListInfo.cIssuers;
915 FindByIssuerPara.rgIssuer = IssuerListInfo.aIssuers;
916
917 pChainContext = NULL;
918
919 while (TRUE)
920 { // Find a certificate chain.
921 pChainContext = CertFindChainInStore(hMyCertStore,
922 X509_ASN_ENCODING,
923 0,
924 CERT_CHAIN_FIND_BY_ISSUER,
925 &FindByIssuerPara,
926 pChainContext);
927 if (pChainContext == NULL) { printf("Error 0x%x finding cert chain
", GetLastError()); break; }
928
929 printf("
certificate chain found
");
930
931 // Get pointer to leaf certificate context.
932 pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext;
933
934 // Create schannel credential.
935 SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
936 SchannelCred.cCreds = 1;
937 SchannelCred.paCred = &pCertContext;
938
939 Status = g_pSSPI->AcquireCredentialsHandleA(NULL, // Name of principal
940 (char*)UNISP_NAME_A, // Name of package
941 SECPKG_CRED_OUTBOUND, // Flags indicating use
942 NULL, // Pointer to logon ID
943 &SchannelCred, // Package specific data
944 NULL, // Pointer to GetKey() func
945 NULL, // Value to pass to GetKey()
946 &hCreds, // (out) Cred Handle
947 &tsExpiry); // (out) Lifetime (optional)
948
949 if (Status != SEC_E_OK) { printf("**** Error 0x%x returned by AcquireCredentialsHandle
", Status); continue; }
950
951 printf("
new schannel credential created
");
952
953 g_pSSPI->FreeCredentialsHandle(phCreds); // Destroy the old credentials.
954
955 *phCreds = hCreds;
956
957 }
958 }
959
960 /*****************************************************************************/
961 static SECURITY_STATUS ClientHandshakeLoop(SOCKET Socket, // in
962 PCredHandle phCreds, // in
963 CtxtHandle * phContext, // in, out
964 BOOL fDoInitialRead, // in
965 SecBuffer * pExtraData) // out
966
967 {
968
969 SecBufferDesc OutBuffer, InBuffer;
970 SecBuffer InBuffers[2], OutBuffers[1];
971 DWORD dwSSPIFlags, dwSSPIOutFlags, cbData, cbIoBuffer;
972 TimeStamp tsExpiry;
973 SECURITY_STATUS scRet;
974 PUCHAR IoBuffer;
975 BOOL fDoRead;
976
977
978 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY |
979 ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
980
981
982 // Allocate data buffer.
983 IoBuffer = (PUCHAR)LocalAlloc(LMEM_FIXED, IO_BUFFER_SIZE);
984 if (IoBuffer == NULL) { printf("**** Out of memory (1)
"); return SEC_E_INTERNAL_ERROR; }
985 cbIoBuffer = 0;
986 fDoRead = fDoInitialRead;
987
988
989
990 // Loop until the handshake is finished or an error occurs.
991 scRet = SEC_I_CONTINUE_NEEDED;
992
993 while (scRet == SEC_I_CONTINUE_NEEDED ||
994 scRet == SEC_E_INCOMPLETE_MESSAGE ||
995 scRet == SEC_I_INCOMPLETE_CREDENTIALS)
996 {
997 if (0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) // Read data from server.
998 {
999 if (fDoRead)
1000 {
1001 cbData = recv(Socket, (char*)IoBuffer + cbIoBuffer, IO_BUFFER_SIZE - cbIoBuffer, 0);
1002 if (cbData == SOCKET_ERROR)
1003 {
1004 printf("**** Error %d reading data from server
", WSAGetLastError());
1005 scRet = SEC_E_INTERNAL_ERROR;
1006 break;
1007 }
1008 else if (cbData == 0)
1009 {
1010 printf("**** Server unexpectedly disconnected
");
1011 scRet = SEC_E_INTERNAL_ERROR;
1012 break;
1013 }
1014 printf("%d bytes of handshake data received
", cbData);
1015 if (fVerbose) { PrintHexDump(cbData, IoBuffer + cbIoBuffer); printf("
"); }
1016 cbIoBuffer += cbData;
1017 }
1018 else
1019 fDoRead = TRUE;
1020 }
1021
1022
1023
1024 // Set up the input buffers. Buffer 0 is used to pass in data
1025 // received from the server. Schannel will consume some or all
1026 // of this. Leftover data (if any) will be placed in buffer 1 and
1027 // given a buffer type of SECBUFFER_EXTRA.
1028 InBuffers[0].pvBuffer = IoBuffer;
1029 InBuffers[0].cbBuffer = cbIoBuffer;
1030 InBuffers[0].BufferType = SECBUFFER_TOKEN;
1031
1032 InBuffers[1].pvBuffer = NULL;
1033 InBuffers[1].cbBuffer = 0;
1034 InBuffers[1].BufferType = SECBUFFER_EMPTY;
1035
1036 InBuffer.cBuffers = 2;
1037 InBuffer.pBuffers = InBuffers;
1038 InBuffer.ulVersion = SECBUFFER_VERSION;
1039
1040
1041 // Set up the output buffers. These are initialized to NULL
1042 // so as to make it less likely we'll attempt to free random
1043 // garbage later.
1044 OutBuffers[0].pvBuffer = NULL;
1045 OutBuffers[0].BufferType = SECBUFFER_TOKEN;
1046 OutBuffers[0].cbBuffer = 0;
1047
1048 OutBuffer.cBuffers = 1;
1049 OutBuffer.pBuffers = OutBuffers;
1050 OutBuffer.ulVersion = SECBUFFER_VERSION;
1051
1052
1053 // Call InitializeSecurityContext.
1054 scRet = g_pSSPI->InitializeSecurityContextA(phCreds,
1055 phContext,
1056 NULL,
1057 dwSSPIFlags,
1058 0,
1059 SECURITY_NATIVE_DREP,
1060 &InBuffer,
1061 0,
1062 NULL,
1063 &OutBuffer,
1064 &dwSSPIOutFlags,
1065 &tsExpiry);
1066
1067
1068 // If InitializeSecurityContext was successful (or if the error was
1069 // one of the special extended ones), send the contends of the output
1070 // buffer to the server.
1071 if (scRet == SEC_E_OK ||
1072 scRet == SEC_I_CONTINUE_NEEDED ||
1073 FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
1074 {
1075 if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
1076 {
1077 cbData = send(Socket, (char*)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
1078 if (cbData == SOCKET_ERROR || cbData == 0)
1079 {
1080 printf("**** Error %d sending data to server (2)
", WSAGetLastError());
1081 DisplayWinSockError(WSAGetLastError());
1082 g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
1083 g_pSSPI->DeleteSecurityContext(phContext);
1084 return SEC_E_INTERNAL_ERROR;
1085 }
1086 printf("%d bytes of handshake data sent
", cbData);
1087 if (fVerbose) { PrintHexDump(cbData, (PBYTE)OutBuffers[0].pvBuffer); printf("
"); }
1088
1089 // Free output buffer.
1090 g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
1091 OutBuffers[0].pvBuffer = NULL;
1092 }
1093 }
1094
1095
1096
1097 // If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
1098 // then we need to read more data from the server and try again.
1099 if (scRet == SEC_E_INCOMPLETE_MESSAGE) continue;
1100
1101
1102 // If InitializeSecurityContext returned SEC_E_OK, then the
1103 // handshake completed successfully.
1104 if (scRet == SEC_E_OK)
1105 {
1106 // If the "extra" buffer contains data, this is encrypted application
1107 // protocol layer stuff. It needs to be saved. The application layer
1108 // will later decrypt it with DecryptMessage.
1109 printf("Handshake was successful
");
1110
1111 if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
1112 {
1113 pExtraData->pvBuffer = LocalAlloc(LMEM_FIXED, InBuffers[1].cbBuffer);
1114 if (pExtraData->pvBuffer == NULL) { printf("**** Out of memory (2)
"); return SEC_E_INTERNAL_ERROR; }
1115
1116 MoveMemory(pExtraData->pvBuffer,
1117 IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer),
1118 InBuffers[1].cbBuffer);
1119
1120 pExtraData->cbBuffer = InBuffers[1].cbBuffer;
1121 pExtraData->BufferType = SECBUFFER_TOKEN;
1122
1123 printf("%d bytes of app data was bundled with handshake data
", pExtraData->cbBuffer);
1124 }
1125 else
1126 {
1127 pExtraData->pvBuffer = NULL;
1128 pExtraData->cbBuffer = 0;
1129 pExtraData->BufferType = SECBUFFER_EMPTY;
1130 }
1131 break; // Bail out to quit
1132 }
1133
1134
1135
1136 // Check for fatal error.
1137 if (FAILED(scRet)) { printf("**** Error 0x%x returned by InitializeSecurityContext (2)
", scRet); break; }
1138
1139 // If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
1140 // then the server just requested client authentication.
1141 if (scRet == SEC_I_INCOMPLETE_CREDENTIALS)
1142 {
1143 // Busted. The server has requested client authentication and
1144 // the credential we supplied didn't contain a client certificate.
1145 // This function will read the list of trusted certificate
1146 // authorities ("issuers") that was received from the server
1147 // and attempt to find a suitable client certificate that
1148 // was issued by one of these. If this function is successful,
1149 // then we will connect using the new certificate. Otherwise,
1150 // we will attempt to connect anonymously (using our current credentials).
1151 GetNewClientCredentials(phCreds, phContext);
1152
1153 // Go around again.
1154 fDoRead = FALSE;
1155 scRet = SEC_I_CONTINUE_NEEDED;
1156 continue;
1157 }
1158
1159 // Copy any leftover data from the "extra" buffer, and go around again.
1160 if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
1161 {
1162 MoveMemory(IoBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer);
1163 cbIoBuffer = InBuffers[1].cbBuffer;
1164 }
1165 else
1166 cbIoBuffer = 0;
1167 }
1168
1169 // Delete the security context in the case of a fatal error.
1170 if (FAILED(scRet)) g_pSSPI->DeleteSecurityContext(phContext);
1171 LocalFree(IoBuffer);
1172
1173 return scRet;
1174 }
1175
1176
1177 /*****************************************************************************/
1178 static SECURITY_STATUS PerformClientHandshake(SOCKET Socket, // in
1179 PCredHandle phCreds, // in
1180 LPSTR pszServerName, // in
1181 CtxtHandle * phContext, // out
1182 SecBuffer * pExtraData) // out
1183 {
1184
1185 SecBufferDesc OutBuffer;
1186 SecBuffer OutBuffers[1];
1187 DWORD dwSSPIFlags, dwSSPIOutFlags, cbData;
1188 TimeStamp tsExpiry;
1189 SECURITY_STATUS scRet;
1190
1191
1192 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY |
1193 ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
1194
1195
1196 // Initiate a ClientHello message and generate a token.
1197 OutBuffers[0].pvBuffer = NULL;
1198 OutBuffers[0].BufferType = SECBUFFER_TOKEN;
1199 OutBuffers[0].cbBuffer = 0;
1200
1201 OutBuffer.cBuffers = 1;
1202 OutBuffer.pBuffers = OutBuffers;
1203 OutBuffer.ulVersion = SECBUFFER_VERSION;
1204
1205 scRet = g_pSSPI->InitializeSecurityContextA(phCreds,
1206 NULL,
1207 pszServerName,
1208 dwSSPIFlags,
1209 0,
1210 SECURITY_NATIVE_DREP,
1211 NULL,
1212 0,
1213 phContext,
1214 &OutBuffer,
1215 &dwSSPIOutFlags,
1216 &tsExpiry);
1217
1218 if (scRet != SEC_I_CONTINUE_NEEDED) { printf("**** Error %d returned by InitializeSecurityContext (1)
", scRet); return scRet; }
1219
1220 // Send response to server if there is one.
1221 if (OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
1222 {
1223 cbData = send(Socket, (CHAR*)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
1224 if (cbData == SOCKET_ERROR || cbData == 0)
1225 {
1226 printf("**** Error %d sending data to server (1)
", WSAGetLastError());
1227 g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
1228 g_pSSPI->DeleteSecurityContext(phContext);
1229 return SEC_E_INTERNAL_ERROR;
1230 }
1231 printf("%d bytes of handshake data sent
", cbData);
1232 if (fVerbose) { PrintHexDump(cbData, (PBYTE)OutBuffers[0].pvBuffer); printf("
"); }
1233 g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer); // Free output buffer.
1234 OutBuffers[0].pvBuffer = NULL;
1235 }
1236
1237 return ClientHandshakeLoop(Socket, phCreds, phContext, TRUE, pExtraData);
1238 }
1239
1240
1241
1242 /*****************************************************************************/
1243 static DWORD EncryptSend(SOCKET Socket, CtxtHandle * phContext, PBYTE pbIoBuffer, SecPkgContext_StreamSizes Sizes)
1244 // http://msdn.microsoft.com/en-us/library/aa375378(VS.85).aspx
1245 // The encrypted message is encrypted in place, overwriting the original contents of its buffer.
1246 {
1247 SECURITY_STATUS scRet; // unsigned long cbBuffer; // Size of the buffer, in bytes
1248 SecBufferDesc Message; // unsigned long BufferType; // Type of the buffer (below)
1249 SecBuffer Buffers[4]; // void SEC_FAR * pvBuffer; // Pointer to the buffer
1250 DWORD cbMessage, cbData;
1251 PBYTE pbMessage;
1252
1253
1254 pbMessage = pbIoBuffer + Sizes.cbHeader; // Offset by "header size"
1255 cbMessage = (DWORD)strlen((const char*)pbMessage);
1256 printf("Sending %d bytes of plaintext:", cbMessage); PrintText(cbMessage, pbMessage);
1257 if (fVerbose) { PrintHexDump(cbMessage, pbMessage); printf("
"); }
1258
1259
1260 // Encrypt the HTTP request.
1261 Buffers[0].pvBuffer = pbIoBuffer; // Pointer to buffer 1
1262 Buffers[0].cbBuffer = Sizes.cbHeader; // length of header
1263 Buffers[0].BufferType = SECBUFFER_STREAM_HEADER; // Type of the buffer
1264
1265 Buffers[1].pvBuffer = pbMessage; // Pointer to buffer 2
1266 Buffers[1].cbBuffer = cbMessage; // length of the message
1267 Buffers[1].BufferType = SECBUFFER_DATA; // Type of the buffer
1268
1269 Buffers[2].pvBuffer = pbMessage + cbMessage; // Pointer to buffer 3
1270 Buffers[2].cbBuffer = Sizes.cbTrailer; // length of the trailor
1271 Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER; // Type of the buffer
1272
1273 Buffers[3].pvBuffer = SECBUFFER_EMPTY; // Pointer to buffer 4
1274 Buffers[3].cbBuffer = SECBUFFER_EMPTY; // length of buffer 4
1275 Buffers[3].BufferType = SECBUFFER_EMPTY; // Type of the buffer 4
1276
1277
1278 Message.ulVersion = SECBUFFER_VERSION; // Version number
1279 Message.cBuffers = 4; // Number of buffers - must contain four SecBuffer structures.
1280 Message.pBuffers = Buffers; // Pointer to array of buffers
1281 scRet = g_pSSPI->EncryptMessage(phContext, 0, &Message, 0); // must contain four SecBuffer structures.
1282 if (FAILED(scRet)) { printf("**** Error 0x%x returned by EncryptMessage
", scRet); return scRet; }
1283
1284
1285 // Send the encrypted data to the server. len flags
1286 cbData = send(Socket, (const char*)pbIoBuffer, Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer, 0);
1287
1288 printf("%d bytes of encrypted data sent
", cbData);
1289 if (fVerbose) { PrintHexDump(cbData, pbIoBuffer); printf("
"); }
1290
1291 return cbData; // send( Socket, pbIoBuffer, Sizes.cbHeader + strlen(pbMessage) + Sizes.cbTrailer, 0 );
1292
1293 }
1294
1295
1296 /*****************************************************************************/
1297 static SECURITY_STATUS ReadDecrypt(SOCKET Socket, PCredHandle phCreds, CtxtHandle * phContext, PBYTE pbIoBuffer, DWORD cbIoBufferLength)
1298
1299 // calls recv() - blocking socket read
1300 // http://msdn.microsoft.com/en-us/library/ms740121(VS.85).aspx
1301
1302 // The encrypted message is decrypted in place, overwriting the original contents of its buffer.
1303 // http://msdn.microsoft.com/en-us/library/aa375211(VS.85).aspx
1304
1305 {
1306 SecBuffer ExtraBuffer;
1307 SecBuffer *pDataBuffer, *pExtraBuffer;
1308
1309 SECURITY_STATUS scRet; // unsigned long cbBuffer; // Size of the buffer, in bytes
1310 SecBufferDesc Message; // unsigned long BufferType; // Type of the buffer (below)
1311 SecBuffer Buffers[4]; // void SEC_FAR * pvBuffer; // Pointer to the buffer
1312
1313 DWORD cbIoBuffer, cbData, length;
1314 PBYTE buff;
1315 int i;
1316
1317
1318
1319 // Read data from server until done.
1320 cbIoBuffer = 0;
1321 scRet = 0;
1322 while (TRUE) // Read some data.
1323 {
1324 if (cbIoBuffer == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE) // get the data
1325 {
1326 cbData = recv(Socket, (char*)pbIoBuffer + cbIoBuffer, cbIoBufferLength - cbIoBuffer, 0);
1327 if (cbData == SOCKET_ERROR)
1328 {
1329 printf("**** Error %d reading data from server
", WSAGetLastError());
1330 scRet = SEC_E_INTERNAL_ERROR;
1331 break;
1332 }
1333 else if (cbData == 0) // Server disconnected.
1334 {
1335 if (cbIoBuffer)
1336 {
1337 printf("**** Server unexpectedly disconnected
");
1338 scRet = SEC_E_INTERNAL_ERROR;
1339 return scRet;
1340 }
1341 else
1342 break; // All Done
1343 }
1344 else // success
1345 {
1346 printf("%d bytes of (encrypted) application data received
", cbData);
1347 if (fVerbose) { PrintHexDump(cbData, pbIoBuffer + cbIoBuffer); printf("
"); }
1348 cbIoBuffer += cbData;
1349 }
1350 }
1351
1352
1353 // Decrypt the received data.
1354 Buffers[0].pvBuffer = pbIoBuffer;
1355 Buffers[0].cbBuffer = cbIoBuffer;
1356 Buffers[0].BufferType = SECBUFFER_DATA; // Initial Type of the buffer 1
1357 Buffers[1].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 2
1358 Buffers[2].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 3
1359 Buffers[3].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 4
1360
1361 Message.ulVersion = SECBUFFER_VERSION; // Version number
1362 Message.cBuffers = 4; // Number of buffers - must contain four SecBuffer structures.
1363 Message.pBuffers = Buffers; // Pointer to array of buffers
1364 scRet = g_pSSPI->DecryptMessage(phContext, &Message, 0, NULL);
1365 if (scRet == SEC_I_CONTEXT_EXPIRED) break; // Server signalled end of session
1366 // if( scRet == SEC_E_INCOMPLETE_MESSAGE - Input buffer has partial encrypted record, read more
1367 if (scRet != SEC_E_OK &&
1368 scRet != SEC_I_RENEGOTIATE &&
1369 scRet != SEC_I_CONTEXT_EXPIRED)
1370 {
1371 printf("**** DecryptMessage ");
1372 DisplaySECError((DWORD)scRet);
1373 return scRet;
1374 }
1375
1376
1377
1378 // Locate data and (optional) extra buffers.
1379 pDataBuffer = NULL;
1380 pExtraBuffer = NULL;
1381 for (i = 1; i < 4; i++)
1382 {
1383 if (pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA) pDataBuffer = &Buffers[i];
1384 if (pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA) pExtraBuffer = &Buffers[i];
1385 }
1386
1387
1388 // Display the decrypted data.
1389 if (pDataBuffer)
1390 {
1391 length = pDataBuffer->cbBuffer;
1392 if (length) // check if last two chars are CR LF
1393 {
1394 buff = (PBYTE)pDataBuffer->pvBuffer; // printf( "n-2= %d, n-1= %d
", buff[length-2], buff[length-1] );
1395 printf("Decrypted data: %d bytes", length); PrintText(length, buff);
1396 if (fVerbose) { PrintHexDump(length, buff); printf("
"); }
1397 if (buff[length - 2] == 13 && buff[length - 1] == 10) break; // printf("Found CRLF
");
1398 }
1399 }
1400
1401
1402
1403 // Move any "extra" data to the input buffer.
1404 if (pExtraBuffer)
1405 {
1406 MoveMemory(pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
1407 cbIoBuffer = pExtraBuffer->cbBuffer; // printf("cbIoBuffer= %d
", cbIoBuffer);
1408 }
1409 else
1410 cbIoBuffer = 0;
1411
1412
1413 // The server wants to perform another handshake sequence.
1414 if (scRet == SEC_I_RENEGOTIATE)
1415 {
1416 printf("Server requested renegotiate!
");
1417 scRet = ClientHandshakeLoop(Socket, phCreds, phContext, FALSE, &ExtraBuffer);
1418 if (scRet != SEC_E_OK) return scRet;
1419
1420 if (ExtraBuffer.pvBuffer) // Move any "extra" data to the input buffer.
1421 {
1422 MoveMemory(pbIoBuffer, ExtraBuffer.pvBuffer, ExtraBuffer.cbBuffer);
1423 cbIoBuffer = ExtraBuffer.cbBuffer;
1424 }
1425 }
1426 } // Loop till CRLF is found at the end of the data
1427
1428 return SEC_E_OK;
1429 }
1430
1431
1432
1433 /*****************************************************************************/
1434 static SECURITY_STATUS SMTPsession(SOCKET Socket, // in
1435 PCredHandle phCreds, // in
1436 CtxtHandle * phContext) // in
1437 {
1438 SecPkgContext_StreamSizes Sizes; // unsigned long cbBuffer; // Size of the buffer, in bytes
1439 SECURITY_STATUS scRet; // unsigned long BufferType; // Type of the buffer (below)
1440 PBYTE pbIoBuffer; // void SEC_FAR * pvBuffer; // Pointer to the buffer
1441 DWORD cbIoBufferLength, cbData;
1442
1443
1444 // Read stream encryption properties.
1445 scRet = g_pSSPI->QueryContextAttributes(phContext, SECPKG_ATTR_STREAM_SIZES, &Sizes);
1446 if (scRet != SEC_E_OK)
1447 {
1448 printf("**** Error 0x%x reading SECPKG_ATTR_STREAM_SIZES
", scRet); return scRet;
1449 }
1450
1451
1452 // Create a buffer.
1453 cbIoBufferLength = Sizes.cbHeader + Sizes.cbMaximumMessage + Sizes.cbTrailer;
1454 pbIoBuffer = (PBYTE)LocalAlloc(LMEM_FIXED, cbIoBufferLength);
1455 if (pbIoBuffer == NULL) { printf("**** Out of memory (2)
"); return SEC_E_INTERNAL_ERROR; }
1456
1457
1458 // Receive a Response
1459 scRet = ReadDecrypt(Socket, phCreds, phContext, pbIoBuffer, cbIoBufferLength);
1460 if (scRet != SEC_E_OK) return scRet;
1461
1462
1463 // Build the request - must be < maximum message size
1464 sprintf((char*)pbIoBuffer + Sizes.cbHeader, "%s", "EHLO
"); // message begins after the header
1465
1466
1467 // Send a request.
1468 cbData = EncryptSend(Socket, phContext, pbIoBuffer, Sizes);
1469 if (cbData == SOCKET_ERROR || cbData == 0)
1470 {
1471 printf("**** Error %d sending data to server (3)
", WSAGetLastError()); return SEC_E_INTERNAL_ERROR;
1472 }
1473
1474
1475 // Receive a Response
1476 scRet = ReadDecrypt(Socket, phCreds, phContext, pbIoBuffer, cbIoBufferLength);
1477 if (scRet != SEC_E_OK) return scRet;
1478
1479
1480
1481
1482 // Build the request - must be < maximum message size
1483 sprintf((char*)pbIoBuffer + Sizes.cbHeader, "%s", "QUIT
"); // message begins after the header
1484
1485
1486 // Send a request.
1487 cbData = EncryptSend(Socket, phContext, pbIoBuffer, Sizes);
1488 if (cbData == SOCKET_ERROR || cbData == 0)
1489 {
1490 printf("**** Error %d sending data to server (3)
", WSAGetLastError()); return SEC_E_INTERNAL_ERROR;
1491 }
1492
1493
1494 // Receive a Response
1495 scRet = ReadDecrypt(Socket, phCreds, phContext, pbIoBuffer, cbIoBufferLength);
1496 if (scRet != SEC_E_OK) return scRet;
1497
1498
1499 return SEC_E_OK;
1500 }
1501
1502
1503 /*****************************************************************************/
1504 void _cdecl main(int argc, char *argv[])
1505 {
1506 WSADATA WsaData;
1507 SOCKET Socket = INVALID_SOCKET;
1508
1509 CredHandle hClientCreds;
1510 CtxtHandle hContext;
1511 BOOL fCredsInitialized = FALSE;
1512 BOOL fContextInitialized = FALSE;
1513
1514 SecBuffer ExtraData;
1515 SECURITY_STATUS Status;
1516
1517 PCCERT_CONTEXT pRemoteCertContext = NULL;
1518
1519
1520
1521 if (!LoadSecurityLibrary())
1522 {
1523 printf("Error initializing the security library
"); goto cleanup;
1524 } //
1525 printf("----- SSPI Initialized
");
1526
1527
1528 // Initialize the WinSock subsystem.
1529 if (WSAStartup(0x0101, &WsaData) == SOCKET_ERROR) // Winsock.h
1530 {
1531 printf("Error %d returned by WSAStartup
", GetLastError()); goto cleanup;
1532 } //
1533 printf("----- WinSock Initialized
");
1534
1535
1536 // Create credentials.
1537 if (CreateCredentials((char*)pszUser, &hClientCreds))
1538 {
1539 printf("Error creating credentials
"); goto cleanup;
1540 }
1541 fCredsInitialized = TRUE; //
1542 printf("----- Credentials Initialized
");
1543
1544
1545 // Connect to server.
1546 if (ConnectToServer((char*)pszServerName, iPortNumber, &Socket))
1547 {
1548 printf("Error connecting to server
"); goto cleanup;
1549 } //
1550 printf("----- Connectd To Server
");
1551
1552
1553
1554 // Perform handshake
1555 if (PerformClientHandshake(Socket, &hClientCreds, (char*)pszServerName, &hContext, &ExtraData))
1556 {
1557 printf("Error performing handshake
"); goto cleanup;
1558 }
1559 fContextInitialized = TRUE; //
1560 printf("----- Client Handshake Performed
");
1561
1562
1563 // Authenticate server's credentials. Get server's certificate.
1564 Status = g_pSSPI->QueryContextAttributes(&hContext, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext);
1565 if (Status != SEC_E_OK)
1566 {
1567 printf("Error 0x%x querying remote certificate
", Status); goto cleanup;
1568 } //
1569 printf("----- Server Credentials Authenticated
");
1570
1571
1572 // Display server certificate chain.
1573 DisplayCertChain(pRemoteCertContext, FALSE); //
1574 printf("----- Certificate Chain Displayed
");
1575
1576
1577 // Attempt to validate server certificate.
1578 Status = VerifyServerCertificate(pRemoteCertContext, (char*)pszServerName, 0);
1579 if (Status) { printf("**** Error 0x%x authenticating server credentials!
", Status); goto cleanup; }
1580 // The server certificate did not validate correctly. At this point, we cannot tell
1581 // if we are connecting to the correct server, or if we are connecting to a
1582 // "man in the middle" attack server - Best to just abort the connection.
1583 printf("----- Server Certificate Verified
");
1584
1585
1586
1587 // Free the server certificate context.
1588 CertFreeCertificateContext(pRemoteCertContext);
1589 pRemoteCertContext = NULL; //
1590 printf("----- Server certificate context released
");
1591
1592
1593 // Display connection info.
1594 DisplayConnectionInfo(&hContext); //
1595 printf("----- Secure Connection Info
");
1596
1597
1598
1599 // Send Request, recover response. LPSTR pszRequest = "EHLO";
1600 if (SMTPsession(Socket, &hClientCreds, &hContext))
1601 {
1602 printf("Error SMTP Session
"); goto cleanup;
1603 } //
1604 printf("----- SMTP session Complete
");
1605
1606
1607 // Send a close_notify alert to the server and close down the connection.
1608 if (DisconnectFromServer(Socket, &hClientCreds, &hContext))
1609 {
1610 printf("Error disconnecting from server
"); goto cleanup;
1611 }
1612 fContextInitialized = FALSE;
1613 Socket = INVALID_SOCKET; //
1614 printf("----- Disconnected From Server
");
1615
1616
1617
1618
1619 cleanup: //
1620 printf("----- Begin Cleanup
");
1621
1622 // Free the server certificate context.
1623 if (pRemoteCertContext)
1624 {
1625 CertFreeCertificateContext(pRemoteCertContext);
1626 pRemoteCertContext = NULL;
1627 }
1628
1629 // Free SSPI context handle.
1630 if (fContextInitialized)
1631 {
1632 g_pSSPI->DeleteSecurityContext(&hContext);
1633 fContextInitialized = FALSE;
1634 }
1635
1636 // Free SSPI credentials handle.
1637 if (fCredsInitialized)
1638 {
1639 g_pSSPI->FreeCredentialsHandle(&hClientCreds);
1640 fCredsInitialized = FALSE;
1641 }
1642
1643 // Close socket.
1644 if (Socket != INVALID_SOCKET) closesocket(Socket);
1645
1646 // Shutdown WinSock subsystem.
1647 WSACleanup();
1648
1649 // Close "MY" certificate store.
1650 if (hMyCertStore) CertCloseStore(hMyCertStore, 0);
1651
1652 UnloadSecurityLibrary();
1653
1654
1655 printf("----- All Done -----
");
1656
1657 }
1658
1659
PS:會笑的人,運氣通常都會比別人好。
總結
以上是生活随笔為你收集整理的Windows C++ TLS 实现连接163邮箱的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浦发美运新贵白金卡国内能用吗?怎么使用这
- 下一篇: PDF压缩,在线压缩免费