Getting SSLPeerUnverifiedException in Android - android

i am getting SSL Peer Unverified Exception when i try to connect using HTTPs Connection.
I am new to HTTPs.
My code is :
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
DefaultHttpClient client = new DefaultHttpClient();
SchemeRegistry registry = new SchemeRegistry();
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
registry.register(new Scheme("https", socketFactory, 443));
SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry);
DefaultHttpClient httpClient = new DefaultHttpClient(mgr, client.getParams()); HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
HttpPost httppost = new HttpPost("https://server.example.com/Login");
List<BasicNameValuePair> nameValuePairs = new ArrayList<BasicNameValuePair>(
2);
nameValuePairs.add(new BasicNameValuePair("LoginId",uname));
nameValuePairs.add(new BasicNameValuePair("Password",pass));
try {
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpClient.execute(httppost);
if (response.getStatusLine().getStatusCode() == 200) {
}
Log.i("zacharia", "Response :"+EntityUtils.toString(response.getEntity()));
} catch (Exception e) {
}

The SSL Peer Unverified Exception could be thrown for several reasons, the most common is when the certificate sent by the server is a self signed certificate and not a certificate signed by authorized CA, if that's the issue the common approach in android is adding the certificate to the Trusted Certificates chain and then making the request as follows:
KeyStore selfsignedKeys = KeyStore.getInstance("BKS");
selfsignedKeys.load(context.getResources().openRawResource(R.raw.selfsignedcertsbks),
"genericPassword".toCharArray());
TrustManagerFactory trustMgr = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustMgr.init(selfsignedKeys);
SSLContext selfsignedSSLcontext = SSLContext.getInstance("TLS");
selfsignedSSLcontext.init(null, trustMgr.getTrustManagers(), new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(selfsignedSSLcontext.getSocketFactory());
URL serverURL = new URL("https://server.example.com/endpointTest");
HttpsURLConnection serverConn = (HttpsURLConnection)serverURL.openConnection();
Take on count that this approach is only when you are sure the certificate not signed by a CA, and in order to make it work you need to have the certificate it self, put it in a BKS keystore (for android to read it) and then open an HttpURLConnection using the SSL context that "accepts" that self signed certificate, because the DefaultHttpClient will not handle those requests based on the Default SSLContext.
If you want to learn more about SSL i recommend you to read the book "Application Security for the Android Platform" by Jeff Six Editorial O'Reilly...
Regards!

Related

No peer certificate error on android 2.3 devices but working fine on android 4+

I have created the bks file for my server's certificate. This was added in the project's sources in raw folder
I've created my https client as follows:
public class MyHttpsClient extends DefaultHttpClient {
final Context context;
public MyHttpsClient(Context context) {
this.context = context;
}
#Override
protected ClientConnectionManager createClientConnectionManager() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// Register for port 443 our SSLSocketFactory with our keystore
// to the ConnectionManager
registry.register(new Scheme("https", newSslSocketFactory(), 443));
return new SingleClientConnManager(getParams(), registry);
}
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
try {
// Initialize the keystore with the provided trusted certificates
// Also provide the password of the keystore
trusted.load(in, "testpassword".toCharArray());
} finally {
in.close();
}
// Pass the keystore to the SSLSocketFactory. The factory is responsible
// for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);
// Hostname verification from certificate
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
Then I use it like:
DefaultHttpClient httpClient = new MyHttpsClient(context);
HttpConnectionParams.setConnectionTimeout(httpClient.getParams(), 30000);
HttpContext localContext = new BasicHttpContext();
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Content-type", "application/json");
httpPost.setEntity(new StringEntity(jsonString));
response = httpClient.execute(httpPost, localContext);
HttpEntity entity = response.getEntity();
httpresponse = getResponse(entity);
Now here comes the funny part. This works just fine on android 4+ both real devices and emulator. THIS fails on android 2.3 with
javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
How can I make it work on android 2.3 without the known "trust all certificates" way ?
A few days age i solved same problem. The solution consisted in the following. I take all certificates installed on device(in my case it was samsung galaxy II) and go to the server side developer, who manage certificate chain installed on server. He analyzes ssl chain and detected that in chain there is one certificate (Thawte 2006) and other certificate (Thawte 2010). He removed oldest certificate, which issued in 2006, and ssl verification on android 2.x become working. I suggest you, before trying to get work local keystore, research your server side ssl chain and check that this chain doesn't have unnecessary certificates, because android 2.x devices cannot ignore not necessary certificates, but other platforms 3.x 4x and ios, windows phone can do it, i mean ignore "trash" in ssl certificate chain.

Certificate pinning in Android

I am trying to learn how to do certificate pinning in an Android application. I found the tutorial here. I wanted to clarify I doubt I have based on my testing this code.
I used the code as follows :
public class CertificatePinning {
static SSLSocketFactory constructSSLSocketFactory(Context context) {
SSLSocketFactory sslSocketFactory = null;
try {
AssetManager assetManager = context.getAssets();
InputStream keyStoreInputStream = assetManager.open("myapp.store");
KeyStore trustStore = KeyStore.getInstance("BKS");
trustStore.load(keyStoreInputStream, "somepass".toCharArray());
sslSocketFactory = new SSLSocketFactory(trustStore);
sslSocketFactory.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
}
catch(Exception e){
Log.d("Exception", e.getLocalizedMessage());
}
return sslSocketFactory;
}
public static HttpClient getNewHttpClient(Context context) {
DefaultHttpClient httpClient = null;
try {
SSLSocketFactory sslSocketFactory = constructSSLSocketFactory(context);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sslSocketFactory, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
httpClient = new DefaultHttpClient(ccm, params);
} catch (Exception e) {
Log.d("Exception", e.getLocalizedMessage() );
return null;
}
return httpClient;
}
}
Quoting a statement from that tutorial :
On the client side, you simply need to distribute the signing certificate
with your app and validate against it.
On my web server, I have my own CA, which I created using open SSL, and used to sign certificates for different domain names that are used with my app.
This statement indicates that this tutorial is meant for the CA certificate I have. I tested the code using ca.pem (from my CA's crt file) and it works fine.
But I also tested the same code with a certificate I signed with that CA, e.g. server.pem ( from the signed server.crt), and still it works.
Did I do something wrong, or is this code meant for pinning either :
1) a CA certificate (covering all certificates signed by that CA) or
2) a particular certificate (signed by some CA) ?

Safely fixing: javax.net.ssl.SSLPeerUnverifiedException: No peer certificate

There are dozens of posts about this issue (javax.net.ssl.SSLPeerUnverifiedException: No peer certificate) but I haven't found anything that works for me.
Many posts (like this, and this) "solve" this by allowing all certificates to be accepted but, of course, this is not a good solution for anything other than testing.
Others seem quite localized and don't work for me. I really hope that someone has some insight that I lack.
So, my problem: I'm testing on a server accessible only through the local network, connecting via HTTPS. Making the call I need to through the browser works fine. No complaining about certificates and if you check the certificates, it all looks good.
When I try on my Android device, I get javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
Here's the code that calls it:
StringBuilder builder = new StringBuilder();
builder.append( /* stuff goes here*/ );
httpGet.setEntity(new UrlEncodedFormEntity(nameValuePairs));
ResponseHandler<String> responseHandler = new BasicResponseHandler();
// Execute HTTP Post Request. Response body returned as a string
HttpClient httpClient = MyActivity.getHttpClient();
HttpGet httpGet = new HttpGet(builder.toString());
String jsonResponse = httpClient.execute(httpGet, responseHandler); //Line causing the Exception
My code for MyActivity.getHttpClient():
protected synchronized static HttpClient getHttpClient(){
if (httpClient != null)
return httpClient;
HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, TIMEOUT_CONNECTION);
HttpConnectionParams.setSoTimeout(httpParameters, TIMEOUT_SOCKET);
HttpProtocolParams.setVersion(httpParameters, HttpVersion.HTTP_1_1);
//Thread safe in case various AsyncTasks try to access it concurrently
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
ClientConnectionManager cm = new ThreadSafeClientConnManager(httpParameters, schemeRegistry);
httpClient = new DefaultHttpClient(cm, httpParameters);
CookieStore cookieStore = httpClient.getCookieStore();
HttpContext localContext = new BasicHttpContext();
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
return httpClient;
}
Any help would be much appreciated.
Edit
Also just to mention I've had other SSL issues with another app but adding the SchemeRegistry portion fixed it for me before.
Edit 2
So far I've only tested on Android 3.1, but I need this to work on Android 2.2+ regardless. I just tested on the browser on my Android tab (Android 3.1) and it complains about the certificate. It's fine on my pc browser, but not on the Android browser or in my app.
Edit 3
Turns out the iOS browser also complains about it. I'm starting to think it's a certificate chain issue described here (SSL certificate is not trusted - on mobile only)
It turns out my code was fine and the problem was that the server was not returning the full certificate chain. For more information see this SO post and this superuser post:
SSL certificate is not trusted - on mobile only
https://superuser.com/questions/347588/how-do-ssl-chains-work
Try below code :-
BasicHttpResponse httpResponse = null;
HttpPost httppost = new HttpPost(URL);
HttpParams httpParameters = new BasicHttpParams();
// Set the timeout in milliseconds until a connection is
// established.
int timeoutConnection = ConstantLib.CONNECTION_TIMEOUT;
HttpConnectionParams.setConnectionTimeout(httpParameters,
timeoutConnection);
// Set the default socket timeout (SO_TIMEOUT)
// in milliseconds which is the timeout for waiting for data.
int timeoutSocket = ConstantLib.CONNECTION_TIMEOUT;
HttpConnectionParams
.setSoTimeout(httpParameters, timeoutSocket);
DefaultHttpClient httpClient = new DefaultHttpClient(
httpParameters);
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair(ConstantLib.locale,
locale));
In my case everything used to work fine. Suddenly after 2 days my device did not show any https site or image link.
After some investigation it turns out that My time settings was not up to date on device.
I changed my time settings properly and it worked.
I had this exception when I used self-signed certificate + ip address. Just add these lines
HostnameVerifier allHostsValid = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
and your HttpURLConnection will work.
That trick is not related to validation against CA! So if I specify wrong CA and use that trick, I will get another exception. So the host remains trusted
Just in case, I will leave code for specifying your own CA here:
String certStr = context.getString(R.string.caApi);
X509Certificate ca = SecurityHelper.readCert(certStr);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null);
ks.setCertificateEntry("caCert", ca);
tmf.init(ks);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
Do not forget to use cool feature buildTypes and place different CAs for debug/release in res folder.

javax.net.ssl.sslpeerunverifiedexception no peer certificate

I am trying to insert a record into MySQL by posting data to a PHP server from an Android app. I have added the INTERNET permission to AndroidManifest.xml
I get javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
Android code
private void senddata(ArrayList<NameValuePair> data)
{
try
{
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("https://10.0.2.2/insert222.php");
httppost.setEntity(new UrlEncodedFormEntity(data));
HttpResponse response = httpclient.execute(httppost);
}
catch (Exception e) {
// TODO: handle exception
Log.e("log_tag", "Error: "+e.toString());
}
}
Can anyone help?
Warning: Do not implement this in production code you are ever going to use on a network you do not entirely trust. Especially anything going over the public internet. This link gives more correct answer. Here is an implementation using SSL.
Your problem is you are using DefaultHttpClient for https(secure url).
Create a custom DefaultHttpClient
public static HttpClient createHttpClient()
{
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
HttpProtocolParams.setUseExpectContinue(params, true);
SchemeRegistry schReg = new SchemeRegistry();
schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schReg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);
return new DefaultHttpClient(conMgr, params);
}
Than change your code as follows:
HttpClient httpclient = createHttpClient();
HttpPost httppost = new HttpPost("https://10.0.2.2/insert222.php");
httppost.setEntity(new UrlEncodedFormEntity(data));
HttpResponse response = httpclient.execute(httppost);
Have a look at here if you have problems
It should work.
I had this issue with an IIS 8 server. In the https binding, I had to uncheck the checkbox labeled "Require Server Name Indication." Once I unchecked it, I quit getting the error.
I have to say all trusted certificates (trusted by authorized centres such as COMODO, Symantec, etc.) have to be work in any case. If your app recieves such javax.net.ssl.SSLPeerUnverifiedException: No peer certificate using bought certificate you give something wrong on server side.
To test use openssl s_client -connect example.com:443 command to get inner information about certificate your app recieve.
In may case my nginx-server sent wrong certificate in some cases.

Android https server connection No peer

I'm developing android application, which connecting to server via https connection.
I've created my_store.bks file with trusted sertificate and using following code to connect to server :
DefaultHttpClient httpclient = new DefaultHttpClient();
KeyStore trustStore = KeyStore.getInstance("BKS");
InputStream in = context.getResources().openRawResource(R.raw.my_store);
try{
trustStore.load(in,pass.toCharArray());
}
catch(Exception e){
Log.v("MyLog", "errror reading sert: " + e.toString());
}
finally{
in.close();
}
SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore);
Scheme sch = new Scheme("https", socketFactory, 443);
httpclient.getConnectionManager().getSchemeRegistry().register(sch);
HttpPost poster = new HttpPost(serverAdress);
UrlEncodedFormEntity ent = new UrlEncodedFormEntity(formparams,"UTF-8");
poster.setEntity(ent);
HttpResponse responsePost = httpclient.execute(poster);
but when I'm executing this code, I've got next error:
javax.net.ssl.SSLPeerUnverifiedException: No peer certificate No peer certificate
here is some similar problem
No peer cert. Not sure which route to take but i don't want ignore SSL certificates.
Does anyone know what it may be? May it be some server error? (server is developing, so may have errors, but in browser it works fine with sert).

Categories

Resources