I'm trying to communicate in https with a server using WinInet (from the Win32 API).
Here is a very minimalist code :
HINTERNET ses = InternetOpenA("test",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0) ;
HINTERNET con = InternetConnectA(ses,"stackoverflow.com",INTERNET_DEFAULT_HTTPS_PORT,NULL,NULL,INTERNET_SERVICE_HTTP,0,NULL) ;
HINTERNET req = HttpOpenRequestA(con,"GET",NULL,NULL,NULL,NULL,INTERNET_FLAG_SECURE,NULL) ;
HttpSendRequestA(req,NULL,0,NULL,0) ;
DWORD read ;
char str[3000] ;
InternetReadFile(req,reinterpret_cast<void*>(str),sizeof(str)-1,&read);
str[read] = 0 ;
cout << &str[0] << endl ;
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 create a certificate context with CertCreateCertificateContext (using binary data from client_cert.pem) and then pass it using InternetSetOptionA. But then, HttpSendRequestA fails with an error 12157...
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 !
Related
I have been learning with WebSocket++ and built some of the server examples (Windows 10 Visual Studio 2019). The non-TLS examples work without issues, however, the TLS-enabled examples (echo_server_both.cpp and echo_server_tls.cpp) can't do the handshake. I am very new to web development in general so I know I must be doing something wrong with regards to the certificate and keys.
I am testing the servers with WebSocket King client, an extension of Google Chrome that connects correctly to other websocket servers like wss://echo.websocket.org and to my own localhost when I don't use TLS.
The echo_server_both example comes with a server.pem file, and the echo_server_tls example comes with server.pem and dh.pem. I have used the same files that come with the samples, and I have also tried generating and registering my own .pem files using openSSL. In both cases I get this when the client tries to connect:
[2021-06-29 20:51:21] [error] handle_transport_init received error: sslv3 alert certificate unknown
[2021-06-29 20:51:21] [fail] WebSocket Connection [::1]:63346 - "" - 0 asio.ssl:336151574 sslv3 alert certificate unknown
[2021-06-29 20:51:21] [info] asio async_shutdown error: asio.ssl:336462231 (shutdown while in init)
I discovered these errors after I edited handle_init() in tls.hpp, following a suggestion in another site, to look like this:
void handle_init(init_handler callback,lib::asio::error_code const & ec) {
if (ec) {
//m_ec = socket::make_error_code(socket::error::tls_handshake_failed);
m_ec = ec;
} else {
m_ec = lib::error_code();
}
callback(m_ec);
}
This change let the actual openSSL error to show in the console, otherwise it would show a generic "handshake failed" error.
I know I'm not doing what I should with the certificates, but I have no idea where else to look or what to do next. Can anyone here help please? Should I use the .pem files that come with the examples, or should I generate my own? in case I should generate my own, what would be the openSSL command to do that correctly and how do I tell my PC to recognize these as valid so that the server works?
Found the problem: WebSocket++ will not accept a self-signed certificate (the ones you can create directly in your own PC using OpenSSL or the Windows utilities). There is no way around it. You must have a valid, authority-validated and endorsed certificate. You can get such a certificate for free (valid only for 90 days) from https://zerossl.com/. The site has detailed instructions on how to request, obtain and install a certificate. After getting a valid certificate and installing it on my server, everything worked as it should.
I am making both server and client for an application, using the ACE library with OpenSSL. I am trying to get mutual authentication to work, o the server will only accept connections from trusted clients.
I have generated a CA key and cert, and used it to sign a server cert and a client cert (each with their own keys also). I seem to be loading the trusted store correctly, but I keep getting the error "peer did not return a certificate" during handshake.
Server side code:
ACE_SSL_Context *context = ACE_SSL_Context::instance();
context->set_mode(ACE_SSL_Context::SSLv23_server);
context->certificate("../ACE-server/server_cert.pem", SSL_FILETYPE_PEM);
context->private_key("../ACE-server/server_key.pem", SSL_FILETYPE_PEM);
if (context->load_trusted_ca("../ACE-server/trusted.pem", 0, false) == -1) {
ACE_ERROR_RETURN((LM_ERROR, "%p\n", "load_trusted_ca"), -1);
}
if (context->have_trusted_ca() <= 0) {
ACE_ERROR_RETURN((LM_ERROR, "%p\n", "have_trusted_ca"), -1);
}
Client side code:
ACE_SSL_Context *context = ACE_SSL_Context::instance();
context->set_mode(ACE_SSL_Context::SSLv23_client);
context->certificate("../ACE-client/client_cert.pem", SSL_FILETYPE_PEM);
context->private_key("../ACE-client/client_key.pem", SSL_FILETYPE_PEM);
I generated the certificates following these instructions: https://blog.codeship.com/how-to-set-up-mutual-tls-authentication/
And checking online, I found that if the .crt and .key files are readable, they should already be in .pem format and there is no need to convert them. So I just changed the extension and used them here.
Any help is appreciated!
My problem apparently was the same as seen here: OpenSSL client not sending client certificate
I was changing the SSL context after creating the SSL Socket. Now the mutual authentication works, but my client crashes when closing the connection. Though I don't know why that is yet.
I have a .pem file which will successfully connect to my website via the --cert parameter of curl. I then converted that to a der file:
openssl x509 -inform PEM -outform DER -in client.pem -out cert.der
Then I loaded that cert.der into my project and I'm now trying to use that with Alamofire, following the example on their homepage:
let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
certificates: ServerTrustPolicy.certificatesInBundle(),
validateCertificateChain: true,
validateHost: true
)
let policyManager = ServerTrustPolicyManager(policies: ["my.domain.com" : serverTrustPolicy])
manager = Alamofire.Manager(configuration: configuration, serverTrustPolicyManager: policyManager)
manager.request(.GET, url, parameters: params, encoding: .URLEncodedInURL, headers: nil)
.authenticate(usingCredential: credential)
.validate()
.responseJSON {
When that runs though it just fails and I get a 'cancelled' as the error's localizedDescription, which is what Alamofire does when authentication fails.
What am I doing wrong?
The Alamofire cert pinning logic does not currently support this use case. It is only designed to handle cert and public key pinning, not client certificates used to authenticate with the server. This is something we could support in the future if this is a common use case.
With that said, I'm assuming in this case you are receiving a NSURLAuthenticationChallenge with a protection space that has an authentication method of type .NSURLAuthenticationMethodClientCertificate. In these cases, you need to evaluate the host of the challenge, then create an NSURLCredential using the credentialWithIdentity:certificates:persistence: API. By passing this credential off to the completion handler, the client certificate should be sent to the server to authenticate the connection. More info can be found here.
Client certificate authentication (NSURLAuthenticationMethodClientCertificate) requires the system identity and all certificates needed to authenticate with the server. Create an NSURLCredential object with credentialWithIdentity:certificates:persistence:.
I've never actually had a need to use this type of authentication before. You'll need to override the auth challenge SessionDelegate closure using the task override closure to get this working.
I constructed a apache mod_perl web service based on SSL.Of course, From my browser, I can access the web service using https (Of cource,I add my self-signed CA cert to brower's trust list) access the web service,but when using SOAP::Lite , I failed.
This is my source code:
$ENV{HTTPS_CERT_FILE} = '/etc/pki/tls/mycerts/client.crt';
$ENV{HTTPS_KEY_FILE} = '/etc/pki/tls/mycerts/client.key';
#$ENV{HTTPS_CA_FILE} = '/etc/pki/tls/mycerts/ca.crt';
#$ENV{HTTPS_CA_DIR} = '/etc/pki/tls/mycerts/ca.key';
#$ENV{HTTPS_VERSION} = 3;
$ENV{SSL_ca_file}='/etc/pki/tls/mycerts/ca.crt';
$ENV{SSL_ca_pah}='/etc/pki/tls/mycerts/';
#$ENV{SSL_cert_file}='/etc/pki/tls/mycerts/client.key';
#$ENV{SSL_key_file}='/etc/pki/tls/mycerts/client.crt';
$ENV{PERL_LWP_SSL_CA_FILE}='/etc/pki/tls/mycerts/ca.crt';
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=1;
#$ENV{PERL_LWP_SSL_CA_PATH}='/etc/pki/tls/mycerts/';
use SOAP::Lite;
my $name = "wuchang";
print "\n\nCalling the SOAP Server to say hello\n\n";
print SOAP::Lite
-> uri('http://localhost/mod_perl_rules1')
-> proxy('https://localhost/mod_perl_rules1')
-> result;
I get the response:
500 Can't connect to localhost:443 (certificate verify failed) at /root/Desktop/test.pl line 18
I really cannot debug this.I don't know if my certificate format is incorrect.I use openssl to generate my cert,including client cert ,server cert and my self-signed ca cert and I make CA sign the client and server cert.I really don't know what is going wrong/.
Simply tell it not to check the certificate. Set SSL Verify to zero like this:
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=0;
I have windows c++ project.
Need to implement both ssl client and server on top of existing winsock code.
I tried with openssl but it seems too messy. I assume there is nicer/shorter/cleaner/faster way implementeing this than openssl..
Im thankful for any suggestions..
You can use Windows built-in SSL stuff -- SChannel . Searching Google fo "SChannel SSL" would give you plenty of information (though SChannel itself is poorly documented and not easy to comprehend).
On the other hand, OpenSSL is not messy once you study the source code of some project, that uses OpenSSL.
Acctually .. After some time spent with openssl hacking I wouldnt say its that messy :)
In case anyone anytime needs to add ssl to existing winsock code:
existing winsock code was like this:
0: sockett.Listen etc....
1: sockett.Accept(client, .....
2: recv(client , ...)
3: send(client , .....)
well in short if you want to implement SSL here..
delete lines 2 and 3 :) and add:
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
SSL_CTX *tlsctx;
SSL *ssl;
tlsctx = SSL_CTX_new( SSLv23_method());
// search google : generate self signed certificate openssl
SSL_CTX_use_certificate_file(tlsctx, "ssl\\server1.crt" , SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(tlsctx, "ssl\\server1.key", SSL_FILETYPE_PEM);
ssl = SSL_new(tlsctx);
SSL_set_fd(ssl, client);
SSL_accept(ssl);
/* instaed recv SSL_read(ssl, ....*/
/* instaed send SSL_write(ssl, ....*/
/* not 100% sure Sleep and shutdown/free/close are entirely correct here but in my code works fine */
Sleep(3000);
SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(tlsctx);
shutdown(client, SD_BOTH);
Sleep(10);
closesocket(client);
For testing:
in command line run:
openssl s_client -host localhost -port <PORT>