Use https to call a webservice - java

I am currently working on a webservice that uses http! I have been asked to change (to use ) https instead to call this webservice!
I am using eclipse kepler and JBoss EAP6.1
I found in the internet that I have to create a keystore and edit the server.xml file.
The thing is that i can't find the xml file in this JBOss version [ i have a standalone.xml file is it the same ? ]
and for the generation of the keystore where do i have to do it ?
Thank you for you ansewers!
if I am on the wrong way, would you please re-direct me to right path ?
Thanks again !

Get the certificate of the HTTPS url. (You can do it by typing the URL in the browser and then extracting the certificate from the browser certificate installation location). After this add this certificate to the JRE of your application which is used by JBOSS server. Most probably this will be the JRE you have given in the system environment. You can google to get how to install certificate in the keystore. May be this will work.

You're calling a remote webservice via https, right?
Ok, you could import the certificate of the remote service in the keystore (plenty of guides about that, look at this other question for an example)
OR
You can bypass the whole https certificate thing (launch this static method before the remote call):
/**
* Bypassing SSL certificate check
*
* #throws Exception
*/
public static void doTrustToCertificates() throws Exception {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HostnameVerifier hv = new HostnameVerifier() {
#Override
public boolean verify(String urlHostName, SSLSession session) {
if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
logger.warn("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
}
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}

In addition to answer of #Rahul, you can import certificate (.cer) file using following command on command prompt for windows OS :
(Assuming you have set required Java paths)
keytool -importcert -file <path of certificate>\<YourCertificateName>.cer -keystore D:\java\jdk1.7.0_40\jre\lib\security\cacerts -alias <certificateAliasName> -storepass <Password>
usually default <password> is 'changeit'.
In case webservice is used for third party client then you can use HttpClient to interact. I am not sure what kind of operation you are performing with that webservice. I assume you want to send some xml to that URL. You can refer following code :
HttpPost httppost = new HttpPost(url);
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(username, password));
CloseableHttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(credentialsProvider).build();
StringEntity entity = null;
try {
entity = new StringEntity(xmlToSend);
} catch (UnsupportedEncodingException e) {
LOG.error("Unsupported Encoding ", e);
}
entity.setContentType("text/xml");
httppost.setEntity(entity);
try{
CloseableHttpResponse response = client.execute(httppost);
returnCode = response.getStatusLine().getStatusCode();
EntityUtils.consume(entity);
LOG.debug("HttpResponse :" + EntityUtils.toString(response.getEntity()));
}catch(IOException e){
LOG.error("Error occured while sending the xml");
}

Related

Unable to find valid certification path to requested target, trying to use a SMS api via https request

I'm trying to send a SMS from java using an API.
I just need to make an https request with parameters written in the URL.
https://notificame.claro.com.hn/api/http/send_to_contact?msisdn504999999=&message=mymessage&api_key=00000&id=000
If I copy the resulting url and paste it directly on my browser, it sends the SMS.
I have a working code using this on PHP, but I need it working on JAVA
I've tried this:
try
{
HttpsURLConnection con;
URL url = new URL(pre_url);
con = (HttpsURLConnection) url.openConnection();
System.out.println(url.getContent());
} catch (MalformedURLException ex) {
Logger.getLogger(crearUsuarioBean.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(crearUsuarioBean.class.getName()).log(Level.SEVERE, null, ex);
}
but Im getting this error:
Severe: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)...
I also followed some instructions using keytool to add the certificate with .cer to cacerts but had no success. I have two folders, jdk1.8.0_131 and jre1.8.0_131, so I tried installing the certificate on both already. Still no success.
This is the code that makes the job for me on PHP:
function enviarMensaje($cel, $solicitud, $id){
$mensaje="My message";
$url = "https://notificame.claro.com.hn/api/http/send_to_contact?msisdn=504".$cel."&message=".$mensaje."&api_key=MyAppiKey&id=".$id;
$url = str_replace(" ", "%20", $url);
file($url);
echo $url."<br />\n";
}
I just need something simple like this.
Im working on JSF2.2 using Primefaces
Or maybe some way to call a simple php code directly from java.
Help please.
After days with the same problem I finally got a solution that might work for some of you.
The trick is to create a TrustManager, that way it can ignore all the SSL stuff.
I added this code:
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
after this any connection made to an HTTPS url will work.
I found the solution here:
https://nakov.com/blog/2009/07/16/disable-certificate-validation-in-java-ssl-connections/

SSLContextBuilder fails to loadKeyMaterial

I'm trying to use Apache httpclient-4.5.5 (with httpcore-4.4.9) to obtain the server certificate from a server that applies 'mutual SSL'. I'm creating an SSLContext as follows:
final String keystorePass = Configuration.getInstance().getItem(EmittentProperties.keystorePass);
final String kmopEncKeyPass = Configuration.getInstance().getItem(EmittentProperties.kmopEncKeyPass);
final String kmopEncKeyAlias = Configuration.getInstance().getItem(EmittentProperties.kmopEncKeyAlias);
//1.Create the SSLContext and SSLConnectionSocketFactory
SSLContext sslContext;
try {
sslContext = SSLContexts.custom()
.loadKeyMaterial(keystoreFile.getFile(), keystorePass.toCharArray(), kmopEncKeyPass.toCharArray(), new PrivateKeyStrategy() {
#Override
public String chooseAlias(Map<String, PrivateKeyDetails> aliases, Socket socket) {
return kmopEncKeyAlias;
}})
.loadTrustMaterial(new TrustStrategy() {
#Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}})
.build();
} catch (Exception e) {
logger.error("Error initializing SSLContext: " + e.getMessage(), e);
throw new FatalException(e.getMessage(), e);
}
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
However it always returns "java.security.UnrecoverableKeyException: Cannot recover key" on the loadKeyMaterial(...).
While debugging this method I can see that he succesfully loads the keystore and that it contains the keys I created in it. However when subsequently trying to load the key entry associated with the password kmopEncKeyPass it returns the UnrecoverableKeyException.
My (JKS) keystore has two (self-signed) entries 'kmop-enc' used for the SSL handshake and 'kmop-sign' for SSL signing of messages. The password for the alias kmop-enc (parameter kmopEncKeyPass) is the same as the password for the keystore (parameter keystorePass). With the custom PrivateKeyStrategy I want to make sure 'kmop-enc' is returned as alias and not 'kmop-sign' (which has a different password).
Verifying my keystore with keystore-explorer.org shows everything is OK with my keystore. Any ideas on why it throws an UnrecoverableKeyException?
Needed to add .setKeyStoreType("JKS") before calling .loadKeyMaterial(...).

How to make client trust self signed certificate Java SSL [duplicate]

It looks like a standard question, but I couldn't find clear directions anywhere.
I have java code trying to connect to a server with probably self-signed (or expired) certificate. The code reports the following error :
[HttpMethodDirector] I/O exception (javax.net.ssl.SSLHandshakeException) caught
when processing request: sun.security.validator.ValidatorException: PKIX path
building failed: sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
As I understand it, I have to use keytool and tell java that it's OK to allow this connection.
All instructions to fix this problem assume I'm fully proficient with keytool, such as
generate private key for server and import it into keystore
Is there anybody who could post detailed instructions?
I'm running unix, so bash script would be best.
Not sure if it's important, but code executed in jboss.
You have basically two options here: add the self-signed certificate to your JVM truststore or configure your client to
Option 1
Export the certificate from your browser and import it in your JVM truststore (to establish a chain of trust):
<JAVA_HOME>\bin\keytool -import -v -trustcacerts
-alias server-alias -file server.cer
-keystore cacerts.jks -keypass changeit
-storepass changeit
Option 2
Disable Certificate Validation (code from Example Depot):
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (GeneralSecurityException e) {
}
// Now you can access an https URL without having the certificate in the truststore
try {
URL url = new URL("https://hostname/index.html");
} catch (MalformedURLException e) {
}
Note that I do not recommend the Option #2 at all. Disabling the trust manager defeats some parts of SSL and makes you vulnerable to man in the middle attacks. Prefer Option #1 or, even better, have the server use a "real" certificate signed by a well known CA.
There's a better alternative to trusting all certificates: Create a TrustStore that specifically trusts a given certificate and use this to create a SSLContext from which to get the SSLSocketFactory to set on the HttpsURLConnection. Here's the complete code:
File crtFile = new File("server.crt");
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));
// Or if the crt-file is packaged into a jar file:
// CertificateFactory.getInstance("X.509").generateCertificate(this.class.getClassLoader().getResourceAsStream("server.crt"));
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("server", certificate);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
You can alternatively load the KeyStore directly from a file or retrieve the X.509 Certificate from any trusted source.
Note that with this code, the certificates in cacerts will not be used. This particular HttpsURLConnection will only trust this specific certificate.
Apache HttpClient 4.5 supports accepting self-signed certificates:
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(new TrustSelfSignedStrategy())
.build();
SSLConnectionSocketFactory socketFactory =
new SSLConnectionSocketFactory(sslContext);
Registry<ConnectionSocketFactory> reg =
RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", socketFactory)
.build();
HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse sslResponse = httpClient.execute(httpGet);
This builds an SSL socket factory which will use the TrustSelfSignedStrategy, registers it with a custom connection manager then does an HTTP GET using that connection manager.
I agree with those who chant "don't do this in production", however there are use-cases for accepting self-signed certificates outside production; we use them in automated integration tests, so that we're using SSL (like in production) even when not running on the production hardware.
I chased down this problem to a certificate provider that is not part of the default JVM trusted hosts as of JDK 8u74. The provider is www.identrust.com, but that was not the domain I was trying to connect to. That domain had gotten its certificate from this provider. See Will the cross root cover trust by the default list in the JDK/JRE? -- read down a couple entries. Also see Which browsers and operating systems support Let’s Encrypt.
So, in order to connect to the domain I was interested in, which had a certificate issued from identrust.com I did the following steps. Basically, I had to get the identrust.com (DST Root CA X3) certificate to be trusted by the JVM. I was able to do that using Apache HttpComponents 4.5 like so:
1: Obtain the certificate from indettrust at Certificate Chain Download Instructions. Click on the DST Root CA X3 link.
2: Save the string to a file named "DST Root CA X3.pem". Be sure to add the lines "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----" in the file at the beginning and the end.
3: Create a java keystore file, cacerts.jks with the following command:
keytool -import -v -trustcacerts -alias IdenTrust -keypass yourpassword -file dst_root_ca_x3.pem -keystore cacerts.jks -storepass yourpassword
4: Copy the resulting cacerts.jks keystore into the resources directory of your java/(maven) application.
5: Use the following code to load this file and attach it to the Apache 4.5 HttpClient. This will solve the problem for all domains that have certificates issued from indetrust.com util oracle includes the certificate into the JRE default keystore.
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new File(CalRestClient.class.getResource("/cacerts.jks").getFile()), "yourpasword".toCharArray(),
new TrustSelfSignedStrategy())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
When the project builds then the cacerts.jks will be copied into the classpath and loaded from there. I didn't, at this point in time, test against other ssl sites, but if the above code "chains" in this certificate then they will work too, but again, I don't know.
Reference: Custom SSL context and How do I accept a self-signed certificate with a Java HttpsURLConnection?
Rather than setting the default socket factory (which IMO is a bad thing) - yhis will just affect the current connection rather than every SSL connection you try to open:
URLConnection connection = url.openConnection();
// JMD - this is a better way to do it that doesn't override the default SSL factory.
if (connection instanceof HttpsURLConnection)
{
HttpsURLConnection conHttps = (HttpsURLConnection) connection;
// Set up a Trust all manager
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager()
{
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType)
{
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType)
{
}
} };
// Get a new SSL context
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
// Set our connection to use this SSL context, with the "Trust all" manager in place.
conHttps.setSSLSocketFactory(sc.getSocketFactory());
// Also force it to trust all hosts
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// and set the hostname verifier.
conHttps.setHostnameVerifier(allHostsValid);
}
InputStream stream = connection.getInputStream();
Trust all SSL certificates:-
You can bypass SSL if you want to test on the testing server.
But do not use this code for production.
public static class NukeSSLCerts {
protected static final String TAG = "NukeSSLCerts";
public static void nuke() {
try {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] myTrustedAnchors = new X509Certificate[0];
return myTrustedAnchors;
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
} catch (Exception e) {
}
}
}
Please call this function in onCreate() function in Activity or in your Application Class.
NukeSSLCerts.nuke();
This can be used for Volley in Android.
Download your self-signed certificate with your browser from target page and add it to default storage with default password:
keytool -import -v -trustcacerts -file selfsigned.crt -alias myserver -keystore /etc/alternatives/jre/lib/security/cacerts -storepass changeit
Use file $JAVA_HOME/jre/lib/security/cacerts , my example here is from Oracle linux 7.7 .
If 'they' are using a self-signed certificate it is up to them to take the steps required to make their server usable. Specifically that means providing their certificate to you offline in a trustworthy way. So get them to do that. You then import that into your truststore using the keytool as described in the JSSE Reference Guide. Don't even think about the insecure TrustManager posted here.
EDIT For the benefit of the seventeen (!) downvoters, and numerous commenters below, who clearly have not actually read what I have written here, this is not a jeremiad against self-signed certificates. There is nothing wrong with self-signed certificates when implemented correctly. But, the correct way to implement them is to have the certificate delivered securely via an offline process, rather than via the unauthenticated channel they are going to be used to authenticate. Surely this is obvious? It is certainly obvious to every security-aware organization I have ever worked for, from banks with thousands of branches to my own companies. The client-side code-base 'solution' of trusting all certificates, including self-signed certificates signed by absolutely anybody, or any arbitary body setting itself up as a CA, is ipso facto not secure. It is just playing at security. It is pointless. You are having a private, tamperproof, reply-proof, injection-proof conversation with ... somebody. Anybody. A man in the middle. An impersonator. Anybody. You may as well just use plaintext.
I had the issue that I was passing a URL into a library which was calling url.openConnection(); I adapted jon-daniel's answer,
public class TrustHostUrlStreamHandler extends URLStreamHandler {
private static final Logger LOG = LoggerFactory.getLogger(TrustHostUrlStreamHandler.class);
#Override
protected URLConnection openConnection(final URL url) throws IOException {
final URLConnection urlConnection = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile()).openConnection();
// adapated from
// https://stackoverflow.com/questions/2893819/accept-servers-self-signed-ssl-certificate-in-java-client
if (urlConnection instanceof HttpsURLConnection) {
final HttpsURLConnection conHttps = (HttpsURLConnection) urlConnection;
try {
// Set up a Trust all manager
final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
#Override
public void checkClientTrusted(final java.security.cert.X509Certificate[] certs, final String authType) {
}
#Override
public void checkServerTrusted(final java.security.cert.X509Certificate[] certs, final String authType) {
}
} };
// Get a new SSL context
final SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
// Set our connection to use this SSL context, with the "Trust all" manager in place.
conHttps.setSSLSocketFactory(sc.getSocketFactory());
// Also force it to trust all hosts
final HostnameVerifier allHostsValid = new HostnameVerifier() {
#Override
public boolean verify(final String hostname, final SSLSession session) {
return true;
}
};
// and set the hostname verifier.
conHttps.setHostnameVerifier(allHostsValid);
} catch (final NoSuchAlgorithmException e) {
LOG.warn("Failed to override URLConnection.", e);
} catch (final KeyManagementException e) {
LOG.warn("Failed to override URLConnection.", e);
}
} else {
LOG.warn("Failed to override URLConnection. Incorrect type: {}", urlConnection.getClass().getName());
}
return urlConnection;
}
}
Using this class it is possible to create a new URL with:
trustedUrl = new URL(new URL(originalUrl), "", new TrustHostUrlStreamHandler());
trustedUrl.openConnection();
This has the advantage that it is localized and not replacing the default URL.openConnection.
The accepted answer is fine, but I'd like to add something to this as I was using IntelliJ on Mac and couldn't get it to work using the JAVA_HOME path variable.
It turns out Java Home was different when running the application from IntelliJ.
To figure out exactly where it is, you can just do System.getProperty("java.home") as that's where the trusted certificates are read from.
The accepted answer needs an Option 3
ALSO
Option 2 is TERRIBLE. It should NEVER be used (esp. in production) since it provides a FALSE sense of security. Just use HTTP instead of Option 2.
OPTION 3
Use the self-signed certificate to make the Https connection.
Here is an example:
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.KeyStore;
/*
* Use a SSLSocket to send a HTTP GET request and read the response from an HTTPS server.
* It assumes that the client is not behind a proxy/firewall
*/
public class SSLSocketClientCert
{
private static final String[] useProtocols = new String[] {"TLSv1.2"};
public static void main(String[] args) throws Exception
{
URL inputUrl = null;
String certFile = null;
if(args.length < 1)
{
System.out.println("Usage: " + SSLSocketClient.class.getName() + " <url>");
System.exit(1);
}
if(args.length == 1)
{
inputUrl = new URL(args[0]);
}
else
{
inputUrl = new URL(args[0]);
certFile = args[1];
}
SSLSocket sslSocket = null;
PrintWriter outWriter = null;
BufferedReader inReader = null;
try
{
SSLSocketFactory sslSocketFactory = getSSLSocketFactory(certFile);
sslSocket = (SSLSocket) sslSocketFactory.createSocket(inputUrl.getHost(), inputUrl.getPort() == -1 ? inputUrl.getDefaultPort() : inputUrl.getPort());
String[] enabledProtocols = sslSocket.getEnabledProtocols();
System.out.println("Enabled Protocols: ");
for(String enabledProtocol : enabledProtocols) System.out.println("\t" + enabledProtocol);
String[] supportedProtocols = sslSocket.getSupportedProtocols();
System.out.println("Supported Protocols: ");
for(String supportedProtocol : supportedProtocols) System.out.println("\t" + supportedProtocol + ", ");
sslSocket.setEnabledProtocols(useProtocols);
/*
* Before any data transmission, the SSL socket needs to do an SSL handshake.
* We manually initiate the handshake so that we can see/catch any SSLExceptions.
* The handshake would automatically be initiated by writing & flushing data but
* then the PrintWriter would catch all IOExceptions (including SSLExceptions),
* set an internal error flag, and then return without rethrowing the exception.
*
* This means any error messages are lost, which causes problems here because
* the only way to tell there was an error is to call PrintWriter.checkError().
*/
sslSocket.startHandshake();
outWriter = sendRequest(sslSocket, inputUrl);
readResponse(sslSocket);
closeAll(sslSocket, outWriter, inReader);
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
closeAll(sslSocket, outWriter, inReader);
}
}
private static PrintWriter sendRequest(SSLSocket sslSocket, URL inputUrl) throws IOException
{
PrintWriter outWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(sslSocket.getOutputStream())));
outWriter.println("GET " + inputUrl.getPath() + " HTTP/1.1");
outWriter.println("Host: " + inputUrl.getHost());
outWriter.println("Connection: Close");
outWriter.println();
outWriter.flush();
if(outWriter.checkError()) // Check for any PrintWriter errors
System.out.println("SSLSocketClient: PrintWriter error");
return outWriter;
}
private static void readResponse(SSLSocket sslSocket) throws IOException
{
BufferedReader inReader = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));
String inputLine;
while((inputLine = inReader.readLine()) != null)
System.out.println(inputLine);
}
// Terminate all streams
private static void closeAll(SSLSocket sslSocket, PrintWriter outWriter, BufferedReader inReader) throws IOException
{
if(sslSocket != null) sslSocket.close();
if(outWriter != null) outWriter.close();
if(inReader != null) inReader.close();
}
// Create an SSLSocketFactory based on the certificate if it is available, otherwise use the JVM default certs
public static SSLSocketFactory getSSLSocketFactory(String certFile)
throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException
{
if (certFile == null) return (SSLSocketFactory) SSLSocketFactory.getDefault();
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(new File(certFile)));
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("server", certificate);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
return sslContext.getSocketFactory();
}
}
This is the first ancient question with far too many answers where I think I can provide a more helpful idea: This option is what I would use if the server owner refuses to provide their certificate to me offline in a trustworthy way:
Retrieve the cert from the server itself (using command line tools instead of the browser)
Add that cert to the java keystore to trust it. You will be displayed with the certificate's details to verify it.
# HOSTNAME_PORT is the host that you want to connect to - example: HOSTNAME_PORT=stackoverflow.com:443
HOSTNAME_PORT=hostname_part_of_url_without_https:port
# whatever you want to call the key within the Java key store
MY_KEY_ALIAS=the_key_I_added_with_help_from_stackoverflow
openssl s_client -showcerts -connect $HOSTNAME_PORT </dev/null 2>/dev/null|openssl x509 -outform PEM >mycertfile.pem
sudo keytool -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts/pki/java/cacerts -storepass changeit -importcert -alias $MY_KEY_ALIAS -file mycertfile.pem
Type in yes when prompted, but only if you really trust the certificate displayed to you and want to add it to the global java keystore of your computer.
FYI:$JAVA_HOME/jre/lib/security/cacerts
is in my case (CentOS 7) pointing at:
/etc/pki/java/cacerts
This is not a solution to the complete problem but oracle has good detailed documentation on how to use this keytool. This explains how to
use keytool.
generate certs/self signed certs using keytool.
import generated certs to java clients.
https://docs.oracle.com/cd/E54932_01/doc.705/e54936/cssg_create_ssl_cert.htm#CSVSG178
Instead of using keytool as suggested by the top comment, on RHEL you can use update-ca-trust starting in newer versions of RHEL 6. You'll need to have the cert in pem format. Then
trust anchor <cert.pem>
Edit /etc/pki/ca-trust/source/cert.p11-kit and change "certificate category: other-entry" to "certificate category: authority". (Or use sed to do this in a script.) Then do
update-ca-trust
A couple caveats:
I couldn't find "trust" on my RHEL 6 server and yum didn't offer to install it. I ended up using it on an RHEL 7 server and copying the .p11-kit file over.
To make this work for you, you may need to do update-ca-trust enable. This will replace /etc/pki/java/cacerts with a symbolic link pointing to /etc/pki/ca-trust/extracted/java/cacerts. (So you might want to back up the former first.)
If your java client uses cacerts stored in some other location, you'll want to manually replace it with a symlink to /etc/pki/ca-trust/extracted/java/cacerts, or replace it with that file.
The variant in Kotlin
#SuppressLint("CustomX509TrustManager", "TrustAllX509TrustManager")
fun ignoreSsl() {
val trustAllCerts: Array<TrustManager> = arrayOf(
object : X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate>? = null
override fun checkClientTrusted(certs: Array<X509Certificate?>?, authType: String?) {}
override fun checkServerTrusted(certs: Array<X509Certificate?>?, authType: String?) {}
})
val sc = SSLContext.getInstance("SSL")
sc.init(null, trustAllCerts, SecureRandom())
HttpsURLConnection.setDefaultSSLSocketFactory(sc.socketFactory)
}
Late to the party, you can do so by disabling it at the RestTemplate level.
Note that this TrustStrategy will trust all certificates, and you disable hostname verification with NoopHostnameVerifier().
public RestTemplate getRestTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
TrustStrategy acceptingTrustStrategy = (x509Certificates, s) -> true;
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return new RestTemplate(requestFactory);
}
Inspired by below annser, I find a way to trust self-signed and keep trust default CA.
File file = new File(System.getProperty("java.home"), "lib/security/cacerts");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(file), "changeit".toCharArray());
InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("testCer.cer");
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(resourceAsStream);
keyStore.setCertificateEntry("my-server-alias", certificate);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContexts.createDefault();
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
// check domain
// SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
// not check domain
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext,
new String[]{"TLSv1","TLSv1.1","TLSv1.2","SSLv3"},null, NoopHostnameVerifier.INSTANCE);
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
factory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(factory);
I was running into this issue because the maven repo where the dependencies are coming from is my local server with a self-signed certificate and a self-signed CA cert. To resolve the error, I had to run these two commands:
<my_java_install_dir>\bin\keytool.exe -importcert -file <my-self-signed-CA-cert>.crt -keystore <my_java_install_dir>\lib\security\cacerts -alias my-CA-cert
and then
<my_java_install_dir>\jdk11.0.14_10\bin\keytool.exe -importcert -file <my-self-signed-maven-repo-cert>.crt -keystore <my_java_install_dir>\lib\security\cacerts -alias my-maven-repo-cert

allowUnsafeRenegotiation, but still CertificateException

I try to use SOAPConnection to call https, and I have already point to keystore and truststore as follow:
System.setProperty("javax.net.ssl.keyStore", "C:/kei/tasks/MIP/Cert/ccc_acp.keystore");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
System.setProperty("javax.net.ssl.trustStore", "C:/kei/tasks/MIP/Cert/trusteaistore.keystore");
System.setProperty("javax.net.ssl.trustStorePassword", "password");
System.setProperty("javax.net.debug", "all");
but I still get the
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present
I google and find the follow temporary solution
System.setProperty( "sun.security.ssl.allowUnsafeRenegotiation", "true" );
but even I set allowUnsafeRenegotation to true, I still get the
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present
And I try to use SOAPUI 5.1.3, and in preference> ssl, I set the keystore and keystore password (but no place to set truststore), this time I can connect to my target server through https!
so
1) why soapUI 5.1.3 does not need to set truststore (but only keystore), but still can connect to https server?
2) why use system property to point to the same keystore, but I cannot connect to https server using SOAPConnection?
3) why I set allowUnsafeRenegotitation system property to true, but it seems it still check the public cert. of the https server, and return CertificateException?
***************** edit on 15/5/2015
I post the code here
public static void main(String args[]){
System.setProperty("javax.net.ssl.keyStore", "C:/kei/tasks/MIP/Cert/ccc_acp.keystore");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
MipCccSoapTest mipCccSoapTest = new MipCccSoapTest();
mipCccSoapTest.testHttpConnection();
}
private void testHttpConnection(){
try{
doTrustToCertificates();
URL url = new URL("https://10.7.3.43:9443/iboss/CustomerCareM1");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
HttpsURLConnection.getDefaultSSLSocketFactory();
System.out.println("ResponseCoede ="+conn.getResponseCode());
}catch(Exception ex){
ex.printStackTrace();
}
System.exit(0);
//end testing
}
// trusting all certificate
public void doTrustToCertificates() throws Exception {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
}
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
and I get the following error
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at sun.security.ssl.Alerts.getSSLException(Unknown Source)
but the keystore should be correct as I use the same keystore in SOAPUI 5.1.3 which can successfully call the server.
**************** edit on 18/5/2015 *************
After I comment out the following code
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
it can connect to the https server now.
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present
This is a problem with the servers certificate. You need to fix it there by adding a subject alternative section with the proper information so that it can be successfully validated. It has nothing to do with the trust chain, so no changes to keyStore or trustStore help. More information might be given if the servers URL or certificate would be known.
System.setProperty( "sun.security.ssl.allowUnsafeRenegotiation", "true" );
This is a TLS protocol level thing and has nothing to do with certificate validation.
In case you cannot fix the servers certificate see SSLHandshakeException: No subject alternative names present for a possible workaround (first hit when googling for this error message!).

How to handle invalid SSL certificates with Apache HttpClient? [duplicate]

This question already has answers here:
Resolving javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed Error?
(33 answers)
Closed 3 years ago.
I know, there are many different questions and so many answers about this problem... But I can't understand...
I have: ubuntu-9.10-desktop-amd64 + NetBeans6.7.1 installed "as is" from off. rep.
I need connecting to some site over the HTTPS. For this I use Apache's HttpClient.
From tutorial I read:
"Once you have JSSE correctly installed, secure HTTP communication over SSL should be as
simple as plain HTTP communication." And some example:
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.verisign.com/");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} finally {
httpget.releaseConnection();
}
By now, I write this:
HttpClient client = new HttpClient();
HttpMethod get = new GetMethod("https://mms.nw.ru");
//get.setDoAuthentication(true);
try {
int status = client.executeMethod(get);
System.out.println(status);
BufferedInputStream is = new BufferedInputStream(get.getResponseBodyAsStream());
int r=0;byte[] buf = new byte[10];
while((r = is.read(buf)) > 0) {
System.out.write(buf,0,r);
}
} catch(Exception ex) {
ex.printStackTrace();
}
As a result I have a set of errors:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1627)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:204)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:198)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:994)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:142)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:533)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:471)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:904)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1132)
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:643)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:78)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:828)
at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2116)
at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)
at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)
at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
at simpleapachehttp.Main.main(Main.java:41)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:302)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:205)
at sun.security.validator.Validator.validate(Validator.java:235)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:147)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:230)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:270)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:973)
... 17 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:191)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:255)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:297)
... 23 more
What have I to do to create simplest SSL connection?
(Probably without KeyManager and Trust manager etc. while.)
https://mms.nw.ru uses a self-signed certificate that's not in the default trust manager set. To resolve the issue, do one of the following:
Configure SSLContext with a TrustManager that accepts any certificate (see below).
Configure SSLContext with an appropriate trust store that includes your certificate.
Add the certificate for that site to the default Java trust store.
Here's a program that creates a (mostly worthless) SSL Context that accepts any certificate:
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class SSLTest {
public static void main(String [] args) throws Exception {
// configure the SSLContext with a TrustManager
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
SSLContext.setDefault(ctx);
URL url = new URL("https://mms.nw.ru");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
System.out.println(conn.getResponseCode());
conn.disconnect();
}
private static class DefaultTrustManager implements X509TrustManager {
#Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
#Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
}
https://mms.nw.ru likely uses a certificate not issued by a certification authority. Consequently, you need to add the certificate to your trusted Java key store as explained in unable to find valid certification path to requested target:
When working on a client that works
with an SSL enabled server running in
https protocol, you could get error
'unable to find valid certification
path to requested target' if the
server certificate is not issued by
certification authority, but a self
signed or issued by a private CMS.
Don't panic. All you need to do is to
add the server certificate to your
trusted Java key store if your client
is written in Java. You might be
wondering how as if you can not access
the machine where the server is
installed. There is a simple program
can help you. Please download the Java
program and run
% java InstallCert _web_site_hostname_
This program opened a connection to
the specified host and started an SSL
handshake. It printed the exception
stack trace of the error that occured
and shows you the certificates used by
the server. Now it prompts you add the
certificate to your trusted KeyStore.
If you've changed your mind, enter
'q'. If you really want to add the
certificate, enter '1', or other
numbers to add other certificates,
even a CA certificate, but you usually
don't want to do that. Once you have
made your choice, the program will
display the complete certificate and
then added it to a Java KeyStore named
'jssecacerts' in the current
directory.
To use it in your program, either
configure JSSE to use it as its trust
store or copy it into your
$JAVA_HOME/jre/lib/security directory.
If you want all Java applications to
recognize the certificate as trusted
and not just JSSE, you could also
overwrite the cacerts file in that
directory.
After all that, JSSE will be able to
complete a handshake with the host,
which you can verify by running the
program again.
To get more details, you can check out
Leeland's blog No more 'unable to find
valid certification path to requested
target'
In addition to Pascal Thivent's correct answer, another way is to save the certificate from Firefox (View Certificate -> Details -> export) or openssl s_client and import it into the trust store.
You should only do this if you have a way to verify that certificate. Failing that, do it the first time you connect, it will at least give you an error if the certificate changes unexpectedly on subsequent connections.
To import it in a trust store, use:
keytool -importcert -keystore truststore.jks -file servercert.pem
By default, the default trust store should be $JAVA_HOME/jre/lib/security/cacerts and its password should be changeit, see JSSE Reference guide for details.
If you don't want to allow that certificate globally, but only for these connections, it's possible to create an SSLContext for it:
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream("/.../truststore.jks");
ks.load(fis, null);
// or ks.load(fis, "thepassword".toCharArray());
fis.close();
tmf.init(ks);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
Then, you need to set it up for Apache HTTP Client 3.x by implementing one if its SecureProtocolSocketFactory to use this SSLContext. (There are examples here).
Apache HTTP Client 4.x (apart from the earliest version) has direct support for passing an SSLContext.
For Apache HttpClient 4.5+ & Java8:
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial((chain, authType) -> true).build();
SSLConnectionSocketFactory sslConnectionSocketFactory =
new SSLConnectionSocketFactory(sslContext, new String[]
{"SSLv2Hello", "SSLv3", "TLSv1","TLSv1.1", "TLSv1.2" }, null,
NoopHostnameVerifier.INSTANCE);
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(sslConnectionSocketFactory)
.build();
But if your HttpClient use a ConnectionManager for seeking connection, e.g. like this:
PoolingHttpClientConnectionManager connectionManager = new
PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
The HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory) has no effect, the problem is not resolved.
Because that the HttpClient use the specified connectionManager for seeking connection and the specified connectionManager haven't register our customized SSLConnectionSocketFactory. To resolve this, should register the The customized SSLConnectionSocketFactory in the connectionManager. The correct code should like this:
PoolingHttpClientConnectionManager connectionManager = new
PoolingHttpClientConnectionManager(RegistryBuilder.
<ConnectionSocketFactory>create()
.register("http",PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslConnectionSocketFactory).build());
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
The Apache HttpClient 4.5 way:
org.apache.http.ssl.SSLContextBuilder sslContextBuilder = SSLContextBuilder.create();
sslContextBuilder.loadTrustMaterial(new org.apache.http.conn.ssl.TrustSelfSignedStrategy());
SSLContext sslContext = sslContextBuilder.build();
org.apache.http.conn.ssl.SSLConnectionSocketFactory sslSocketFactory =
new SSLConnectionSocketFactory(sslContext, new org.apache.http.conn.ssl.DefaultHostnameVerifier());
HttpClientBuilder httpClientBuilder = HttpClients.custom().setSSLSocketFactory(sslSocketFactory);
httpClient = httpClientBuilder.build();
NOTE: org.apache.http.conn.ssl.SSLContextBuilder is deprecated and org.apache.http.ssl.SSLContextBuilder is the new one (notice conn missing from the latter's package name).
From http://hc.apache.org/httpclient-3.x/sslguide.html:
Protocol.registerProtocol("https",
new Protocol("https", new MySSLSocketFactory(), 443));
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.whatever.com/");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} finally {
httpget.releaseConnection();
}
Where MySSLSocketFactory example can be found here. It references a TrustManager, which you can modify to trust everything (although you must consider this!)
want to paste the answer here:
in Apache HttpClient 4.5.5
How to handle invalid SSL certificate with Apache client 4.5.5?
HttpClient httpClient = HttpClients
.custom()
.setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build())
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
Once you have a Java Cert Store (by using the great InstallCert class created above), you can get java to use it by passing the "javax.net.ssl.trustStore" param at java startup.
Ex:
java -Djavax.net.ssl.trustStore=/path/to/jssecacerts MyClassName
Another issue you may run into with self signed test certs is this:
java.io.IOException: HTTPS hostname wrong: should be ...
This error occurs when you are trying to access a HTTPS url. You might have already installed the server certificate to your JRE's keystore. But this error means that the name of the server certificate does not match with the actual domain name of the server that is mentioned in the URL. This normally happens when you are using a non CA issued certificate.
This example shows how to write a HttpsURLConnection DefaultHostnameVerifier that ignore the certificates server name:
http://www.java-samples.com/showtutorial.php?tutorialid=211
EasySSLProtocolSocketFactory was giving me problems so I ended up implementing my own ProtocolSocketFactory.
First you need to register it:
Protocol.registerProtocol("https", new Protocol("https", new TrustAllSSLSocketFactory(), 443));
HttpClient client = new HttpClient();
...
Then implement ProtocolSocketFactory:
class TrustAllSSLSocketFactory implements ProtocolSocketFactory {
public static final TrustManager[] TRUST_ALL_CERTS = new TrustManager[]{
new X509TrustManager() {
public void checkClientTrusted(final X509Certificate[] certs, final String authType) {
}
public void checkServerTrusted(final X509Certificate[] certs, final String authType) {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
private TrustManager[] getTrustManager() {
return TRUST_ALL_CERTS;
}
public Socket createSocket(final String host, final int port, final InetAddress clientHost,
final int clientPort) throws IOException {
return getSocketFactory().createSocket(host, port, clientHost, clientPort);
}
#Override
public Socket createSocket(final String host, final int port, final InetAddress localAddress,
final int localPort, final HttpConnectionParams params) throws IOException {
return createSocket(host, port);
}
public Socket createSocket(final String host, final int port) throws IOException {
return getSocketFactory().createSocket(host, port);
}
private SocketFactory getSocketFactory() throws UnknownHostException {
TrustManager[] trustAllCerts = getTrustManager();
try {
SSLContext context = SSLContext.getInstance("SSL");
context.init(null, trustAllCerts, new SecureRandom());
final SSLSocketFactory socketFactory = context.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
return socketFactory;
} catch (NoSuchAlgorithmException | KeyManagementException exception) {
throw new UnknownHostException(exception.getMessage());
}
}
}
Note: This is with HttpClient 3.1 and Java 8
For a way to easily add hosts you trust at runtime without throwing out all checks, try the code here: http://code.google.com/p/self-signed-cert-trust-manager/.
I happened to face the same issue, all of a sudden all my imports were missing. I tried deleting all the contents in my .m2 folder. And trying to re-import everything , but still nothing worked.
Finally what I did was opened the website for which the IDE was complaining that it couldn't download in my browser. And saw the certificate it was using, and saw in my
$ keytool -v -list PATH_TO_JAVA_KEYSTORE
Path to my keystore was /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
that particular certificate was not there.
So all you have to do is put the certificate into the JAVA JVM keystore again.
It can be done using the below command.
$ keytool -import -alias ANY_NAME_YOU_WANT_TO_GIVE -file PATH_TO_YOUR_CERTIFICATE -keystore PATH_OF_JAVA_KEYSTORE
If it asks for password, try the default password 'changeit'
If you get permission error when running the above command.
In windows open it in administration mode.
In mac and unix use sudo.
After you have successfully added the key,
You can view it using :
$ keytool -v -list /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
You can view just the SHA-1 using teh command
$ keytool -list /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
This link explains the requirement you have step by step. If You are not really concerned which certificate you can proceed with the process in below link.
Note You might want to double check what you are doing since, this is a unsafe operation.
Using the InstallCert to generate the jssecacerts file and do
-Djavax.net.ssl.trustStore=/path/to/jssecacerts worked great.
I'm useing httpclient 3.1.X ,and this works for me
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager trustManager = new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[]{trustManager}, null);
SslContextSecureProtocolSocketFactory socketFactory = new SslContextSecureProtocolSocketFactory(sslContext,false);
Protocol.registerProtocol("https", new Protocol("https", (ProtocolSocketFactory) socketFactory, 443));//同样会影响到HttpUtils
} catch (Throwable e) {
e.printStackTrace();
}
public class SslContextSecureProtocolSocketFactory implements SecureProtocolSocketFactory {
private SSLContext sslContext;
private boolean verifyHostname;
public SslContextSecureProtocolSocketFactory(SSLContext sslContext, boolean verifyHostname) {
this.verifyHostname = true;
this.sslContext = sslContext;
this.verifyHostname = verifyHostname;
}
public SslContextSecureProtocolSocketFactory(SSLContext sslContext) {
this(sslContext, true);
}
public SslContextSecureProtocolSocketFactory(boolean verifyHostname) {
this((SSLContext)null, verifyHostname);
}
public SslContextSecureProtocolSocketFactory() {
this((SSLContext)null, true);
}
public synchronized void setHostnameVerification(boolean verifyHostname) {
this.verifyHostname = verifyHostname;
}
public synchronized boolean getHostnameVerification() {
return this.verifyHostname;
}
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException {
SSLSocketFactory sf = this.getSslSocketFactory();
SSLSocket sslSocket = (SSLSocket)sf.createSocket(host, port, clientHost, clientPort);
this.verifyHostname(sslSocket);
return sslSocket;
}
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
if(params == null) {
throw new IllegalArgumentException("Parameters may not be null");
} else {
int timeout = params.getConnectionTimeout();
Socket socket = null;
SSLSocketFactory socketfactory = this.getSslSocketFactory();
if(timeout == 0) {
socket = socketfactory.createSocket(host, port, localAddress, localPort);
} else {
socket = socketfactory.createSocket();
InetSocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
InetSocketAddress remoteaddr = new InetSocketAddress(host, port);
socket.bind(localaddr);
socket.connect(remoteaddr, timeout);
}
this.verifyHostname((SSLSocket)socket);
return socket;
}
}
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
SSLSocketFactory sf = this.getSslSocketFactory();
SSLSocket sslSocket = (SSLSocket)sf.createSocket(host, port);
this.verifyHostname(sslSocket);
return sslSocket;
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
SSLSocketFactory sf = this.getSslSocketFactory();
SSLSocket sslSocket = (SSLSocket)sf.createSocket(socket, host, port, autoClose);
this.verifyHostname(sslSocket);
return sslSocket;
}
private void verifyHostname(SSLSocket socket) throws SSLPeerUnverifiedException, UnknownHostException {
synchronized(this) {
if(!this.verifyHostname) {
return;
}
}
SSLSession session = socket.getSession();
String hostname = session.getPeerHost();
try {
InetAddress.getByName(hostname);
} catch (UnknownHostException var10) {
throw new UnknownHostException("Could not resolve SSL sessions server hostname: " + hostname);
}
X509Certificate[] certs = (X509Certificate[])((X509Certificate[])session.getPeerCertificates());
if(certs != null && certs.length != 0) {
X500Principal subjectDN = certs[0].getSubjectX500Principal();
List cns = this.getCNs(subjectDN);
boolean foundHostName = false;
Iterator i$ = cns.iterator();
AntPathMatcher matcher = new AntPathMatcher();
while(i$.hasNext()) {
String cn = (String)i$.next();
if(matcher.match(cn.toLowerCase(),hostname.toLowerCase())) {
foundHostName = true;
break;
}
}
if(!foundHostName) {
throw new SSLPeerUnverifiedException("HTTPS hostname invalid: expected \'" + hostname + "\', received \'" + cns + "\'");
}
} else {
throw new SSLPeerUnverifiedException("No server certificates found!");
}
}
private List<String> getCNs(X500Principal subjectDN) {
ArrayList cns = new ArrayList();
StringTokenizer st = new StringTokenizer(subjectDN.getName(), ",");
while(st.hasMoreTokens()) {
String cnField = st.nextToken();
if(cnField.startsWith("CN=")) {
cns.add(cnField.substring(3));
}
}
return cns;
}
protected SSLSocketFactory getSslSocketFactory() {
SSLSocketFactory sslSocketFactory = null;
synchronized(this) {
if(this.sslContext != null) {
sslSocketFactory = this.sslContext.getSocketFactory();
}
}
if(sslSocketFactory == null) {
sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
}
return sslSocketFactory;
}
public synchronized void setSSLContext(SSLContext sslContext) {
this.sslContext = sslContext;
}
}
For HttpClient, we can do this :
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
SSLContext.setDefault(ctx);
String uri = new StringBuilder("url").toString();
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
HttpClient client = HttpClientBuilder.create().setSSLContext(ctx)
.setSSLHostnameVerifier(hostnameVerifier).build()
follow the instruction given below for Java 1.7, to create an SSL certificate using InstallCert.java program file.
https://github.com/escline/InstallCert
you must restart the tomcat
Used the following along with DefaultTrustManager and it worked in httpclient like charm. Thanks a ton!! #Kevin and every other contributor
SSLContext ctx = null;
SSLConnectionSocketFactory sslsf = null;
try {
ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
SSLContext.setDefault(ctx);
sslsf = new SSLConnectionSocketFactory(
ctx,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
} catch (Exception e) {
e.printStackTrace();
}
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();

Categories