Send GET HTTPS request but get 403 forbidden response, why? - android

Below is the URL I send to the WS after the handshake is done
"https://ekp.truefriend.com/COVIWeb/gate/AutoAuthentication.aspx?UserID=DP0001&BackUrl=http%3a%2f%2fgw.truefriendtest.com%2fCOVIWeb%2fApproval%2fForms%2fForm.aspx%3fmobileyn%3dY%26piid%3d96482621-6cc4-401c-a6f9-5ba6cb7ce26f%26wiid%3d425a9bc9-8607-4898-9158-ed9170da1d89%26fmpf%3dWF_A_DRAFT_PAPER01%26fmrv%3d0%26fiid%3d749526BE-B208-4987-B751-2DD0FC03F0F6%26fmid%3d24f6765d-69d1-429f-b0da-b540a064f0e2%26scid%3ddc4378f1-7edd-4d69-8fe4-5867ed32c8b9"
What it should do is redirecting the browser to BackUrl page given in the url. It display correct result in IE8 despite the certificate problem. In PC version of Chrome it display some code of the HTML. In Android, I get 403 Forbidden error.
HTTP/1.1 403 Forbidden ( The server denied the specified Uniform Resource Locator (URL). Contact the server administrator. )
I use this method to stream data
try{
URL url = new URL(urlString);
HttpsURLConnection.setDefaultHostnameVerifier(new FakeHostVerifier());
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
Log.d("SSLDemo", "getAcceptedIssuers");
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
Log.d("SSLDemo", "Check Client Trusted");
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
Log.d("SSLDemo", "Check Server Trusted");
}
}
};
SSLContext sc = SSLContext.getInstance("TLS"); //"TLS"
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
int port = 443;
SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();
socket = (SSLSocket)factory.createSocket(url.getHost(), port);
socket.startHandshake();
/**
* Connection Method
*/
String method = "GET";
String os = method + " "+urlString+" HTTP/1.0\r\n";
os += "Content-Length: 0";
os += "\r\n\r\n";
((SSLWeb)this.caller).updateRequest(urlString, method);
Log.i("TESTWEB", os);
BufferedWriter wout = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
wout.write(os);
wout.flush();
wout.close();
rd = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//********* Not using thread
StringBuffer buff = new StringBuffer();
char[] buffer = new char[1024];
while(rd.read(buffer) > -1) {
buff.append(buffer);
Log.i("TESTWEB", "read buffer :" + String.valueOf(buffer));
}
Log.i("TESTWEB", "read line :" + buff.toString());
//**********
}catch(Exception e){
Log.e("TESTWEB", "Connecting error", e);
e.printStackTrace();
}
Is there something wrong with my code? I thought the problem was with the URL parameter, but it work in browser :(
I've been finding a way around the problem for the last three days now, no luck so far.
EDIT: This is FakeHostVerifier class that used to skip the certificate validation process. Isn't this correct?
public class FakeHostVerifier implements HostnameVerifier {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}

As I was saying in a comment to another answer, this has nothing to do with trusting the server's certificate or not. If you get an HTTP response, even if it's a 403, that means that the HTTP connection was established, which also means that the underlying SSL/TLS connection was established. If your client doesn't trust the server certificate, the SSL/TLS connection will close before any HTTP traffic happens.
I'd try a few things:
Remove the Content-Length header. It's a GET request, so it doesn't have an entity. Implying a 0-length entity might confuse the server.
Try to set a User-Agent header to simulate the request as coming from a browser.
More generally, look at the headers a browser that work would send and try to reproduce them. (Try Accept header as well, that might be the cause of your problem with Chrome.)
EDIT: (other potential problem, more likely to be the cause)
If you urlstring variable really contains "https://ekp.truefriend.com/COVIWeb/gate/...", that's where the problem comes from.
When you send an HTTP GET the request should be like this:
GET /COVIWeb/gate/... HTTP/1.1
Host: ekp.truefriend.com
Not:
GET https://ekp.truefriend.com/COVIWeb/gate/... HTTP/1.1
(that's only for requests via a proxy, and doesn't apply to the HTTPS requests anyway.)
If you're using HTTP 1.0, you won't use the Host header, but it shouldn't really matter (unless that host serves multiple virtual hosts, which it can do, even over HTTPS). Consider using HTTP/1.1 if you can, although you may have to deal with closing the connection (content-length or chunked encoding perhaps).

Your question contains the answer. Upon trying to access the URL you specified in Chrome, you get a big red warning "The site's security certificate is not trusted!". While you can manually override in a browser and ignore the warning, your code treats this as a security problem and a dead end. It even recommends you contact the server administrator.
If you are the server's admin, change the SSL cert to a valid one. if not, ask the admin to do it. Failing that, try accessing the HTTP (non-SSL) version of the site.

Related

SSLHandshakeException - Chain chain validation failed, how to solve?

in my application I am trying to do a HTTPS POST request to my server.
However, I keep getting SSLHandshakeException - Chain chain validation failed, all the time. I tried to send a request using POSTMAN and I got a response from the server. What can be causing this error when I try to send the request from the application?
Here a code snippet where I try to send the post request:
public static JSONObject getDataLibConfiguration(Context context) throws HttpRequestException {
int statusCode = 0;
JSONObject commonInformation;
HttpsURLConnection connection = null;
try {
commonInformation = ConfigurationProcessor.getCommonInformation(context);
if (commonInformation == null) {
return null;
}
URL url = new URL(BuildConfig.SERVER_CONFIG_URL);
if (BuildConfig.DEBUG) {
LogUtils.d(TAG, "url = " + url.getPath());
}
connection = getHttpsConnection(url);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
connection.setRequestProperty("Content-Encoding", "gzip");
byte[] gzipped = HttpUtils.gzip(commonInformation.toString());
cos = new CountingOutputStream(connection.getOutputStream()); //<-- This is where I get the exception
cos.write(gzipped);
cos.flush();
statusCode = connection.getResponseCode();
// More code her
}
private static HttpsURLConnection getHttpsConnection(URL url) throws IOException, GeneralSecurityException {
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
MatchDomainTrustManager myTrustManager = new MatchDomainTrustManager(url.getHost());
TrustManager[] tms = new TrustManager[]{myTrustManager};
sslContext.init(null, tms, null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
connection.setSSLSocketFactory(sslSocketFactory);
} catch (AssertionError ex) {
if (BuildConfig.DEBUG) {
LogFileUtils.e(TAG, "Exception in getHttpsConnection: " + ex.getMessage());
}
LogUtils.e(TAG, "Exception: " + ex.toString());
}
return connection;
}
In my case it was wrong date on phone.
Fixing date resolved an issue
If you're using an emulated device it may solve the problem if you just 'Cold Boot' it.
Sometimes the date on those things can get stuck if you let them run for some time, which results in this expired-certificate-problem.
The problem was that the certificate was expired.
In my case, I fetch this issue on Android Emulator.
When I clear emulator cache has resolved the issue.
My date and time were correct, but I didn't have "Use Network Provided Time checked" in my system settings.
I fixed this issue by going to Settings > Date and Time > Check "Use network-provided time" and also check "Use network-provided time zone".
Then this error went away.
In my case, the issue was with the phone date. So please check it, set to automatic.
public static void trustEveryone() {
try {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
public boolean verify(String hostname, SSLSession session) {
return true;
}});
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new X509TrustManager(){
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}}}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(
context.getSocketFactory());
} catch (Exception e) { // should never happen
e.printStackTrace();
}
}
or check system date of your device - I had this Exception when I tried to connect with wrong date!..
If anyone come across this issue pertaining to specific device, then the reason should be because of incorrect date set in the device.
I fixed this error by resetting my emulator date and time. My server is working fine just I changed the date and time of my emulator as current server time zone.
#Yash Bhardwaj in the comment on #Vadim answer said that the problem was in Glide framework. I faced the same problem: Https requests to server using Ktor framework were all successful, but when Glide tried to load image from the same server, it faced the SSLHandshakeException.
To solve this issue you should look here: Solve Glide SSLHandshakeException.
To make a deal with #GlideModule annotation you should import kapt plugin and add these dependencies into your app build.gradle:
implementation 'com.github.bumptech.glide:okhttp3-integration:4.11.0'
kapt 'com.github.bumptech.glide:compiler:4.12.0'
If you use android emulator, you can wipe data and run again, it works

When is it necessary to specific application/json Content-Type explicitly

Currently, I'm building a Android mobile app & Python restful server services.
I found that, it makes no different, whether or not I'm using
self.response.headers['Content-Type'] = "application/json"
The following code (which doesn't specific Content-Type explicitly) works fine for me. I was wondering, in what situation, I should specific Content-Type explicitly?
Python restful server services code
class DebugHandler(webapp2.RequestHandler):
def get(self):
response = {}
response["key"] = "value"
self.response.out.write(json.dumps(response))
application = webapp2.WSGIApplication([
('/debug', DebugHandler),
], debug = True)
Android mobile app client code
public static String getResponseBodyAsString(String request) {
BufferedReader bufferedReader = null;
try {
URL url = new URL(request);
HttpURLConnection httpURLConnection = (HttpURLConnection)url.openConnection();
initHttpURLConnection(httpURLConnection);
InputStream inputStream = httpURLConnection.getInputStream();
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
int charRead = 0;
char[] buffer = new char[8*1024];
// Use StringBuilder instead of StringBuffer. We do not concern
// on thread safety. stringBuffer = new StringBuffer();
StringBuilder stringBuilder = new StringBuilder();
while ((charRead = bufferedReader.read(buffer)) > 0) {
stringBuilder.append(buffer, 0, charRead);
}
return stringBuilder.toString();
} catch (MalformedURLException e) {
Log.e(TAG, "", e);
} catch (IOException e) {
Log.e(TAG, "", e);
} finally {
close(bufferedReader);
}
return null;
}
Content-Type specifies what's inside the response (i.e. how to interpret the body of the response). Is it JSON, a HTML document, a JPEG, etc? It is useful when you have different representations of your resources and together with Accept it's a header involved in doing content negotiation between client and server.
Different clients might need different formats. A C# client might prefer XML, a Javascript client might prefer JSON, another client could work with multiple representations but try to request the most efficient one first and then settle for others if the server can't serve the preferred one, etc.
Content-Type is very important in the browser so that the user agent knows how to display the response. If you don't specify one the browser will try to guess, usually based on the extension and maybe fallback to some Save as... dialog if that fails also. In a browser, the lack of a Content-Type might cause some HTML to open a Save as... dialog, or a PDF file to be rendered as gibberish in the page.
In an application client, not having a Content-Type might cause a parsing error or might be ignored. If you server only serves JSON and your client only expects JSON then you can ignore the Content-Type, the client will just assume it's JSON because that's how it was built.
But what if at some point you want to add XML as a representation, or YAML or whatever? Then you have a problem because the client assumed it's always JSON and ignored the Content-Type. Now when it receives XML it will try to parse as JSON and fail. If instead the client was built with content types in mind and you always specify a Content-Type then your client will then take it into account and select an appropriate parser instead of blindly making assumptions.

HTTP CONNECT method with Android

I want to extend an existing Android app which sets up a http connection to a remote device which it sends commands to and receives values from.
The feature consists of a tunneled connection via a custom proxy server that has been set up. I have the http header format given which should make the proxy server create and provide a tunnel for my app.
CONNECT <some id>.<target ip>:80 HTTP/1.1
Host: <proxy ip>:443
Authorization: basic <base64 encoded auth string>
# Here beginns the payload for the target device. It could be whatever.
GET / HTTP/1.1
Host: 127.0.0.1:80
The app uses the Apache HttpClient library to handle it's connections, and I would like to integrate with that. This is not mandatory, however.
The authorization is standard conform basic auth.
I have trouble implementing this because it is not clear to me how the HttpClient is intended to be used for such behaviour.
There is no CONNECT method in the library, only GET, POST and so on. I figured this would then be managed by the proxy settings of the HttpClient instance.
The problem here is that the request line is not standard, since the CONNECT line contains an id which the custom proxy then would parse and interpret.
I now would like to know if there is any intended method to implement this using the Apache HttpClient and what it would look like with this sample data given, or if I have to implement my own method for this. And if so, which interface (there are a few that would sound reasonable to inherit from) it should implement.
Any explanation, snippet or pointer would be appreciated.
UPDATE:
I now have a small snippet set up, without Android. Just plain Java and Apache HttpClient. I still think the Host mismatch in the request is a problem, since I can't manage to establish a connection.
final HttpClient httpClient = new DefaultHttpClient();
// Set proxy
HttpHost proxy = new HttpHost (deviceId + "." + "proxy ip", 443, "https");
httpClient.getParams().setParameter (ConnRoutePNames.DEFAULT_PROXY, proxy);
final HttpGet httpGet = new HttpGet("http://" + "target device ip");
httpGet.addHeader ("Authorization", "Basic" +
Base64.encodeBase64String((username + ":" + password).getBytes()));
// Trying to overvrite the host in the header containing the device Id
httpGet.setHeader("Host", "proxy ip");
System.out.println("Sending request..");
try {
final HttpResponse httpResponse = httpClient.execute (httpGet);
final InputStream inputStream = httpResponse.getEntity ().getContent ();
final InputStreamReader inputStreamReader =
new InputStreamReader(inputStream, "ISO-8859-1");
final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
final StringBuilder stringBuilder = new StringBuilder ();
String bufferedStrChunk = null;
while ((bufferedStrChunk = bufferedReader.readLine ()) != null) {
stringBuilder.append (bufferedStrChunk);
}
System.out.println("Received String: " + stringBuilder.toString());
}
catch (final ClientProtocolException exception) {
System.out.println("ClientProtocolException");
exception.printStackTrace();
}
catch (final IOException exception) {
System.out.println("IOException");
exception.
}
This looks fairly good to me in the way of "it could actually work".
Anyways, I receive the following log and trace:
Sending request..
2015/03/03 13:16:16:199 CET [DEBUG] ClientParamsStack - 'http.route.default-proxy': https://"device id"."proxy ip":443
2015/03/03 13:16:16:207 CET [DEBUG] SingleClientConnManager - Get connection for route HttpRoute[{}->https://"device id"."proxy ip":443->http://"target device ip"]
2015/03/03 13:16:16:549 CET [DEBUG] ClientParamsStack - 'http.tcp.nodelay': true
2015/03/03 13:16:16:549 CET [DEBUG] ClientParamsStack - 'http.socket.buffer-size': 8192
2015/03/03 13:16:16:563 CET [DEBUG] DefaultClientConnection - Connection shut down
2015/03/03 13:16:16:563 CET [DEBUG] SingleClientConnManager - Releasing connection org.apache.http.impl.conn.SingleClientConnManager$ConnAdapter#bc6a08
Exception in thread "main" java.lang.IllegalStateException: Unable to establish route.
planned = HttpRoute[{}->https://"device id"."proxy ip":443->http://"target device ip"]
current = HttpRoute[{s}->https://"device id"."proxy ip":443->http://"target device ip"]
at org.apache.http.impl.client.DefaultRequestDirector.establishRoute(DefaultRequestDirector.java:672)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:385)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:576)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:554)
at run.main(run.java:71)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Any ideas what goes wrong?
UPDATE: As stated here, this might be due to a bug when redirecting. The fact that the target does redirect tells me, that I do not have reached the correct target, implying that the Host parameter may have not been overwritten.
In fact, this can't be done with the HttpClient at all, I tried at the wrong level. It works when done with a TcpSocket (or a SSLSocket). The custom CONNECT header can simply be assembled and sent like that:
final Socket tcpSocket = SSLSocketFactory.getDefault().createSocket("host ip", 443);
String connect = "custom CONNECT header";
tcpSocket.getOutputStream().write((connect).getBytes());
tcpSocket.getOutputStream().flush();
The response from the server can then be read with a BufferedReader or whatever.

How to use Socket.IO with server demanding authentication

This is where I got the socketIO files from.
https://github.com/Gottox/socket.io-java-client/tree/master/src/io/socket
I am on the client side.
I know connecting works when the server does not need authentication.
But when it needs authentication (Username and password), I get a handshaking error message.
How do I get passed authentication?? Could it be a server side error? Would the server side of things change if authentication was added?
This is the function that throws an error...I did not write it.
This line is the one causing problems: InputStream stream = connection.getInputStream();
It says it is caused by: java.io.FileNotFoundException: url:80/socket.io/1/
private void handshake() {
URL url;
String response;
URLConnection connection;
try {
setState(STATE_HANDSHAKE);
url = new URL(IOConnection.this.url.toString() + SOCKET_IO_1);
connection = url.openConnection();
if (connection instanceof HttpsURLConnection) {
((HttpsURLConnection) connection)
.setSSLSocketFactory(sslContext.getSocketFactory());
}
connection.setConnectTimeout(connectTimeout);
connection.setReadTimeout(connectTimeout);
/* Setting the request headers */
for (Entry<Object, Object> entry : headers.entrySet()) {
connection.setRequestProperty((String) entry.getKey(),
(String) entry.getValue());
}
InputStream stream = connection.getInputStream();
Scanner in = new Scanner(stream);
response = in.nextLine();
String[] data = response.split(":");
sessionId = data[0];
heartbeatTimeout = Long.parseLong(data[1]) * 1000;
closingTimeout = Long.parseLong(data[2]) * 1000;
protocols = Arrays.asList(data[3].split(","));
} catch (Exception e) {
error(new SocketIOException("Error while handshaking", e));
}
}
Problem solved (sort of), here: Android developpement, Gottox socket.io-java-client: file not fount Exception /socket.io/1/
(try using an earlier version of socket.io - by first deleting socket.io folder from node_modules and then install an older version, e.g., 0.9.16, using this command: npm install socket.io#0.9.16)

Receive & Validate certificate from server HTTPS - android

I am calling web service from my android client via https. I got to validate the certificate receive from server side. How do I do that ? At present this is my code that I use to call a web service.
private static String SendPost(String url, ArrayList<NameValuePair> pairs) { // url = "https://....."
errorMessage = "";
String response = "";
DefaultHttpClient hc=new DefaultHttpClient();
ResponseHandler <String> res=new BasicResponseHandler();
HttpPost postMethod=new HttpPost(url);
try {
postMethod.setEntity(new UrlEncodedFormEntity(pairs));
response = hc.execute(postMethod, res);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
How do I validate a self-signed certificate received from server during performing Post ? I got to do testing via public/private keys. Client will have a CA file. Ijust need the client to verify the server certificate using the CA, the service is public .This has to do with public/private key. How can I receive the certificate from the server before calling the post ?
Their are several options and code snippets available on stackoverflow. Couple of links I found with multiple answers is :
Accepting a certificate for HTTPs on Android
HTTPS GET (SSL) with Android and self-signed server certificate
But I can't make out which is good/applicable for me ! I don't want to disable all or accept any. Have to check the public/private keys/
Any help is highly appreciated.
Bob Lee wrote a nice blog post on how using SSL certificates with Android. I think it is applicable to your case: http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html
You just have to create a KeyStore containing your self-signed certificate and use the custom HttpClient implementation described in that post.
UPDATE:
Host name validation can be customizez by setting a custom X509HostnameVerifier on the SSLSocketFactory. Some implementations are already available in android: AllowAllHostnameVerifier, BrowserCompatHostnameVerifier, StrictHostnameVerifier
/* ... */
public class MyHostnameVerifier extends AbstractVerifier {
boolean verify(String hostname, SSLSession session) {
X509Certificate[] chain = session.getPeerCertificateChain();
/* made some checks... */
return checked;
}
}
sslSocketFactory.setHostnameVerifier(new MyHostnameVerifier());

Categories

Resources