I'm having an issue sending our certificate (client) to another server during a web service call.
We're expected to return the certificate with the CN:
b2b-test
however whenever we receive the servers certificate and attempt to send our own, we're getting:
Unable to find valid certificate path to requested target
From my understanding, if I put our certificate b2b-test inside of our keystore jre/lib/security/cacerts, then when receiving the server request, it should send the appropriate certificate.
How does the client know which key from the keystore it should send to the server? Should I be specifying the exact certificate to send during my request, or is it enough for it to be in the keystore?
Currently the server is sending back the certificate
b2b-a
The certificate chain shows:
*** Certificate chain
chain [0] = [
[
Version: V3
Subject: CN=b2b-a, OU=Web Technologies
The URL for the service is:
https://b2b-a/service
If I configure it like one way ssl, and put the servers certificate into our cacerts, then it picks up their cert and fails the handshake (I'm assuming because it's expecting our cert and not theirs back due to it being a two way setup).
I'm a little stumped here. Any clarification on what I've said is greatly appreciated.
The "Unable to find valid certificate path to requested target" error usually occurs when one side passes a certificate for which the other side doesn't trust any of the certs in the chain. You'll generally (always?) see the error message on the side that's receiving the cert (the side that's missing the truststore).
If you're the client and they're the server, one-way SSL would send their cert from them to you and you would validate it (making sure that the hostname matches the content in the cert), but it would not send any cert from you to them nor would they do any validation.
You'll want to make sure you're configuring a truststore that contains either the b2b-a certificate or a certificate that was used to sign it (but that cert doesn't appear to have any root CAs, so you're stuck with directly importing the cert itself). So yes, you want to do what you wrote in your post and put their certificate into your cacerts. And you'll also need to make sure that the b2b-a service trusts the cert you're sending (or a root CA that was used to sign your cert, if any), so you'll need to put your cert into their cacerts as well, or do something equivalent.
Note that putting a cert in cacerts (or in a truststore JKS) DOES NOT send any certs to anyone; any truststore is used only to allow you to validate that you trust the certificate someone else provides you, either because you trust the cert or because you trust a CA cert that was used to sign the cert. No certs are picked out of the cacerts directory to send to another machine.
If you're using a JKS rather than the JRE directory (which is generally a better idea, since you can specify a different set of trusted certs for your process without changing the default set for anyone running a Java project within your JRE), it's possible to use the same JKS as both a keystore and a truststore (you'll just provide the same filename and password for both the keystore and the truststore properties), or you can have two separate files; either approach will work.
From my understanding, if I put our certificate b2b-test inside of our keystore jre/lib/security/cacerts, then when receiving the server request, it should send the appropriate certificate.
Certainly not. That's a truststore. A source of trusted certificates. Don't mess with it. It isn't used for the purpose you require and in any case it is updated every Java dot release, which clobbers your changes.
Your own certificate and private/public key pair should go in a file of your own, a keystore, which is defined to JSSE by the javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword system properties, or by more complex means described in the JSSE Reference Guide.
The keystore is a precious and confidential file containing your private key. Guard and protect it. Private key leakage would compromise your security and identity.
Related
I am running the Java process (Microservice) and trustStore configured in the JVM parameter. If the Microservice needs to connect the external URL which required the cert to be imported in trustStore.
Example:
example.co.uk -> examplecouk as alias in trustStore
example.com -> examplecom as alias in trustStore
example.in -> examplein as alias in trustStore
How does Java know that which certs and alias to be picked from the trustStore for the particular endpoint as I don't pass/mention the alias in the JVM params? Is it pick randomly?
user207421 is nearly correct. To be more exact:
When you as client open an SSL/TLS connection to a server, as part of the handshake the server sends its certificate 'chain' containing its own cert, plus usually one or more linked CA (Certificate Authority) certificates ending with a 'root' CA that should be trusted. See the neighboring Stack https://security.stackexchange.com/questions/20803/how-does-ssl-work/ for an epically complete explanation. Public servers normally use a cert issued and signed by a public CA like Digicert, GoDaddy, LetsEncrypt/ISRG which are already in the standard default truststore (for Java in the file JRE/lib/security/cacerts) so no action is needed. If a server uses a cert from a off-brand or private CA, or a self-signed cert (from no CA at all), then (for Java) some cert in the chain must be added to the client truststore, or otherwise overridden; this is only required to be the server cert in the case where the server cert is self-signed (which is a chain by itself and has no relevant CA certs).
Java/JSSE implements this through an SSLContext which contains among other things a TrustManager, more specifically an X509ExtendedTrustManager, which is initialized from a truststore. You can create an SSLContext explicitly in code from any set of trusted certs (which doesn't even need to be from a file), or use the default context which uses the default truststore file, which defaults to the filename above unless overridden by a system property.
When the server cert chain is received it is passed to the context's TrustManager for validation; among (many!) other checks, at each level of a normal chain, or the single level of a self-signed cert, the JSSE TrustManager looks for an anchor cert with the same Subject and (Subject)PublicKey and if so uses it to validate the cert chain. Note that a normal (CA-issued) leaf cert can have Subject empty if Subject Alternative Name is used instead -- see rfc5280 and rfc2818 -- but a self-signed cert cannot, because it has Subject = Issuer and Issuer must not be empty. Certs for different entities (e.g. different servers) normally are expected to have different keys, although a single entity can have multiple certs with either the same key or different keys, and might correspond to multiple server names and/or addresses.
If the certificate is determined valid in general, for some TLS applications, notably HTTPS, the validator also checks it is for the correct server, specifically that the CommonName attribute in the Subject field, or an entry in the Subject Alternative Name extension if present -- which for public CAs for at least a decade it is, matches the host DNS name or IP adddress in the URL. In older versions of Java (through 6 IIRC) this was not done in JSSE but rather in the calling application(s) or library, such as HttpsURLConnection, which as a legacy still has the option to use its own HostnameVerifier instead.
All of this can be altered by using a custom TrustManager in place of the standard one, and some things like Apache HttpClient do so validly, but you will find (too many) answers here and some other Stacks recommending you 'solve' TLS errors by using a neutered TrustManager that just accepts any cert, regardless of whether it is actually valid and correct, and thus happily connects and sends sensitive data to, or accepts changes from, any attacker who manages to intercept the IP traffic, which is often pretty easy nowadays.
Alias is a way to directly access the certificate but your keystore has other information regarding the certificate as well. X.509 certificates have a field called SAN (Subject Alternative name) which contains the DNS information of the certificate. When you try to connect to a specific URL, the keystore is looked up for the corresponding DNS name in SAN and correct certificate is picked up.
I hope it clarifies your doubt about java not asking for the alias. Rest assured, there is nothing random in this process.
UPDATE: After I have found a solution, I edited the question to be more clear for future reference.
I've got a corporate (ie not well known) CA certificate from a company which provides us a web services to be called from Java. I added this CA certificate to default cacerts trust store (keytool -import -file cert.cer -alias myca -keystore jre/lib/security/cacerts), but connection to the service still fails with the dreaded "PKIX path building failed" message. I have checked that issuer field of the end server certificate is the same as in the CA certificate and also validity dates is ok.
I don't know how to explain this. I can think of the following reasons but I don't know which one is true:
I have noticed that when I add also the end server certificate to trust store, the connection is OK. Maybe cacerts by design don't work as I expect (ie all the certificates signed by an authority added there are considered valid), but instead I have to add all the end server certificates to a trust store including CA certificate of their issuer.
I have to add CA certificate in some other way - by different command, to different file etc.
Maybe the CA certificate is not correct and keytool refuses to consider it a certificate authority.
Maybe PKIX path building fails for other reason.
How can I debug this problem more to find an answer?
Details:
The end server certificate is wildcard certificate
There is no intermediate certificate, just root and the end certificate
I was facing the same problem with "PKIX path building failed" with Let's Encrypt signed certificates at a time Java didn't incorporate the Let's encrypt CA certificate in its default trust store.
My story is written in detail here: http://blog.novoj.net/2016/02/29/how-to-make-apache-httpclient-trust-lets-encrypt-certificate-authority/
At the end I was able to make Java trust "the end of the chain" server certificate by creating internal trust store embedded in my application that contains only root CA certificate (and the backup one).
I much more prefer creating internal application truststore than importing certificate in main Java trust store for two reasons:
you don't need another extra step in install procedure for initializing the global trust store
you limit "the trust" to you application and don't affect another applications running on the same JVM (or better you can even limit the trust to the certain instances of client objects in your application if required)
Maybe I had a different scenario than you're facing, so downvote me if I didn't get the point.
The trust store needs to contain the root certificate (the CA's cert).
I'm not sure if that's what you mean by "the last one I'm the chain", but the CA certificate should be the last one in the certificate chain presented by the server.
If your certificate is signed by a well-known CA, then the CA cert should be in the trust store, and if the server's certificate chain is set up properly, everything should just work.
If yours is a self-signed certificate, then the root certificate will not be in the trust store, and you will have to add it.
Thanks to #pedrofb comment I found out that the reason PKIX path fails is simply that the CA certificate I got is not the CA that signed the end certificate. What made it so complicated is the monstrous incompetence of company that gave me the CA certificate which obviously has two CAs with almost the same description (cn, o, st, c) which differs only in SN and which both issued the same wildcard certificate. Only after I became super paranoid and compared the SNs, I understood the problem.
When I create an SSLServerSocket in Java 7 the server correctly uses my server certificate and key. The certificate was issued by a sub-ca of a ca. Therefore the complete chain from the root cert to the server cert has four certificates.
The complete chain is present in the keystore/truststore.
However when a client connects the server always sends only the server certificate itself.
This also applies to Java based web servers like Jetty.
Because most clients have only the root ca certificate installed and not the two sub-ca certificates this is a big problem.
How can I force Java to send the full certificate chain in the SSL/TLS handshake?
A key entry in a keystore isn't just for a single certificate, but for a certificate chain (see KeyStore.setKeyEntry, which takes a Certificate[] chain parameter).
If you want a specific chain to be used, it needs to be set up as a chain in the entry where you have the certificate and its private key. Whether the intermediate certificates are also in the same keystore, in different entries doesn't really matter.
This is a very similar problem to getting a client to send the full client-certificate chain. The same keystore configuration steps should also work from a server point of view, as described in this question.
This question already has answers here:
Truststore and Keystore Definitions
(7 answers)
Closed 6 years ago.
I have a PKCS#12 file which I considered as a keystore file since it contains one key entry & one certificate entry.
In Android, I see people programmatically install keystore in the following way (The code is from Android developer blog):
byte[] keystore = . . (read from a PKCS#12 keystore)
Intent installIntent = KeyChain.createInstallIntent();
installIntent.putExtra(KeyChain.EXTRA_PKCS12, keystore);
startActivityForResult(installIntent, INSTALL_KEYSTORE_CODE);
I also see people programmatically install only the certificate wrapped inside keystore:
Intent intent = KeyChain.createInstallIntent();
intent.putExtra(KeyChain.EXTRA_CERTIFICATE, cert);
startActivity(intent);
Besides, I also see people install both the keystore & the certificate wrapped in keystore. For example, this article shows us how to first install keystore & then install the certificate wrapped in keystore programmatically.
I really get confused about when should I install keystore only & when should I install certificate (wrapped inside keystore) only ? And when should I install both ?? Could someone make me clear about this please?
For example, my keystore PKCS#12 file (mycert.p12) contains key/certificate pair, it is used to connect to VPN server. When should my android client install both keystore and certificate wrapped in the keystore ? When should client install only certificate wrapped in keystore? What are the differences ? I am quite confused about this.
I have a PKCS#12 file which I considered as a keystore file since it contains one key entry & one certificate entry.
Correct.
In Android, I see people programmatically install keystore in the following way ...
This is done when you have a keystore, i.e. a keypair and certificate.
I also see people programmatically install only the certificate wrapped inside keystore
This is done when you have someone else's certificate, typically a self-signed one, that isn't trusted by any of the default CA's (certificate authorities) that are already installed. You should never have to do this.
So note that you never do both with the same certificate, because the cases (the ownerships) are different. There can never be any doubt about which process is appropriate. If it's yours, import the keystore. If it's someone else's, import the certificate.
The ultimate normative reference for all this stuff is ITU Recommendation X.509.
Finally, some notes on the poor quality blog articles you have linked.
From Unifying key store access in ICS:
In the past, it was common practice for apps to maintain their own key
store if they needed to authenticate a secure SSL web server, or
authenticate the user to a server via a client certificate.
This is already incorrect.
To authenticate a web server you shouldn't need anything, if it has a CA-signed certificate. If it has a self-signed certificate you will need to import it into your truststore.
To authenticate yourself to a web server, you need a keystore containing your own private key and a certificate, preferably a CA-signed one. Otherwise the server has to import your self-signed certificate into its truststore, i.e. the converse of (1) above. Don't go down this path. Self-signed certificates are far more trouble than they are worth, which is nothing, as you can tell from the price you pay for them.
From Using ICS keychain API:
We first get the private key and certificate chain using the key alias
and then create and verify a signature to check if the key is actually
usable.
Complete nonsense. We already have the private key, the public key, and the certificate. They are already usable. Creating a signature and verifying it locally is just a complete waste of time.
Installing a CA certificate is not very different from installing a
PKCS#12 file: you load the certificate in a byte array and pass it as
an extra to the install intent.
The difference being that you use KeyChain.EXTRA_CERTIFICATE in the CA certificate case, and KeyChain.EXTRA_PKCS12 in the keystore case.
Since no one has answered you yet, I hope I can at least clarify some points from the blog article you have linked to.
In the past, it was common practice for apps to maintain their own key
store if they needed to authenticate a secure SSL web server, or
authenticate the user to a server via a client certificate.
These are the two basic use-cases right here:
If you are authenticating with a client certificate (which proves to the server that you are an authorized client), then you would only install a certificate.
If you are trying to verify the server's identity, then you will want to validate the server's certificate. In this case you will need a keystore installed (possibly a chain if you're not using self-signed certs). The private key in the keystore will be used to validate the server's certificate.
That second bit of code you have in your question was intended for creating a certificate chain (when you're NOT using self-signed certs):
We first get the private key and certificate chain using the key alias
and then create and verify a signature to check if the key is actually
usable. Since we are using a self-signed certificate the 'chain'
consists of a single entry, but for a certificate signed by a CA you
will need to find the actual end entity certificate in the returned
array.
Installing a CA certificate is not very different from installing a
PKCS#12 file: you load the certificate in a byte array and pass it as
an extra to the install intent.
Intent intent = KeyChain.createInstallIntent();
intent.putExtra(KeyChain.EXTRA_CERTIFICATE, cert);
startActivity(intent);
I hope this explanation helps! :)
With respect to an android or any other 'client', that is, an application, the following hold -
A truststore (public key only) is required whenever it needs to validate the certificate (or a certificate chain) that is sent across by the server during SSL communication (in case of ssl communication the server will always present its certificate to the client).
If the server's certificate is already signed by a trusted certificate authority (implying that certificate is already present in the java-runtime-truststore that can usually be found under $JAVA_HOME/jre/lib/security/cacerts), then this step is not required unless a customized SSLContext is being used (which also means that a customized TrustManager is being used).
For example SO's current certificate is signed by DigiCert identified by SHA1-Thumbprint : 5F:B7:EE:06:33:E2:59:DB:AD:0C:4C:9A:E6:D3:8F:1A:61:C7:DC:25 and would likely be present in the 'cacerts' truststore under the alias 'digicerthighassuranceevrootca'. If a java client were to make a request to https://stackoverflow.com then by default there would not be any specific keystore or truststore required for communication.
A keystore (private and public key) is generally required when the client is required to digitally-sign some data which is being posting to the server.
A common example is xml-signing, you can find a mention here
It is also required if the server expects the client to present its own certificate for authentication as part of two-way ssl handshake. From what I have come across this is not common.
Links :
Two Way SSL
SO's own Keystore and truststore post
When I create an SSLServerSocket in Java 7 the server correctly uses my server certificate and key. The certificate was issued by a sub-ca of a ca. Therefore the complete chain from the root cert to the server cert has four certificates.
The complete chain is present in the keystore/truststore.
However when a client connects the server always sends only the server certificate itself.
This also applies to Java based web servers like Jetty.
Because most clients have only the root ca certificate installed and not the two sub-ca certificates this is a big problem.
How can I force Java to send the full certificate chain in the SSL/TLS handshake?
A key entry in a keystore isn't just for a single certificate, but for a certificate chain (see KeyStore.setKeyEntry, which takes a Certificate[] chain parameter).
If you want a specific chain to be used, it needs to be set up as a chain in the entry where you have the certificate and its private key. Whether the intermediate certificates are also in the same keystore, in different entries doesn't really matter.
This is a very similar problem to getting a client to send the full client-certificate chain. The same keystore configuration steps should also work from a server point of view, as described in this question.