3
  • This is an evolution of my previous question, which was about WinHttp.
  • I hope this is the right way to do this...

I'm trying to communicate in https with a server using WinInet (from the Win32 API).

Here is a very minimalist code :

HINTERNET ses = InternetOpen("test",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0) ;
HINTERNET con = InternetOpenUrl( ses,"https://stackoverflow.com",NULL,0,0,0 ) ;
DWORD read ;
char  str [3000] ;
InternetReadFile( con,reinterpret_cast<void*>( str ),sizeof( str )-1,&read ) ;
str[read] = 0 ;
cout << &str[0] ;
InternetCloseHandle( con ) ;
InternetCloseHandle( ses ) ;

As long as I communicate with a "classic" https server, like stackoverflow.com, everything goes well. The problem is when I try to communicate with a server that requests an authentication of the client.

I have 3 .pem files : a certificate and a private key for my client, and a root certificate that authenticates my client certificate (i.e. a certificate chain of length 2).

For information, I can connect my server using this cULR command line :

curl https://my.server --cert Client_cert.pem --key Client_key.pem --cacert Root_cert.pem

This is the proof that it's possible!

Reading the WinInet documentation, I found a page named "Handling Authentication", but it's all about username:password, and there's nothing about certificate.

I found out that I have to use the Crypt32 library : I have to create a certificate context with CertCreateCertificateContext and then insert it in a certificat store, and then use that store for my connection... Well, I must admit that I would be glad to find a good tutorial or some code sample ! By the way, I don't have a piece of clue about how to insert my private key into that stuff...

Thanks in advance !

Captain'Flam
  • 479
  • 4
  • 12

1 Answers1

0

You have to pass your certificate to WinInet using INTERNET_OPTION_CLIENT_CERT_CONTEXT with a call to InternetSetOption():

INTERNET_OPTION_CLIENT_CERT_CONTEXT

84

This flag is not supported by InternetQueryOption. The lpBuffer parameter must be a pointer to a CERT_CONTEXT structure and not a pointer to a CERT_CONTEXT pointer. If an application receives ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED, it must call InternetErrorDlg or use InternetSetOption to supply a certificate before retrying the request. CertDuplicateCertificateContext is then called so that the certificate context passed can be independently released by the application.

So, for example:

InternetSetOption(hI,INTERNET_OPTION_CLIENT_CERT_CONTEXT,(void*)cert,sizeof(CERT_CONTEXT));

Btw, curl calls are not good examples because curl uses libcurl which uses OpenSSL.

Michael Chourdakis
  • 10,345
  • 3
  • 42
  • 78
  • Thx, I have already read that page... The tricky part is that I have to build a CERT_CONTEXT that contains a handle to a certificate store : do I have to build it first ? And what about the client's private key ? And how do I check the certificate chain ? Do I have to do it myself ? – Captain'Flam Mar 22 '19 at 14:34
  • It depends where you have this certificate. Is it in a PFX file? https://learn.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-pfximportcertstore - PFXImportStore. Then you can CertEnumCertificatesInStore to loop through all the certificates. The private key is automatically included when it is needed when it exists (you may be prompted with a PIN request, for example). Also see https://stackoverflow.com/questions/6307886/how-to-create-pfx-file-from-certificate-and-private-key – Michael Chourdakis Mar 22 '19 at 15:35
  • My 2 certificate + 1 private key are stored in .pem files. And my code is supposed to run silently, so a PIN request is not possible... – Captain'Flam Mar 22 '19 at 15:53
  • If you have the private key and the public key, you save it in a PFX with the pvk2pfx tool and then import it and use it with the functions I said. Also, you need one certificate. The chain, if any, will be validated from the server, not you. If this is not a key signed by a certificate authority that your server knows, the chain you would provide is useless anyway. If it's your own organization, then the server will be already aware of the root certificate. – Michael Chourdakis Mar 22 '19 at 16:04
  • Thank you Michael ! Finally I managed to build a PFX from my 2 certificates + private key. I import it using `PFXImportCertStore`, but now that I have a brand new `HCERTSTORE`, I can't figure out how to _pass_ it to my https connection... It's not using `InternetSetOption`... I don't see any Internet or Http function that takes a `HCERTSTORE` as parameter. – Captain'Flam Apr 10 '19 at 17:43
  • The certificate store has your certificates, you can find them with [CertEnumCertificatesInStore](https://learn.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-certenumcertificatesinstore) – Michael Chourdakis Apr 10 '19 at 18:04