Decent routine to check server certificate - ssl

I am writing a client app that need talk to Active Directory server and one of requirements is to support LDAPS/StartTLS.
I already figure out there is one option need to set:
if (ldap_set_option(pLdap, LDAP_OPT_SERVER_CERTIFICATE, &my_cert_check_func) != LDAP_SUCCESS) {
std::cerr << "ldap set cert check callback failed" <<std::endl;
return NULL;
}
and my_cert_check_func is over-naive and not safe at all:
static BOOLEAN my_cert_check_func(PLDAP connection, PCCERT_CONTEXT server_cert)
{
return TRUE;
}
And I also did a lot of googling and read quite a lot msdn, but still no clue. I have never handle such security-related coding before so any thing related to cert check are welcome.
And because I write this app using Winldap API, so the code should use Windows specific APIs.
And I am also thinking do such check using openssl api (this api is a dependency of my app, so it is fine to use that).
Could you show me some sample code for doing real checking of server certs against client security store or what ever client has?
Thank you very much!

You don't need to verify the entire certificate chain etc. for validity. LDAPS should already have done that. You only need to check the subjectDN of the certificate against what you think it should be when talking to that server.

Related

WCF - certificate not configured properly with HTTP.SYS in the HTTPS case. Works with Charles proxy running

This seems to be a common error (there are other posts with similar issues) - however, I have gone through all those posts and MSDN articles ( https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/working-with-certificates ).
Scenario: Trying to access a service with an HTTPS end point.
Setting the client certificate in code (certificate is loading correctly).
As for the Server cert, I have tried both the options below:
client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerOrChainTrust;
I have imported the server certificate to Personal as well as machine store (Trusted Root certificate authorities / certificates).
The weird thing is the call is going through when I use Charles Proxy as the SSL proxy.
Other settings:
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback +=
(se, cert, chain, sslerror) =>
{
//Console.WriteLine(cert.GetCertHashString());
if (cert.GetCertHashString() == "[actual hash here]")
return true;
else
return false;
};
The above Hash check works fine when Charles proxy is running. Without the proxy running, the callback does not even get called.
Any feedback is appreciated.
(It may be worthwhile to note that a Java client using Apache CXF library works fine - against the same service.)
Update:
For completeness, the original error also had this text:
This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server.
OK, after days(& nights) of head banging, the following are my musings / findings (& of course the solution !):
There is "SSL" and then there is SSLv2, SSLv3, TLSv1.0, TLSv1.1, TLS1.2 & TLSv1.3 (draft as of now).
It is critical that the server and client are able to negotiate & pick one of these versions to successfully communicate.
The HTTP.SYS error seems to be a result of the client not being able to negotiate with the server on the appropriate version. When going through Charles proxy, it was clear that both Charles and the service we were trying to hit, were using TLSV1.1.
In my case, I was using wsHTTPBinding & though I tried setting the System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls; and other combinations, I could never get the HTTP.SYS error to go away. It would seem that the server and the client could never pick a version that they could agree on.
I did try using other bindings such as basicHttpBinding (with TransportWithMessageCredential) as well as basicHttpsBinding, but to no avail. What's more with some minor tweaks in the binding elements (through config & code) in each case, I ended with exactly the same binding configuration in all 3 cases (basicHttp/basichHttps/wsHttp bindings)! In essence, while there are these out-of-the-box bindings, they probably work for the most simple of scenarios. What's more, there is probably no need for so many of these pre-packaged bindings, especially as they seem to be using mostly the same binding elements.
I did remember reading that using a custom binding is better in many cases - but I imagined that by customizing a wsHttpBinding I would be achieving the same thing. Looks not - as there are some hard-coded properties (e.g.: default SSL protocols) in this binding that seem difficult to get around. I did take a look at the source code of wsHttpBinding and its base class, but could not find the exact hard coded location (but there are references to "default" protocols in the System.ServiceModel code).
In the end a "CustomBinding" worked for me, configured like so:
Custom Binding configuration
- Sorry for including this as an image - as the formatting on SO was playing up.
The idea is to use httpsTransport with requireClientCertificate, security with authenticationMode="CertificateOverTransport" & includeTimestamp="true" (our service required Timestamp) and the relevant messageSecurityVersion - in our case it was:
WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10.
The above configurations automatically signed the Timestamp as well.
On top of this we had to include the username / password credentials. Simply setting the client.ClientCredentials.UserName.UserName & client.ClientCredentials.UserName.Password did not result in these credentials included in the Security header. The logic was to add the username "token" as well, like so:
//Get the current binding
System.ServiceModel.Channels.Binding binding = client.Endpoint.Binding;
//Get the binding elements
BindingElementCollection elements = binding.CreateBindingElements();
//Locate the Security binding element
SecurityBindingElement security = elements.Find<SecurityBindingElement>();
//This should not be null - as we are using Certificate authentication anyway
if (security != null)
{
UserNameSecurityTokenParameters uTokenParams = new UserNameSecurityTokenParameters();
uTokenParams.InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient;
security.EndpointSupportingTokenParameters.SignedEncrypted.Add(uTokenParams);
}
client.Endpoint.Binding = new CustomBinding(elements.ToArray());
With all this setup, I was able to finally hit the Service and actually get the result - well, almost ! - as the result does not include a Timestamp, which WCF is throwing up as an exception. That is another problem to solve though.
Hopefully readers find this useful.
Update:
Now the Timestamp issue is also "sorted". The thing is the response lacked any security header, not just the timestamp. Thankfully there was a straightforward way to notify WCF to ignore unsecure responses, by simply marking an attribute on the security element: enableUnsecuredResponse="true". Obviously this is not desirable, but as we do not have any control on the service, this is the best we can do at the moment.

Php's password_hash encoding for HTTP basic authentication

My problem:
I need, basic authentication over HTTP (client can't afford HTTPS). So I don't worry if communication is not encrypted. I just want to prevent some user from sniffing and using the password (site only used to upload photos and those photos are public.).
Toolbox of what I have at my disposal:
Javascript
PHP
Sha512.js
The SHA algorithm is the same in both PHP and JS:
The proof:(?)
<?php
$password= "password";
echo hash('sha512',$password);
//outputs: b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86
?>
While in JS (all my files are encoded in utf8)
document.getElementById("hiddenField").value
= JS.sha512("password");
//outputs b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86
However I cannot simply send the same hash on every connection, else anyone can sniff it and send it to connect.
So my idea was to use password_hash() function as salt generator.
The salt is public, the hash of (password+salt) is public, but password is private and never sent as clear text: the server will compute (hopefully) the same hash as the one in JS from the client and verify that both hashes match.
The problem is that regardless of what I do, I'm unable to get the same output when I hash the output of that function(password_hash). It seems to be something related to encoding.
I want to use password_hash() because it already keeps into account a lot of security stuff:
Javascript:
document.getElementById("hiddenField").value
= JS.sha512("password" + document.getElementById("publicToken").value);
I put the "password_hash" content into another hidden form field that I call "publicToken". Regardless of what I do I'm unable to get the hash match:
<?php
$salt = ut8_encode(password_hash("another_password")); //doesn't work either
In the end, what do I have to do to get a correctly encoded salt?
<?php
$salt = //... one time usage salt.. but what to put here?
I realize your client cannot afford a standard HTTPS certificate, but honestly, even a free SSL certificate is likely to be far better than what ever you can concoct here.
In this situation, all you are doing is making the browser-side hash the user's password, and all one has to do to get in is send a matching hash. If you decide to do this, you definitely need to hash the password again on the server side, but it is still no replacement for SSL.
More on it here: https://crackstation.net/hashing-security.htm
Without public key cryptography and a way to verify the identity of the server (in other words, HTTPS), the unfortunate truth is that there is simply no way to secure the communication to an acceptable level. I would not even advise trying, for fear of getting a false sense of security. No matter how much you hash and salt, it will only be minimally better than sending the plaintext password and trivial to break.
If your client cannot afford a certificate, I would recommend taking a look at StartSSL. Their basic level certificates are completely free; I believe they're valid for 1 year with unlimited renewal.
Another project worth looking at is Let's Encrypt. They've trying to make the process of getting a certificate much simpler and more accessible, so they've developed a way to completely automate the process of issuing (free) certificates. The service is not live yet, unfortunately; they plan to start issuing certificates this summer. Quoting their page:
Anyone who has gone through the trouble of setting up a secure website
knows what a hassle getting a certificate can be. Let’s Encrypt
automates away all this pain and lets site operators turn on HTTPS
with a single click or shell command.
When Let’s Encrypt launches in mid-2015, enabling HTTPS for your site
will be as easy as installing a small piece of certificate management
software on the server:
$ sudo apt-get install lets-encrypt
$ lets-encrypt example.com
That’s all there is to it! https://example.com is immediately live.
The Let’s Encrypt management software will:
Automatically prove to the Let’s Encrypt CA that you control the website
Obtain a browser-trusted certificate and set it up on your web server
Keep track of when your certificate is going to expire, and automatically renew it
Help you revoke the certificate if that ever becomes necessary.
No validation emails, no complicated configuration editing, no expired
certificates breaking your website. And of course, because Let’s
Encrypt provides certificates for free, no need to arrange payment.

Grails Spring Security X509 for Authentication and LDAP for Authorities

Some pointers more than anything required here.
I'm trying to get both X509 and LDAP working in my application. I want users to be authenticated using their PKI certs and then for the APP to get their authorities from our LDAP server.
I have LDAP working with a customer userDetailsContextMapper at the moment however how to add the x509 properly stumps me a little.
I think what I want is a PreAuthenticatedAuthenticationProvider that uses an injected ldapUserDetails service.
How can I do that? Do I need a UserDetailsByNameServiceWrapper to wrap the LdapUserDetailsService up to be used within the pre-authentication provider?
I ask because unfortunately the testing platform and the development environment at the moment is detached, and I don't have local LDAP or PKI set up to test against so its about a 6 hour process getting a new war onto the dev environment... Restrictive I know... So I want to get it right first time.
Cheers!
NOTE: THE FOLLOWING WORKS WITH Spring-Security-Core v1.2.7.3, Configuration names are different in 2.0RC2
Following a few different ideas, this is what I came up with. This assumes you already have LDAP working with a custom and UserDetailsContextMapper (see: ldap documentation):
Ensure both the LDAP and a PreAuthenticatedAuthentication Provider are in the provider list:
grails.plugins.springsecurity.providerNames = [
'preAuthenticatedAutehnticationProvider',
'ldapAuthProvider',
'daoAutehnticationProvider',
'anonymousAuthenticationProvider',
'rememberMeAuthenticationProvider']
Then in your spring resources (grails-app/conf/spring/resources.groovy) configure the following beans:
ldapUserDetailsService(org.springframework.security.ldap.userdetails.LdapUserDetailsService,
ref('ldapUserSearch'),
ref('ldapAuthoritiesPopulator')) {
userDetailsMapper = ref('ldapUserDetailsMapper')
}
userDetailsByNameServiceWrapper(org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper) {
userDetailsService = ref('ldapUserDetailsService')
}
preAuthenticatedAuthenticationProvider(org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider) {
preAuthenticatedUserDetailsService = ref('userDetailsByNameServiceWrapper')
}
And bobs your uncle and you have some aunts!
For reference the pages I used to come up with this solution are:
No AuthenticationProvider found using spring security
Wrap your LdapUserDetailsService in a UserDetailsByNameServiceWrapper
Instead of the LdapAuthenticationProvider configure a PreAuthenticatedAuthenticationProvider that will be able to process the PreAuthenticatedAuthenticationToken issued by your CustomX509AuthenticationFilter.
Inject the wrapped LdapUserDetailsService into the PreAuthenticatedAuthenticationProvider.
http://blog.serindu.com/2011/05/26/grails-spring-security-using-preauthenticated-authentication-provider/
Covers how to wire up a preAuthenticationAuthenticationProvider in grails
http://forum.spring.io/forum/spring-projects/security/108467-combine-pre-authentication-with-ldap-for-user-details-and-authorities
there's an LdapUserDetailsService that does all the good things the LdapAuthenticationProvider does - except for authentication
http://pwu-developer.blogspot.co.uk/2012/02/grails-security-with-cas-and-ldap.html more on how to wire up that ldapUserDetailsService
Hope this helps someone else!

WCF over HTTPS in Monotouch?

I am trying to call a wcf service over https and have followed the suggested setup from here. It works fine over http, but I get a RemoteCertificateNameMismatch error over https which I am handling with this (as suggested) -
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
(se, cert, chain, sslerror) => { return true; };
I later get a 415 unsupported media type error which I can't figure out. I have a win .net test client that is able to call the service and receive results, but from monotouch I can't get it to work. Has anyone been able to do this successfully and wouldn't mind pasting an example?
Any help is much appreciated!
HTTP error codes comes from the server side. Of course the client configuration may play a role into this.
If possible switch to HTTP and compare sessions (e.g. using wireshark) between your Windows's and MonoTouch clients. Doing so in HTTPS may tell you a few things but that's less likely to be helpful.
Also check for similars 415 errors affecting other (non-MonoTouch) projects. Since the error comes the server the information they provide might help you find what's going on.

Objective-C/Cocoa: How do I accept a bad server certificate?

Using NSURLRequest, I am trying to access a web site that has an expired certificate. When I send the request, my connection:didFailWithError delegate method is invoked with the following info:
-1203, NSURLErrorDomain, bad server certificate
My searches have only turned up one solution: a hidden class method in NSURLRequest:
[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:myHost];
However, I don't want to use private APIs in a production app for obvious reasons.
Any suggestions on what to do? Do I need to use CFNetwork APIs, and if so, two questions:
Any sample code I can use to get started? I haven't found any online.
If I use CFNetwork for this, do I have to ditch NSURL entirely?
EDIT:
iPhone OS 3.0 introduced a supported method for doing this. More details here: How to use NSURLConnection to connect with SSL for an untrusted cert?
The supported way of doing this requires using CFNetwork. You have to do is attach a kCFStreamPropertySSLSettings to the stream that specifies kCFStreamSSLValidatesCertificateChain == kCFBooleanFalse. Below is some quick code that does it, minus checking for valid results add cleaning up. Once you have done this You can use CFReadStreamRead() to get the data.
CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, CFSTR("http://www.apple.com"), NULL);
CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), myURL, kCFHTTPVersion1_1);
CFReadStreamRef myStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, myRequest);
CFMutableDictionaryRef myDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(myDict, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
CFReadStreamSetProperty(myStream, kCFStreamPropertySSLSettings, myDict);
CFReadStreamOpen(myStream);
If it's for an internal server for testing purposes, why not just import the test server's certificate into the KeyChain and set custom trust settings?
iPhone OS 3.0 introduced a supported way of doing this that doesn't require the lower-level CFNetwork APIs. More details here:
How to use NSURLConnection to connect with SSL for an untrusted cert?
I've hit the same issue - I was developing a SOAP client, and the dev server has a "homegrown" certificate. I wasn't able to solve the issue even using that method, since I wasn't using NSURL, but the (poorly documented and apparently abandoned) WS methods, and decided for the time being to (internally) just use a non-SSL connection.
Having said that, however, the question that springs to mind is, if you aren't willing to use a private API in a production app, should you be allowing access to a site with a dodgy certificate?
I'll quote Jens Alfke:
That's not just a theoretical security problem. Something
like 25% of public DNS servers have been compromised, according to
recent reports, and can direct users to phishing/malware/ad sites even
if they enter the domain name properly. The only thing protecting you
from that is SSL certificate checking.
Can you create a self signed certificate and add your custom certificate authority to the trusted CAs? I'm not quite sure how this would work on the iPhone, but I'd assume on Mac OS X you would add these to the Keychain.
You may also be interested in this post Re: How to handle bad certificate error in NSURLDownload
Another option would be to use an alternate connection library.
I am a huge fan of AsyncSocket and it has support for self signed certs
http://code.google.com/p/cocoaasyncsocket/
Take a look, I think it is way more robust then the standard NSURLRequests.