javax.net.ssl.SSLException: hostname in certificate didn't match - java

My Android app tells me that my https certificate doesn't match the hostname:
javax.net.ssl.SSLException: hostname in certificate didn't match: <hostname1> != <oldhostname>
What is odd is that
The website (hostname1) gives the correct certificate (checked with browsers and the ssllabs tool)
oldhostname is the previous hostname I had set up in previous versions of the app
Is there some kind of cache for certificates? I cant't find any info on that

Add this class
public class HttpsTrustManager implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
#Override
public void checkClientTrusted(
X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
#Override
public void checkServerTrusted(
X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
public static void allowAllSSL() {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[]{new HttpsTrustManager()};
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context != null ? context.getSocketFactory() : null);
}
}
and call it from your MainActivity with HttpsTrustManager.allowAllSSL();
Although it's not save approach but i solve my problem with this.

Related

Your app is using an unsafe implementation of the X509TrustManager interface with an Apache HTTP client, resulting in a security vulnerability

i had a code which app can access https, just like this :
public class HttpsTrustManager implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
chain[0].checkValidity();
} catch (Exception e) {
throw new CertificateException("Certificate not valid or trusted.");
}
}
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
public static void allowAllSSL() {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[]{new HttpsTrustManager()};
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
}
}
after that, google play console give me a notice :
Apache Trust Manager
Your app is using an unsafe implementation of the X509TrustManager interface with an Apache HTTP client, resulting in a security vulnerability. Please see this Google Help Center article for details, including the deadline for fixing the vulnerability.
Insecure Hostname Verifier
Your app is using an unsafe implementation of HostnameVerifier. Please see this Google Help Center article for details, including the deadline for fixing the vulnerability.
is there something wrong with my code ??

SSLSocketFactory in Java, LDAP network connection

My question is similar to: SSLSocketFactory in java
I need to set a custom SSLSocketFactory...except I do NOT have an https connection (it's LDAPS), so can't use:
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
...to set the SSLSocketFactory. I have an SSLContext object initialized but when I make the LDAP connection the default SSLContext is called automatically since my custom one is not set:
dirContext = new InitialDirContext(env); // <-- reverts to default ssl context
Is there a non-HTTPS equivalent method to line #3 below:
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(myKeyManagerFactory.getKeyManagers(), myTrustManagerArray, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
Yes, there is.
env.put("java.naming.ldap.factory.socket", UnsecuredSSLSocketFactory.class.getName());
UnsecuredSSLSocketFactory.java:
public class UnsecuredSSLSocketFactory extends SSLSocketFactory
{
private SSLSocketFactory socketFactory;
public UnsecuredSSLSocketFactory()
{
try
{
var sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new X509TrustManager()
{
#Override
public void checkClientTrusted(X509Certificate[] xcs, String string){}
#Override
public void checkServerTrusted(X509Certificate[] xcs, String string){}
#Override
public X509Certificate[] getAcceptedIssuers()
{
return null;
}
}}, new SecureRandom());
socketFactory = sslContext.getSocketFactory();
}
catch(Exception e)
{
throw new RuntimeException(e);
}
}
#SuppressWarnings("unused")
public static SocketFactory getDefault()
{
return new UnsecuredSSLSocketFactory();
}
#Override
public String[] getDefaultCipherSuites()
{
return socketFactory.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites()
{
return socketFactory.getSupportedCipherSuites();
}
#Override
public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException
{
return socketFactory.createSocket(socket, string, i, bln);
}
#Override
public Socket createSocket(String string, int i) throws IOException
{
return socketFactory.createSocket(string, i);
}
#Override
public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException
{
return socketFactory.createSocket(string, i, ia, i1);
}
#Override
public Socket createSocket(InetAddress ia, int i) throws IOException
{
return socketFactory.createSocket(ia, i);
}
#Override
public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException
{
return socketFactory.createSocket(ia, i, ia1, i1);
}
#Override
public Socket createSocket() throws IOException
{
return socketFactory.createSocket();
}
}
Note, if the issue is just a hostname mismatch (which is super common in clustered Active Directory Environments), you can just set the system property com.sun.jndi.ldap.object.disableEndpointIdentification to true, so as a command line arg -Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true
Note this will only ignore a hostname mismatch on the certificate, you will still need to have a trust chain from ldap's cert to something in your truststore, but this seems to be the most common issue people have with SSL, LDAP and Active Directory, as the certificate's the domain generate for each domain controller don't include a subject alternate name for the domain itself, so if you follow the standard example of just pointing ldap to yourcomapanydomain.com, when it resolves to domaincontroller1.yourcompanydomain.com you get a failure. Note, if you are upgrading from an old java version, this behavior changed in https://www.oracle.com/java/technologies/javase/8u181-relnotes.html

Disable SSL certificate check in retrofit library

I am using retrofit in android to connect with server.
public class ApiClient {
public static final String BASE_URL = "https://example.com/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
This is my dev. server and I want to disable certificate check. How can I implement in this code?
ERROR: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Use this class to get unsafe Retrofit instance. I have included imports to avoid confusion.
import java.security.cert.CertificateException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import view.utils.AppConstants;
/**
* Created by Hitesh.Sahu on 11/23/2016.
*/
public class NetworkHandler {
public static Retrofit getRetrofit() {
return new Retrofit.Builder()
.baseUrl(AppConstants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(getUnsafeOkHttpClient())
.build();
}
private static OkHttpClient getUnsafeOkHttpClient() {
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[]{};
}
}
};
// 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 SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory);
builder.hostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
OkHttpClient okHttpClient = builder.build();
return okHttpClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
And then simply use retrofit without ssl check like this
private void postFeedbackOnServer() {
MyApiEndpointInterface apiService =
NetworkHandler.getRetrofit().create(MyApiEndpointInterface.class);
Call<ResponseBE> call = apiService.submitFeedbackToServer(requestObject);
Log.e(TAG , "Request is" + new Gson().toJson(requestObject).toString() );
call.enqueue(new Callback<ResponseBE>() {
#Override
public void onResponse(Call<ResponseBE> call, Response<ResponseBE> response) {
int statusCode = response.code();
if (statusCode == HttpURLConnection.HTTP_OK) {
......
} else {
Toast.makeText(FeedbackActivity.this, "Failed to submit Data" + statusCode, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ResponseBE> call, Throwable t) {
// Log error here since request failed
Toast.makeText(FeedbackActivity.this, "Failure" + t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
});
}
The syntax has changed a little since Hitesh Sahu's answer was posted. Now you can use lambdas for some of the methods, remove some throw clauses and chain builder method invocations.
private static OkHttpClient createOkHttpClient() {
try {
final TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
return new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory())
.hostnameVerifier((hostname, session) -> true)
.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
In my case I solved like this with kotlin:
object Instance {
private const val BASE_URL: String = "https://base_url/"
val service: Service by lazy {
Retrofit
.Builder()
.baseUrl(BASE_URL)
.client(getUnsafeOkHttpClient())
.build().create(Service::class.java)
}
private fun getUnsafeOkHttpClient(): OkHttpClient? {
return try {
// Create a trust manager that does not validate certificate chains
val trustAllCerts = arrayOf<TrustManager>(
object : X509TrustManager {
#Throws(CertificateException::class)
override fun checkClientTrusted(
chain: Array<X509Certificate?>?,
authType: String?
) {
}
#Throws(CertificateException::class)
override fun checkServerTrusted(
chain: Array<X509Certificate?>?,
authType: String?
) {
}
override fun getAcceptedIssuers(): Array<X509Certificate?>? {
return arrayOf()
}
}
)
// Install the all-trusting trust manager
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
// Create an ssl socket factory with our all-trusting manager
val sslSocketFactory = sslContext.socketFactory
val trustManagerFactory: TrustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val trustManagers: Array<TrustManager> =
trustManagerFactory.trustManagers
check(!(trustManagers.size != 1 || trustManagers[0] !is X509TrustManager)) {
"Unexpected default trust managers:" + trustManagers.contentToString()
}
val trustManager =
trustManagers[0] as X509TrustManager
val builder = OkHttpClient.Builder()
builder.sslSocketFactory(sslSocketFactory, trustManager)
builder.hostnameVerifier(HostnameVerifier { _, _ -> true })
builder.build()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
IMO, you can read Google's documentation - Security with HTTPS and SSL.
About sample code to use Retrofit with your self-signed certificate, please try the following, hope it helps!
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try{
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(getSSLSocketFactory())
.hostnameVerifier(getHostnameVerifier())
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL_BASE)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
WebAPIService service = retrofit.create(WebAPIService.class);
Call<JsonObject> jsonObjectCall = service.getData(...);
...
} catch (Exception e) {
e.printStackTrace();
}
}
// for SSL...
// Read more at https://developer.android.com/training/articles/security-ssl.html#CommonHostnameProbs
private HostnameVerifier getHostnameVerifier() {
return new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true; // verify always returns true, which could cause insecure network traffic due to trusting TLS/SSL server certificates for wrong hostnames
//HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
//return hv.verify("localhost", session);
}
};
}
private TrustManager[] getWrappedTrustManagers(TrustManager[] trustManagers) {
final X509TrustManager originalTrustManager = (X509TrustManager) trustManagers[0];
return new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return originalTrustManager.getAcceptedIssuers();
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
try {
if (certs != null && certs.length > 0){
certs[0].checkValidity();
} else {
originalTrustManager.checkClientTrusted(certs, authType);
}
} catch (CertificateException e) {
Log.w("checkClientTrusted", e.toString());
}
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
try {
if (certs != null && certs.length > 0){
certs[0].checkValidity();
} else {
originalTrustManager.checkServerTrusted(certs, authType);
}
} catch (CertificateException e) {
Log.w("checkServerTrusted", e.toString());
}
}
}
};
}
private SSLSocketFactory getSSLSocketFactory()
throws CertificateException, KeyStoreException, IOException,
NoSuchAlgorithmException, KeyManagementException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = getResources().openRawResource(R.raw.your_cert); // File path: app\src\main\res\raw\your_cert.cer
Certificate ca = cf.generateCertificate(caInput);
caInput.close();
KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
TrustManager[] wrappedTrustManagers = getWrappedTrustManagers(tmf.getTrustManagers());
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, wrappedTrustManagers, null);
return sslContext.getSocketFactory();
}
...
I tried #whirlwin's solution on this page but that didn't work with java 9+. Some small changes resulted in this:
private static OkHttpClient createTrustingOkHttpClient() {
try {
X509TrustManager x509TrustManager = new X509TrustManager() {
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
};
final TrustManager[] trustAllCerts = new TrustManager[] {
x509TrustManager
};
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
return new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory(), x509TrustManager)
.hostnameVerifier((hostname, session) -> true)
.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
And this worked for me as you can imagine. Happy days! Still, be careful when using this.
Adding code for doing same in Kotlin based on #Hitesh Sahu's answer :
fun getRetrofirApiService(currentBaseURL: String): YourAPIService{
val TIMEOUT = 2L
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
val retrofit = Retrofit.Builder()
.baseUrl(currentBaseURL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(NullOnEmptyConverterFactory())
.addConverterFactory(GsonConverterFactory.create())
.client(createOkHttpClient())
.build()
return retrofit.create(APIService::class.java)
}
Now create Http client for same as shown below :
private fun createOkHttpClient(): OkHttpClient {
return try {
val trustAllCerts: Array<TrustManager> = arrayOf(MyManager())
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory())
.addInterceptor(logging)
.hostnameVerifier { hostname: String?, session: SSLSession? -> true }
.build()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
MyManager class is as shown below :
class MyManager : X509TrustManager {
override fun checkServerTrusted(
p0: Array<out java.security.cert.X509Certificate>?,
p1: String?
) {
//allow all
}
override fun checkClientTrusted(
p0: Array<out java.security.cert.X509Certificate>?,
p1: String?
) {
//allow all
}
override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> {
return arrayOf()
}
}
Imports for same are as shown below :
import okhttp3.MediaType
import okhttp3.OkHttpClient
import okhttp3.RequestBody
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.Result
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.security.SecureRandom
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSession
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
Implementation of such workaround in code, even for testing purposes is a bad practice.
You can:
Generate your CA.
Sign your certificate with CA.
Add your CA as trusted.
Some links that may be useful:
https://jamielinux.com/docs/openssl-certificate-authority/create-the-root-pair.html
http://wiki.cacert.org/FAQ/ImportRootCert

java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors

I am new to web services. I am getting javax.net.ssl.SSLHandshakeException while hitting one URL. The code for the Trust Manager is given below.
DatedTrustManager(Date validationDate, StorePkixParameters parameters) {
this.validationDate = validationDate;
this.parameters = parameters;
try {
validator = CertPathValidator.getInstance("PKIX");
certFactory = CertificateFactory.getInstance("X.509");
}
catch(GeneralSecurityException e) {
logger.error("error constructing CertPathValidator/CertificateFactory", e);
throw new IllegalStateException(e);
}
}
private void validate(X509Certificate[] chain)
throws CertificateException {
CertPath certPath = certFactory.generateCertPath(removeExtraCertificates(fixCertificatesOrder(chain)));
PKIXParameters localParams = parameters.getParams();
synchronized (validator) {
localParams.setDate(validationDate!=null ?validationDate :new Date());
try {
validator.validate(certPath, localParams);
} catch (CertPathValidatorException e) {
logCertificatePathException(e);
Throwable cause = e.getCause();
if(cause instanceof CertificateException) {
throw (CertificateException)cause;
}
throw new CertificateException(e);
} catch (InvalidAlgorithmParameterException e) {
throw new CertificateException(e);
}
}
}
private X509Certificate[] fixCertificatesOrder(X509Certificate[] chain) {
X509Certificate[] newChain = Arrays.copyOf(chain, chain.length);
for(int i = 0; i < newChain.length-1; i++) {
X500Principal issuer = newChain[i].getIssuerX500Principal();
boolean found = false;
for(int j = i+2; j < newChain.length; j++) {
if(issuer.equals(newChain[j].getSubjectX500Principal())) {
X509Certificate temp = newChain[i+1];
newChain[i+1] = newChain[j];
newChain[j] = temp;
found = true;
break;
}
}
if(!found) {
return Arrays.copyOf(newChain, i+1);
}
}
if(!isCertificateValid(newChain[i+1])) {
return Arrays.copyOf(newChain, i+1);
}
}
return newChain;
}
private boolean isCertificateValid(X509Certificate cert) {
try {
if(validationDate == null) {
cert.checkValidity();
} else {
cert.checkValidity(validationDate);
}
return true;
}
catch(CertificateNotYetValidException ex) {
return false;
}
catch(CertificateExpiredException ex) {
return false;
}
}
private List<X509Certificate> removeExtraCertificates(X509Certificate[] chain) {
List<X509Certificate> rc= new ArrayList<X509Certificate>(chain.length);
for(X509Certificate cert : chain) {
rc.add(cert);
X500Principal subjectX500Principal = cert.getSubjectX500Principal();
X500Principal issuerX500Principal = cert.getIssuerX500Principal();
if(subjectX500Principal.equals(issuerX500Principal)) {
// remove old algorithm that is no longer supported
// root certificate should be found in trust store
// 6/21/11 - Opened up to all algorithms (before just cert.getSigAlgName() = "MD2withRSA" or "SHA1withRSA").
// Remove all root certificates and expect them to be in the trust store.
rc.remove(rc.size()-1);
break;
}
}
return rc;
}
static private void logCertificatePathException(CertPathValidatorException e) {
CertPath path = e.getCertPath();
if(path!=null) {
int idx= e.getIndex();
if(0<=idx) {
Certificate certInError = path.getCertificates().get(idx);
if(certInError instanceof X509Certificate) {
X509Certificate xcert= (X509Certificate)certInError;
logger.info("Certificate {} in error - {}", xcert.getSubjectX500Principal(), e.getMessage());
}
}
}
}
#Override
public void checkClientTrusted(X509Certificate chain[], String authType)
throws CertificateException {
validate(chain);
}
#Override
public void checkServerTrusted(X509Certificate chain[], String authType)
throws CertificateException {
validate(chain);
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return parameters.getTrustCerts().clone();
}
public Date getValidationDate() {
return validationDate;
}
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType,
Socket socket) throws CertificateException {
validate(chain);
}
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType,
SSLEngine engine) throws CertificateException {
validate(chain);
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType,
Socket socket) throws CertificateException {
validate(chain);
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType,
SSLEngine engine) throws CertificateException {
validate(chain);
}
I verified the SSL certificates and updated the trust store but still this issue is coming. Please help me out here.

How to disable SSLv3 in android for HttpsUrlConnection?

We wrote client application in android which connects with https servers using HttpsUrlConnection apis. Due to Poodle vulnerability, we need to disable SSLv3 from the list of enabled protocols while invoking any request.
We followed the guidelines captured by oracle
and added following line before invoking url connection
java.lang.System.setProperty("https.protocols", "TLSv1");
This solution works fine with normal java program.
We got SSLHandShakeException when tried to connect with a server which only works on SSLv3 protocol.
But concern is : same fix does not work for android. Am I missing something or should I try another approach for android? Please suggest.
I found the solution for it by analyzing the data packets using wireshark. What I found is that while making a secure connection, android was falling back to SSLv3 from TLSv1 . It is a bug in android versions < 4.4 , and it can be solved by removing the SSLv3 protocol from Enabled Protocols list. I made a custom socketFactory class called NoSSLv3SocketFactory.java. Use this to make a socketfactory.
/*Copyright 2015 Bhavit Singh Sengar
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class NoSSLv3SocketFactory extends SSLSocketFactory{
private final SSLSocketFactory delegate;
public NoSSLv3SocketFactory() {
this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
}
public NoSSLv3SocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
#Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
private Socket makeSocketSafe(Socket socket) {
if (socket instanceof SSLSocket) {
socket = new NoSSLv3SSLSocket((SSLSocket) socket);
}
return socket;
}
#Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return makeSocketSafe(delegate.createSocket(s, host, port, autoClose));
}
#Override
public Socket createSocket(String host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(host, port));
}
#Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
return makeSocketSafe(delegate.createSocket(host, port, localHost, localPort));
}
#Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(host, port));
}
#Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return makeSocketSafe(delegate.createSocket(address, port, localAddress, localPort));
}
private class NoSSLv3SSLSocket extends DelegateSSLSocket {
private NoSSLv3SSLSocket(SSLSocket delegate) {
super(delegate);
}
#Override
public void setEnabledProtocols(String[] protocols) {
if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) {
List<String> enabledProtocols = new ArrayList<String>(Arrays.asList(delegate.getEnabledProtocols()));
if (enabledProtocols.size() > 1) {
enabledProtocols.remove("SSLv3");
System.out.println("Removed SSLv3 from enabled protocols");
} else {
System.out.println("SSL stuck with protocol available for " + String.valueOf(enabledProtocols));
}
protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
}
super.setEnabledProtocols(protocols);
}
}
public class DelegateSSLSocket extends SSLSocket {
protected final SSLSocket delegate;
DelegateSSLSocket(SSLSocket delegate) {
this.delegate = delegate;
}
#Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
#Override
public String[] getEnabledCipherSuites() {
return delegate.getEnabledCipherSuites();
}
#Override
public void setEnabledCipherSuites(String[] suites) {
delegate.setEnabledCipherSuites(suites);
}
#Override
public String[] getSupportedProtocols() {
return delegate.getSupportedProtocols();
}
#Override
public String[] getEnabledProtocols() {
return delegate.getEnabledProtocols();
}
#Override
public void setEnabledProtocols(String[] protocols) {
delegate.setEnabledProtocols(protocols);
}
#Override
public SSLSession getSession() {
return delegate.getSession();
}
#Override
public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
delegate.addHandshakeCompletedListener(listener);
}
#Override
public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
delegate.removeHandshakeCompletedListener(listener);
}
#Override
public void startHandshake() throws IOException {
delegate.startHandshake();
}
#Override
public void setUseClientMode(boolean mode) {
delegate.setUseClientMode(mode);
}
#Override
public boolean getUseClientMode() {
return delegate.getUseClientMode();
}
#Override
public void setNeedClientAuth(boolean need) {
delegate.setNeedClientAuth(need);
}
#Override
public void setWantClientAuth(boolean want) {
delegate.setWantClientAuth(want);
}
#Override
public boolean getNeedClientAuth() {
return delegate.getNeedClientAuth();
}
#Override
public boolean getWantClientAuth() {
return delegate.getWantClientAuth();
}
#Override
public void setEnableSessionCreation(boolean flag) {
delegate.setEnableSessionCreation(flag);
}
#Override
public boolean getEnableSessionCreation() {
return delegate.getEnableSessionCreation();
}
#Override
public void bind(SocketAddress localAddr) throws IOException {
delegate.bind(localAddr);
}
#Override
public synchronized void close() throws IOException {
delegate.close();
}
#Override
public void connect(SocketAddress remoteAddr) throws IOException {
delegate.connect(remoteAddr);
}
#Override
public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
delegate.connect(remoteAddr, timeout);
}
#Override
public SocketChannel getChannel() {
return delegate.getChannel();
}
#Override
public InetAddress getInetAddress() {
return delegate.getInetAddress();
}
#Override
public InputStream getInputStream() throws IOException {
return delegate.getInputStream();
}
#Override
public boolean getKeepAlive() throws SocketException {
return delegate.getKeepAlive();
}
#Override
public InetAddress getLocalAddress() {
return delegate.getLocalAddress();
}
#Override
public int getLocalPort() {
return delegate.getLocalPort();
}
#Override
public SocketAddress getLocalSocketAddress() {
return delegate.getLocalSocketAddress();
}
#Override
public boolean getOOBInline() throws SocketException {
return delegate.getOOBInline();
}
#Override
public OutputStream getOutputStream() throws IOException {
return delegate.getOutputStream();
}
#Override
public int getPort() {
return delegate.getPort();
}
#Override
public synchronized int getReceiveBufferSize() throws SocketException {
return delegate.getReceiveBufferSize();
}
#Override
public SocketAddress getRemoteSocketAddress() {
return delegate.getRemoteSocketAddress();
}
#Override
public boolean getReuseAddress() throws SocketException {
return delegate.getReuseAddress();
}
#Override
public synchronized int getSendBufferSize() throws SocketException {
return delegate.getSendBufferSize();
}
#Override
public int getSoLinger() throws SocketException {
return delegate.getSoLinger();
}
#Override
public synchronized int getSoTimeout() throws SocketException {
return delegate.getSoTimeout();
}
#Override
public boolean getTcpNoDelay() throws SocketException {
return delegate.getTcpNoDelay();
}
#Override
public int getTrafficClass() throws SocketException {
return delegate.getTrafficClass();
}
#Override
public boolean isBound() {
return delegate.isBound();
}
#Override
public boolean isClosed() {
return delegate.isClosed();
}
#Override
public boolean isConnected() {
return delegate.isConnected();
}
#Override
public boolean isInputShutdown() {
return delegate.isInputShutdown();
}
#Override
public boolean isOutputShutdown() {
return delegate.isOutputShutdown();
}
#Override
public void sendUrgentData(int value) throws IOException {
delegate.sendUrgentData(value);
}
#Override
public void setKeepAlive(boolean keepAlive) throws SocketException {
delegate.setKeepAlive(keepAlive);
}
#Override
public void setOOBInline(boolean oobinline) throws SocketException {
delegate.setOOBInline(oobinline);
}
#Override
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
}
#Override
public synchronized void setReceiveBufferSize(int size) throws SocketException {
delegate.setReceiveBufferSize(size);
}
#Override
public void setReuseAddress(boolean reuse) throws SocketException {
delegate.setReuseAddress(reuse);
}
#Override
public synchronized void setSendBufferSize(int size) throws SocketException {
delegate.setSendBufferSize(size);
}
#Override
public void setSoLinger(boolean on, int timeout) throws SocketException {
delegate.setSoLinger(on, timeout);
}
#Override
public synchronized void setSoTimeout(int timeout) throws SocketException {
delegate.setSoTimeout(timeout);
}
#Override
public void setTcpNoDelay(boolean on) throws SocketException {
delegate.setTcpNoDelay(on);
}
#Override
public void setTrafficClass(int value) throws SocketException {
delegate.setTrafficClass(value);
}
#Override
public void shutdownInput() throws IOException {
delegate.shutdownInput();
}
#Override
public void shutdownOutput() throws IOException {
delegate.shutdownOutput();
}
#Override
public String toString() {
return delegate.toString();
}
#Override
public boolean equals(Object o) {
return delegate.equals(o);
}
}
}
Use this class like this while connecting :
SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null,
null,
null);
SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(NoSSLv3Factory);
l_connection = (HttpsURLConnection) l_url.openConnection();
l_connection.connect();
UPDATE :
Now, correct solution would be to install a newer security provider using Google Play Services:
ProviderInstaller.installIfNeeded(getApplicationContext());
This effectively gives your app access to a newer version of OpenSSL and Java Security Provider, which includes support for TLSv1.2 in SSLEngine. Once the new provider is installed, you can create an SSLEngine which supports SSLv3, TLSv1, TLSv1.1 and TLSv1.2 the usual way:
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
SSLEngine engine = sslContext.createSSLEngine();
Or you can restrict the enabled protocols using engine.setEnabledProtocols.
Don't forget to add the following dependency (latest version found here):
compile 'com.google.android.gms:play-services-auth:11.8.0'
For more info, checkout this link.
Inspired by Bhavit S. Sengar's answer, it bundled that technique into a dead simple method call. You can use the NetCipher library to get a modern TLS config when using Android's HttpsURLConnection. NetCipher configures the HttpsURLConnection instance to use the best supported TLS version, removes SSLv3 support, and configures the best suite of ciphers for that TLS version. First, add it to your build.gradle:
compile 'info.guardianproject.netcipher:netcipher:1.2'
Or you can download the netcipher-1.2.jar and include it directly in your app. Then instead of calling:
HttpURLConnection connection = (HttpURLConnection) sourceUrl.openConnection();
Call this:
HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);
At first I tried Bhavit S. Sengar's answer and it worked for most cases. But sometimes there where issues even when SSLv3 protocol was removed from Enabled Protocols on an Android 4.4.4 device. So the NetCipher library by Hans-Christoph Steiner is perfect to solve that problem as far as I could test it.
We use jsoup to make a bunch of web scraping on different servers, so we cannot set HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);. I assume that's the same problem if you use OkHttp.
The best solution we've come to is to set the info.guardianproject.netcipher.client.TlsOnlySocketFactory from NetCipher as DefaultSSLSocketFactory in a static block. So it's set for the whole runtime of our app:
SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
SSLSocketFactory noSSLv3Factory = new TlsOnlySocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(noSSLv3Factory);
If you like to inspect the full details (with trustAllCertificates) you can do it here.
use this code snippet, if server is SSLv3 enable then it will fail handshaking.
SocketFactory sf = SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) sf.createSocket("host-name", 443);
socket.setEnabledProtocols(new String[] { "TLSv1"});
socket.startHandshake();
SSLContext sslContext = SSLContext.getInstance("TLSv1");
sslContext.init(null, null, null);
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
httpURLConnection.setSSLSocketFactory(socketFactory);
HttpsURLConnection using TSL create a security failed, the Android implementation will fall back to SSLV3 to connection.
Please refer this http://code.google.com/p/android/issues/detail?id=78431
Using PlayService publisher client libraries running on Android I experienced the same problem when running the sample.
Fixed it with #bhavit-s-sengar's awnser above. Had to also change AndroidPublisherHelper.newTrustedTransport() to this:
SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
// NoSSLv3SocketFactory is #bhavit-s-sengar's http://stackoverflow.com/a/29946540/8524
SSLSocketFactory noSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());
NetHttpTransport.Builder netTransportBuilder = new NetHttpTransport.Builder();
netTransportBuilder.setSslSocketFactory(noSSLv3Factory);
HTTP_TRANSPORT = netTransportBuilder.build();
Connects with https server we need certificate in handshaking from client side. 1 year back I solved a similar issue using self sign certificate in the following way-
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class HttpsTrustManager implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
#Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
#Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
public static void allowAllSSL() {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[]{new HttpsTrustManager()};
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
}
}
Usage in client side before HttpsUrlConnection
HttpsTrustManager.allowAllSSL();
hopefully it will work :)
Actually we don't need to disable the SSLV3 or TLSV1.0, What we just need to enable TLSV1.1 or TLSv1.2 in android < 5 devices.
The problem is TLSv1.1 and TLSv1.2 not enabled on Android <5 by default and to connect using these latest secure protocol we must have to enable in Android <5 devices.
This solution fixed my problem : https://stackoverflow.com/a/45853669/3448003

Categories