How to tell LDAP SSL server with multiple certificates to return the one that I need? - authentication

My simple LDAP java program, using
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, <UserDN>);
env.put(Context.SECURITY_CREDENTIALS, <Password>);
env.put(Context.SECURITY_PROTOCOL, "ssl");
env.put(Context.PROVIDER_URL, "ldaps://<host>:636");
to make LDAP SSL authentication stopped working ever since a 2nd server certificate with the same CN but other details in the subject are different was installed on the server which I don't have access at all.
The program fails when I make the initial context
new InitialDirContext(env);
The error is "Failed to initialize directory context: <host>:636"
It returns the 2nd server certificate when I run
openssl s_client -showcerts -connect <host>:636 </dev/null
that makes me believe that the solution will be to find a way to tell the server which certificate to use.
I search and read a lot of articles on this topic and I have to admit that I am very confused, it is not clear to me if these articles are talking about client certificate or server certificate, or the actions to be taken are for the client side, or server side.
In one article, it says that I can use a custom SSLSocketFactory with the keystore path and
env.put("java.naming.ldap.factory.socket", "com.xxx.MyCustomSSLSocketFactory");
But I don't know the path to the server certificate keystore on the server.
In one Microsoft article, it says the best resolution is to have just one server certificate on the server or to put the server certificate to Active Directory Domain Services (NTDS\Personal) certificate store for LDAPS communications. But I don't have access to the server and the 'fix' to this problem has to be done in my LDAP java program.
In another article, it says to use Server Name Indication (SNI) extension.
So is there a way that I can specify which certificate I want to the server? Or my problem is somewhere else?
Thanks a lot.
Here is the stack trace:
javax.naming.ServiceUnavailableException: <host>:636; socket closed
at com.sun.jndi.ldap.Connection.readReply(Connection.java:419)
at com.sun.jndi.ldap.LdapClient.ldapBind(LdapClient.java:340)
at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:192)
at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2694)
at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:293)
at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:175)
at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:193)
at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:136)
at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:66)
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:667)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288)
at javax.naming.InitialContext.init(InitialContext.java:223)
at javax.naming.InitialContext.<init>(InitialContext.java:197)
at javax.naming.directory.InitialDirContext.<init>(InitialDirContext.java:82)
When I used Jxplorer to run the same test, it gave me the same error.
EJP was right to point out that the issue was that the certificate was not trusted. Many thanks EJP.
When I installed the CA Certificate in %JAVA_HOME%/lib/security/cacerts, Jxplorer worked. My program still failed. I had to add these lines in it to make it work (not sure if I need all of them though ...):
System.setProperty("javax.net.ssl.keyStore",%JAVA_HOME%/lib/security/cacerts);
System.setProperty("javax.net.ssl.trustStore",%JAVA_HOME%/lib/security/cacerts);
System.setProperty("javax.net.ssl.keyStorePassword=changeit);
System.setProperty("javax.net.ssl.trustStorePassword=changeit);
But since the certificate is not trusted in the first place, I simply 'force' our server to trust it, hence this solution is not acceptable. And neither our server nor the LDAP server runs with Java 7. So SNI is out too!
EJP mentioned that I could control the server certificate by restricting the cipher suites or accepted issuers in the client (my webapp), if the server certificates have different algorithms or issuers. The 2 certificates do have different issuers, however, I don't know how to do that and I could not find anything on that neither.
EJP can you please elaborate, or point me to some sites ... ?

If the certificates have different issuers, you can control which certificate you get at the client by controlling which of those issuers is in your truststore. If only one of them is, that's the one you'll get. If they're both there, you get pot luck. Note that if your truststore also contains a common super-issuer, again it's probably pot luck.

The result isn't pot luck if you specify one and only one certificate in the Certificates - Service (Active Directory Domain Service) - NTDS\Personal location in Microsoft Management Console. Contrary to Microsoft docs I've read, though, a domain controller restart seemed to be necessary for the newly specified certificate to 'take hold'.

Related

LDAPS Microsoft Active Directory Multiple Certificates RFC6125

We have an Microsoft Active Directory Domain with a large pool of domain controllers (DC) that are are setup with LDAP. These are all setup with LDAPS and uses Certificate Services via a template to setup a certificate with the domain name (i.e. test.corp) in the Subject Alternate Name (SAN) for the LDAPS server to serve.
Since these are DC's, DNS is setup in a pool for each these systems to respond to requests to test.corp in a round robin fashion.
Each of these DC's have multiple templates and multiple certificates in the Local Computer\Personal Certificate Store.
Upon testing, using a nodejs module, ldapjs when making a LDAPS request using the domain name, test.corp we notice that a handful of servers fail with the following message:
Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match
certificate's altnames: Host: test.corp. is not in the cert's
altnames: othername:, DNS:.test.corp
As we investigated we found that these handful of LDAPS servers are serving the incorrect certificate. We determined this by using the following command
openssl s_client -connect .test.corp:636
If you take the certificate section of the output and put it in a file and use a tool such as the Certificate manager or certutil to read the file, you can see the certificate is not the correct one. (It does not have the domain "test.corp" SAN). We also verified this by comparing the Serial Numbers
As we investigated, since we have DC's that have multiple certificates in the Local Computer\Personal Certificate store, we came across the following article:
https://social.technet.microsoft.com/wiki/contents/articles/2980.ldap-over-ssl-ldaps-certificate.aspx
It suggests putting the certificate from the local computer\Personal certificate store to the Active Directory Domain Service\Personal store. We followed the steps outlined but we found the same results.
Upon further investigation, it was suggested to use a tool called ldp or adsiedit. We then proceeded to use these tools and spoofed the local machine's host file we were doing the test from, to point the domain (test.corp) to the ip's of one of the DC's that are giving us trouble. After a restart to clear any cache we tested the "ldp" and "adsiedit" tools to connect to test.corp. These systems did not report any errors.
We found this odd, we then ran the openssl command to see what certificate it was serving from this same system and we found it was still serving the incorrect certificate.
Upon further research, it appears that the "ldp" upon selecting the SSL checkbox and "adsiedit" tools were not compliant with RFC6125, specifically B.3
https://www.rfc-editor.org/rfc/rfc6125#appendix-B.3
, which basically states the identity of the certificate must match the identity of the request otherwise the handshake would fail. This identity verification is done by using the certificate common name (CN) or the SAN.
Based on this appears the tools "ldp" and "adsiedit" are not conforming to the RFC6125 standard.
All this to say, we need to first fix the handful of domain controllers that are serving the correct certificate. We are open to suggestions since we have been working on this problem for the past few months. Second, is there a way to get the MS tools in question to work to the RFC6125 standard?
This has been moved to:
https://serverfault.com/questions/939515/ldaps-microsoft-active-directory-multiple-certificates-rfc6125
RFC6125 specifically states that it does not supersede existing RFCs. LDAP cert handling is defined in RFC4513. Outside of that, RFC6125 has significant flaws. See also https://bugzilla.redhat.com/show_bug.cgi?id=1740070#c26
LDP will supposedly validate the SSL against the client store if you toggle the ssl checkbox on the connection screen.
That said, I'm not surprised that neither it nor ADSI edit enforce that part of the standard given they are often used to configure or repair broken configurations. Out of the box and without Certificate Services they use self signed certs on LDAPS. I would wager 80% of DCs never get a proper certificate for LDAP. If they enforced it most wouldn't be able to connect. A better design decision would have been to toggle off the validation.
I use a similar openssl command to verify my own systems. I think it's superior to LDP even if LDP were to validate the certificate. To save you some effort, I would suggest using this variant of the openssl command:
echo | openssl s_client -connect .test.corp:636 2>/dev/null | openssl x509 -noout -dates -issuer -subject -text
That should save you having to output to a file and having to read it with other tools.
I've found LDAPS on AD to be a huge pain for the exact reasons you describe. It just seems to pick up the first valid cert it can find. If you've already added it to the AD DS personal store, I'm not sure where else to suggest you go other than removing some of tother certs from the DCs computer store.

Tomcat/Java Truststore confusion

I am rather confused by the purpose of the trustore file in a tomcat server.
I.e use of the truststoreFile parameter when setting up a TLS enabled connector.
According to the tomcat docs:
truststoreFile
The trust store file to use to validate client certificates. The
default is the value of the javax.net.ssl.trustStore system property.
If neither this attribute nor the default system property is set, no
trust store will be configured.
To me this is telling me that for an inbound connection to my tomcat server, this is where I would store certificates that a client would present. (e.g. a users browser)
Would I really put in 3rd party CA certificates here or other 3rd party server certificates, if my tomcat server wants to make an outbound connection to a TLS enabled server (e.g. an LDAPS server)
As per my understanding the place to do this is the cacerts file.
Is my understanding of the truststore wrong?
Yes, you are right, it is quite confusing.
If you need to make an outbound connection a 3rd party, you need to add the certificates to cacerts.
There is a very popular "signing, validation, etc." service for government-owned companies here in Spain, #firma, that just changed its wildcard server certificate and we had to add it to cacerts.

How can I setup an FTPS server on my aws EC2 ubuntu instance

1) I am trying to setup an FTPS server on my EC2 Ubuntu instance. I can only find resources to setup tutorials for an SFTP server.
2)From what I understand, the SSL certificate is only applicable to the server. When a user tries to FTPS to my server, should he/she upload a certificate or public/private key file similar to SFTP? Or only hostname, port, username, password is sufficient?
You might have better luck searching for "ftp over tls" which is another name for ftps. TLS is the successor protocol to SSL, though often still referred to casually as "SSL."
I use proftpd and I mention that primarily because their docs discuss some theory and troubleshooting techniques using openssl s_client -connect which you will find quite handy regardless of which server you deploy.
The SSL cert is only required at the server side, and if you happen to have a web server "wildcard" cert, you may be able to reuse that, and avoid purchasing a new one.
Client certs are optional; username and password will suffice in many applications. Properly configured, authentication will only happen over encrypted connections. (Don't configure the server to also operate in cleartext mode on the standard ftp port; inevitably you'll find a client who thinks they are using TLS when they are not).
If client certs are required, it is because of your policy, rather than technical reasons. You'll find that SSL client certs operate differently than SSH. Typically the client certs are signed you a certificate authority that you create, and then you trust them because they are signed by your certificate authority as opposed to your possession of their public key, as in SSH.

OpenSSL in GitLab, what verification for self-signed certificate?

On Debian, using GitLab, I ran into issues with my self-signed certificate.
Reading through the code after a lot of searching on the Internet (I guess, it's the last resort, FOSS is helpful), I found the following lines in gitlab-shell/lib/gitlab_net.rb which left me... perplexed.
if config.http_settings['self_signed_cert']
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
Most Stack Overflow responses about the diverse issues I've had until now have led me to believe that VERIFY_NONE, as you'd expect, doesn't verify anything. VERIFY_PEER seems, based on my reading, to be the correct setting for self-signed.
As I read it, it feels like taking steps to secure my connection using a certificate, and then just deciding to not use it? Is it a bug, or am I misreading the source?
gitlab-shell (on the GitLab server) has to communicate to the GitLab instance through an HTTPS or SSH URL API.
If it is a self-signed certificate, it doesn't want any error/warning when trying to access those GitLab URLs, hence the SSL::VERIFY_NONE.
But, that same certificate is also used by clients (outside of the GitLab server), using those same GitLab HTTPS URLs from their browser.
For them, the self-signed certificate is useful, provided they install it in their browser keystore.
For those transactions (clients to GitLab), the certificate will be "verified".
The OP Kheldar point's out in Mislav's post:
OpenSSL expects to find each certificate in a file named by the certificate subject’s hashed name, plus a number extension that starts with 0.
That means you can’t just drop My_Awesome_CA_Cert.pem in the directory and expect it to be picked up automatically.
However, OpenSSL ships with a utility called c_rehash which you can invoke on a directory to have all certificates indexed with appropriately named symlinks.
(See for instance OpenSSL Verify location)
cd /some/where/certs
c_rehash .

SSL client authentication returning Bad Certificate error

I was trying to connect to my custom wrote ssl server written in CPP. It has got client authentication features. Its throwing error Bad certificate when watched through Wireshark. At the server side the error returned was
14560:error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned:s3_srvr.c:2619:
I used the following code to force requesting client certificate
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_verify_depth(ctx, 1);
I could see client returning certificate in Wireshark.
Which function should be used to set the public key used for verifying the client certificate at the server side?
From the error messages it looks like your client does not present a certificate to server and you explicitely requested that a client needs to present one (in server code):
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
What you probably need is to tell your client code to use certificate (along with a private key):
SSL_CTX_use_certificate_chain_file(ctx, pcszCertPath);
SSL_CTX_use_PrivateKey_file(ctx, pcszPrivKeyPath,SSL_FILETYPE_PEM);
I hope that helps.
Also make sure that your server uses the same certificate chain (that it trusts the same CA's). If this is a problem, let me know and I'll help you do that.
With wireshark, you will find out if the server ever requested certificate from the client. The command would be "CertificateRequest".
I was getting a similar error (only line number different):
140671281543104:error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned:s3_srvr.c:3292:
I had generated self-signed certificates using the procedure mentioned in https://help.ubuntu.com/community/OpenSSL.
After juggling with the error for one day, i found that the error was because the self-generated CA was not in the trust chain of the machine I was using.
To add the CA to the trust chain in RHEL-7, one can follow the below procedure:
To add a certificate in the simple PEM or DER file formats to the
list of CAs trusted on the system:
Copy it to the
/etc/pki/ca-trust/source/anchors/
subdirectory, and run the
update-ca-trust
command.
If your certificate is in the extended BEGIN TRUSTED file format,
then place it into the main source/ directory instead.
I think the above procedure can be followed for fedora too.
If "update-ca-trust" command is not available, it might be useful to explore the commands like "update-ca-certificates".
Hope this will be useful to someone.
Difference Between SSLCACertificateFile and SSLCertificateChainFile
SSLCertificateChainFile is generally the correct option to choose, as it has the least impact; it causes the listed file to be sent along with the certificate to any clients that connect.
Provide all the Root CA into the following file to resolve the issue
SSLCACertificateFile (hereafter "CACert") does everything SSLCertificateChainFile does (hereafter "Chain"), and additionally permits the use of the cert in question to sign client certificates. This sort of authentication is quite rare (at least for the moment), and if you aren't using it, there's IMHO no reason to augment its functionality by using CACert instead of Chain. On the flipside, one could argue that there's no harm in the additional functionality, and CACert covers all cases. Both arguments are valid.
Needless to say, if you ask the cert vendor, they'll always push for CACert over Chain, since it gives them another thing (client certs) that they can potentially sell you down the line. ;)
Modify the server code with the below line:
Server code:
SSL_CTX_load_verify_locations(ctx,"client_certificate_ca.pem", NULL);
Here client_certificate_ca.pem is the file which generated the client certificates.
verify your client certificate with the "client_certificate_ca.pem" file using below command.
verify -CAfile client_certificate_ca.pem client_certificate.pem
Client code:
if (SSL_CTX_use_certificate_file(ctx, "client_certificate.pem", SSL_FILETYPE_PEM) <= 0)
{
}
if (SSL_CTX_use_PrivateKey_file(ctx, "client_certificate.ky", SSL_FILETYPE_PEM) <= 0)
{
}
if (!SSL_CTX_check_private_key(ctx))
{
}