I am obtaining this exception (client certificate not found) when trying to
connect to a secure Web Service that requires a client certificate. I am
using a web service client automatically generated by axis2, using the
Eclipse wizard.
This is the calling code, that causes the exception in the last line:
System.setProperty("javax.net.ssl.trustStore","C:\\Archivos de programa\\Java\\jre7\\lib\\security\\cacerts");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
System.setProperty("javax.net.ssl.keyStore","D:\\Perfil Usuario\\internet\\Escritorio\\workspace\\certificados\\clientes.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
ServicioBoletinStub lala = new ServicioBoletinStub();
ConsultaDeCatalogo cons = new ConsultaDeCatalogo();
cons.setArgs0("SECCIONES");
ConsultaDeCatalogoResponse conResp = lala.consultaDeCatalogo(cons);
The client certificate is imported in the "clientes.jks" keystore, and all the
other required certificates for the authentication path are in "cacerts".
The only weird thing I had to do was to convert the client certificate from
.p12 to .cer, because keytool was complaining that the .p12 file was not an
x509 certificate. The .p12 file was encrypted with a password, but the .cer
file is not, so I am afraid that something was missing during the
conversion. I am very new to handling certificates so I do not know what I
am missing.
I also used SSLPoke to test the connection, and no errors were given.
Thank you very much.
Ok, the problem was in the import process of the client certificate, as suspected. Keytool was not importing the private key into the "clientes.jks" keystore.
I deleted everything from this keystore and followed this post (http://cunning.sharp.fm/2008/06/importing_private_keys_into_a.html) to import the client certificate plus the private key.
Anyway, I think Axis2 should have specified that the exception message was taken from the SOAP response message: I thought it was generating it by itself.
Related
I have written a Java client in SAAJ which works successfully when sending SOAP messages over HTTP however when I attempt to send any SOAP messages to a web service over HTTPS that requires a client certificate it doesnt work.
At the bottom of the page at the following link - SAAJ Security - it states the following:
From the SAAJ side, all you need to do is use URLs with HTTPS as the protocol. This will work only if the certificate was successfully imported into /jre/lib/security/cacerts; otherwise JSSE will not allow the connection
I imported the client certificate along with the associated root certificate into my Java cacerts as instructed to do so above and ran the program however I get the following error:
java.net.SocketException: Connection reset
I ran a wireshark trace on the traffic and noticed that the Java code isnt presenting a client certificate when asked to do so by the server therefore I have the following questions:
1) By only passing the HTTPS URL to the soapConnection.call() method along with importing the certs to my cacerts file, is this enough for authentication to occur i.e. does SAAJ handle this automatically? Or are there more steps required that arent described in the above link?
2) By importing the certificates into the cacerts file within my JAVA_HOME, does the Java SAAJ client automatically know to look here when calling the soapConnection.call()? Or do I need to explicitly tell my code what cacerts file to use?
3) If the Java SAAJ client is automatically using the cacerts file under my JAVA_HOME then how does it know what client certificate to use? Again, shouldnt I need to explicitly code this or does SAAJ handle this automatically?
Thanks in advance
I managed to figure this out. I used the following code:
static public void doTrustToCertificates() throws Exception
{
// Set truststore that contains root / intermediary certs
System.setProperty("javax.net.ssl.trustStore", "C:\\cert\\trusted.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
// Set keystore that contains private key
File pKeyFile = new File("C:\\cert\\privatekey.pfx");
String pKeyPassword = "Password01";
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream keyInput = new FileInputStream(pKeyFile);
keyStore.load(keyInput, pKeyPassword.toCharArray());
keyInput.close();
keyManagerFactory.init(keyStore, pKeyPassword.toCharArray());
// Set ssl context with private key and truststore details
SSLContext sc = SSLContext.getInstance("TLSv1");
sc.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
SSLSocketFactory sockFact = sc.getSocketFactory();
// Add ssl context to https connection
HttpsURLConnection.setDefaultSSLSocketFactory(sockFact);
}
Then called the doTrustToCertificates() method just before the soapConnection.call() method of SAAJ and it worked like so:
doTrustToCertificates();
SOAPMessage soapResponse = soapConnection.call(soapMsgXml, ENDPOINT_URL);
You weren't so instructed. The instructions are misleading, but they only apply to the situation where the server certificate is self-signed, and not at all to the situation where the client needs a certificate.
In this case, you need to create a client certificate in your client's keystore. Either you create a CSR and get it signed and import that back into the same keystore using the same alias as you started with when you generated the keypair and CSR, or else you need to generate a self-signed certificate (yuck) and export it and get it imported into the server's truststore.
You don't need to write any code. All you need to do is set the system properties javax.net.ssl.keyStore and friends to tell JSSE about your client keystore.
I need to integrate an application with external web service which forces to use https. Authors of this web service provided me with .crt file which I should use for making https requests. After some investigation I've found the following code which uses KeyStore class for secured https access:
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream instream = new FileInputStream(new File(file));
try {
trustStore.load(instream, password.toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext =
SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
SSLConnectionSocketFactory sslsf =
new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1.2"}, null,
BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
HttpClients.custom().setSSLSocketFactory(sslsf).build();
In this code KeyStore requires input stream along with password trustStore.load(instream, password.toCharArray());. However, as I understand, we don't need password when using .crt file. So this way of loading certificate is not suitable for me. At the same time, from what I've discovered so far, the code I provided here is the only way to configure HttpClient for using SSL certificate. Is there any workaround to configure HttpClient for using .crt certificate?
Thanks,
Andrey
I assume the Web service is providing you with a self-signed certificate (i.e. not signed by a well-known CA). If it is already signed by a well-known CA that is in Java's cacerts file, then you don't need to do anything.
Otherwise, you have a couple of options:
Import the certificate into global cacerts keystore
Launch your application with application-specific keystore
In either case, you first need to convert crt file into jks keystore that Java uses. You can do this by:
$ keytool -import -keystore mykeystore.jks -storepass horsestaple
Note that keytool requires the password (horsestaple above) to be supplied for creating a jks store. You can put anything there; as you mention, public website certificates do not need password protection, they are public after all.
If you are doing option 1, make a backup of your cacerts and supply the cacerts file instead of mykeystore.jks. See the link below for the location of cacerts. For this option, you are all set, your application should be connecting to the Web service via HTTPS without any additional configuration, since Java loads cacerts by default.
If you are doing option 2, which is probably preferred at least for testing phase, you need to run your application with this parameter:
-Djavax.net.ssl.trustStore=mykeystore.jks
This is a JVM parameter, so supply it appropriately. This depends on how you are running you application.
Note that you will only have the imported certificate in this case, so your other HTTPS connections will not work. You can avoid this by first copying the standard cacerts to a temporary location, importing the key into it and using that in the command above. That will give you all the standard certificates, plus the one you need.
A slight downside of option 2 is that if new certificates are added or revoked, your application-specific keystore will not be updated. If this is a concern, you can merge keystores on the fly, for example:
Registering multiple keystores in JVM
In either case, you should now be able to just do a standard URL fetch, such as in example given here:
https://blogs.oracle.com/java-platform-group/entry/diagnosing_tls_ssl_and_https
i.e.:
final URL url = new URL("https://example.com");
try(final InputStream in = url.openStream()){
//…
}
More information here:
Keytool and general certificates info
https://docs.oracle.com/cd/E19830-01/819-4712/ablqw/index.html
Cacerts location
http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html
More options for importing self-signed certificates
How to properly import a selfsigned certificate into Java keystore that is available to all Java applications by default?
Unless they provided you with their .crt file which should be used for validating connections to them, which would indicate they are using a self-signed certificate, they haven't provided you with anything useful. If you need a certificate as a client, the first thing you need is a public/private key pair, from which you generate a CSR, which you get signed. Nobody else can securely provide any of that except the signed certificate.
If they've provided their own (self-signed?) certificate, you need to load it into a truststore, not a keystore, via the keytool with the -trustcacerts option, and then tell Java to use that truststore, either via the javax.net.ssl.trustStore system property or by constructing your own TrustManager and feeding it to a custom SSLContext, as described in the JSSE Reference Guide.
I am creating a Java program to get information from a server but I have to perform a ssl handshake with the server from the Java program.
I have myfilercert.cer file certificate for authentication purpose but I have no idea how I can load that certificate in java so that the java program can perform 'handshake' with the server where I want to get information from. Where to begin?
What you need is the java keystore. The keystore is a repository of security certificates used in SSL encryption.
You can read here about the Server Authentication During SSL Handshake. This is a keystore tutorial.
As an alternative to keytool, i would suggest a tool with a Graphical User Interface called Portecle. You can use it to browse the contents of your .cer file and see what's in it.
It can be useful to know about the various certificate encodings. Also read about the X.509 standard.
This is an article on java keytool essentials (which is the oracle tool that works with the java keystore).
You can google and find a lot of resources that instruct you how to generate. I think you will want to keep the certificate at the application level.
Some SO questions that helped me along the way:
Trust Store vs Key Store - creating with keytool - important to know the difference between the trust manager and keymanager
Java HTTPS client certificate authentication
How to export private key from a keystore of self-signed certificate
What is difference between cacerts and keystore
How to connect to a secure website using SSL in Java with a pkcs12 file?
Received fatal alert: handshake_failure through SSLHandshakeException
How to configure trustStore for javax.net.ssl.trustStore on windows?
Good luck!
You can use Apache HttpClient (or just use the required classes from it to use SslContextBuilder, really), and then it'd be like so:
SSLContextBuilder sslContextBuilder = SSLContextBuilder.create();
sslContextBuilder.loadTrustMaterial(new File("yourTrustStore.jks"), "thePassWord");
SSLContext sslContext = sslContextBuilder.build();
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) (new URL("https://thesite.com").openConnection());
httpsURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());
But you need to create a keystore for your certificate, which can be done with keytool. If you need this for android, you'll need SpongyCastle library, and use that as a provider for KeyTool to create a BKS keystore instead of a JKS keystore; and you will need to explicitly open the KeyStore in Java.
KeyStore keyStore = KeyStore.getInstance("BKS",
BouncyCastleProvider.PROVIDER_NAME);
byteArrayInputStream = new ByteArrayInputStream(keyStoreBytes);
keyStore.load(byteArrayInputStream, keyStorePassword);
Certificate[] certificates = keyStore.getCertificateChain("theCertAlias");
Certificate certificate = certificates[0];
I need to consume some HTTPS web services. To use the certificate, I'm setting (javax.net.ssl) trustStore, trustStorePassword, keyStore and keyStorePassword properties through the System.setProperty.
If I do new URL([HTTPS...WSDL]).openStream(), it works. So, I'm sure that trust and keystore properties are ok (without setting those properties, my routine failed).
The problem is when I try to consume the service, I'm getting the famous
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
exception.
Anyone have an idea of what I need to do?
Thanks a lot.
There are three possible failures:
You are using a self-signed certificate.
Your certificate has been signed by a common CA (Certificate Authority) using an intermediate certificate, but their intermediate certificate is not served by your web server.
Both the server certificate and the CA intermediate certificate are served by your web server, but the Java truststore does not contain the root certificate.
To solve the last step use:
Import CA root cert
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.