Java - standard SSL certificate all-trusting code fails - java

I think that by now, nearly every Java coder who's had experience with SSL certificate trusting errors has used or at least encountered this code:
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts;
trustAllCerts = new TrustManager[] {
new X509TrustManager() {
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
#Override
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() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
So far, I have used this code with success to ignor certificate mismatches when accessing an HTTPs site via its IP address (don't ask me why I do it, it's a completely different story). However, I tried to do the same thing for another HTTPs site, and found that this code fails with the following exception:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Certificates does not conform to algorithm constraints
So, the question is, what is wrong, and what can I do about it?

I've fixed the problem. It looks like the server is using MD2 in its certificate. So far, the only fix I know for this problem is locating the jre/lib/security/java.security file in your JDK path and changing jdk.certpath.disabledAlgorithms=MD2 to jdk.certpath.disabledAlgorithms= so that the MD2 algorithm isn't disabled. This looks really nasty, but, sadly, I couldn't find a way to enable MD2 programmatically.
Looking back, I now remember that I COULD access it, but only before I updated Java, which killed the java.security file edit.

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/

javax.net.ssl.SSLHandshakeException: Handshake failed in Android 7.0

I am calling API to login but I am getting error of ssl handshake in Android 7.0, other than this version everything is working fine. I am using retrofit.
Following is the error.
SSL handshake terminated: ssl=0xcbcd0340: Failure in SSL library, usually a protocol error
error:1000043e:SSL routines:OPENSSL_internal:TLSV1_ALERT_INAPPROPRIATE_FALLBACK (external/boringssl/src/ssl/s3_pkt.c:610 0xebc87640:0x00000001)
Also as said by someone to add the following code so that this issue will be resolved but still no luck,
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.cipherSuites(
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
.build();
okHttpClient.connectionSpecs(Collections.singletonList(spec));
I have even tried to letgo trust each and every certificate but still no luck. Following is the code.
public static OkHttpClient.Builder sslSocketFactory(OkHttpClient.Builder okHttpClient)
{
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_0)
.allEnabledCipherSuites()
.build();
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
//////// OkHttpClient.Builder builder = new OkHttpClient.Builder();
okHttpClient.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
okHttpClient.hostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return okHttpClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Following are my ssllabs tested using android 7.0
ssllabs test
In all other android versions all API's are working fine I do get response, but I am not able to get response in version 7.0.
Actually it's more likely to be a ssl_ciphers server-side settings problem.
Assuming nginx, change your ssl_ciphers settings to the one recommended by openHab :
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:HIGH:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!CBC:!EDH:!kEDH:!PSK:!SRP:!kECDH;
Don't forget to reload (systemctl reload nginx) and now all problematic android devices should work just fine.

javax.net.ssl.SSLException: Received fatal alert: bad_record_mac

I'm getting a javax.net.ssl.SSLException: Received fatal alert: bad_record_mac for an HTTPS connection. This doesn't happen for every request -- if I send the same request in 10 times I only get this error once or twice.
I have the following code to validate the certificate:
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) {
}
} };
try {
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("SSLv3");
} catch (NoSuchAlgorithmException e3) {
logException(Arrays.toString(e3.getStackTrace()));
}
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
SSLSocketFactory factory = sslContext.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(factory);
} catch (KeyManagementException e) {
logException(Arrays.toString(e.getStackTrace()));
}
// 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);
/*
* end of the fix
*/
I've set two system properties in my main method:
System.setProperty("jsse.enableSNIExtension", "false");
System.setProperty("https.protocols", "SSLv3");
But nothing helps.
According to this rubygems issue and the detailed description of the error (see below), it appears to be a bug in Oracle's JDK that is not present in OpenJDK. I recall (but cannot verify) that there was a bug in OpenSSL that also caused this error, so you may want to check the software on the other side of the connection.
You can read more details about what this error means here.
It is a hard to say what causing this. You need to find out by analyzing the logs. Enable debug by setting property:
System.setProperty("javax.net.debug", "all");
and check what is wrong.
A problem may be that the server is not supporting TLS, which may be picked by the implementation. To make sure that you always use the plain SSLv3 set the property:
System.setProperty("https.protocols", "SSLv3");
Try set com.sun.net.ssl.rsaPreMasterSecretFix to true.

Setting trust store programatically in ActiveMQSslConnectionFactory seems to fail

I have been working on a java activemq client software to connect to a ssl powered broker, but setting the trust store programatically through:
// Configure the secure connection factory.
ActiveMQSslConnectionFactory connectionFactory = new ActiveMQSslConnectionFactory(url);
connectionFactory.setTrustStore("/conf/client.ts"); // truststore which includes the certificate of the broaker
connectionFactory.setTrustStorePassword("password");
as indicated here. However, that throw a
javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed
Error
Following the response of the QA Resolving javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed Error? I was able to successfully connect the client to the broker by adding the broker certificate to my java installation's trusted certificates.
However, in this case, I do not want each user using the application to import the certificate on their java distribution, but rather that the client application already carries the broker certificate. How can I do that preferably using the ActiveMQSslConnectionFactory class?
From what I understand, you need to trust all the incoming self-signed certificates.
You could try this way (create a trust-manager which does not validate and then register it:
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certificates, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certificates, String authType) {
}
}
};
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
} catch (GeneralSecurityException e) {
}
//then do the ssl conversation.
I still havent managed to set the truststore programattically using the setTrustStore method from ActiveMQSslConnectionFactory
But based on #Chris response, it was possible to attach a new trust manager which accept all certificates to the ActiveMQSslConnectionFactory.
In order to do so, I created the same TrustManager as him, but used a different method to link it to the ActiveMQSslConnectionFactory
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certificates, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certificates, String authType) {
}
}
};
try {
String connectionString = "ssl://ipaddress:port"
ActiveMQSslConnectionFactory factory = new ActiveMQSslConnectionFactory(connectionString);
factory.setKeyAndTrustManagers(null, trustAllCerts, new SecureRandom());
Connection connection = factory.createConnection(user,password);
connection.start();
} catch (Exception e) {
}

Make a connection to a HTTPS server from Java and ignore the validity of the security certificate

I've been testing a system that accesses a group of https servers with different keys, some of which are invalid and all of them are not in the local key store for my JVM. I am really only testing things out, so I don't care about the security at this stage. Is there a good way to make POST calls to the server and tell Java not to worry about the security certificates?
My google searches for this have brought up some code examples that make a class to do the validation, that always works, but I cannot get it to connect to any of the servers.
As per the comments:
With Googled examples, you mean among others this one?
Update: the link broke, so here's an extract of relevance which I saved from the internet archive:
// 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(
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 (Exception 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) {
}
You need to create a X509TrustManager which bypass all the security check. You can find an example in my answer to this question,
How to ignore SSL certificate errors in Apache HttpClient 4.0

Categories