Android hooking HTTPS traffic using Frida - android

I'm trying to learn Frida to hook into various application. Specifically I'm trying to hook into Android applications, I'm using the Appmon project. That project has an HTTPS.js script that hooks into the getInputStream and getOutputStream from the HttpUrlConnection class. The HTTPS.js script successfully hooks the methods, but when HTTPS traffic is sent the data that i see is encrypted.
According to Android documentation on HTTPUrlConnection if the URL.openconnection() method receives a HTTPS url it will return a HTTPSUrlConnection object.
Android documentation for HttpsURLConnection states it is an abstract class that extends HttpURLConnection.
public abstract class HttpsURLConnection extends HttpURLConnection
I searched the android source code and found HttpsURLConnection is abstract and is extended by DelegatingHttpsURLConnection which is also abstract. DelegatingHttpsURLConnection is extended by HttpsURLConnectionImpl
I've hooked into
com.android.okhttp.internal.huc.HttpsURLConnectionImpl
which is successful but the data is still encrypted. Here is the code
var HttpsURLConnection = Java.use("com.android.okhttp.internal.huc.HttpsURLConnectionImpl");
HttpsURLConnection.getInputStream.overloads[0].implementation = function() {
try {
methodURL = "";
responseHeaders = "";
responseBody = "";
var Connection = this;
var stream = stream = this.getInputStream.overloads[0].apply(this, arguments);
var requestURL = Connection.getURL().toString();
var requestMethod = Connection.getRequestMethod();
var requestProperties
methodURL = requestMethod + " " + requestURL;
if (Connection.getHeaderFields) {
var Keys = Connection.getHeaderFields().keySet().toArray();
var Values = Connection.getHeaderFields().values().toArray();
responseHeaders = "";
for (var key in Keys) {
if (Keys[key] && Keys[key] !== null && Values[key]) {
responseHeaders += Keys[key] + ": " + Values[key].toString().replace(/\[/gi, "").replace(/\]/gi, "") + "\n";
} else if (Values[key]) {
responseHeaders += Values[key].toString().replace(/\[/gi, "").replace(/\]/gi, "") + "\n";
}
}
}
var retval;
if (stream) {
var baos = ByteArrayOutputStream.$new();
var buffer = -1;
var BufferedReaderStream = BufferedReader.$new(InputStreamReader.$new(stream));
while ((buffer =stream.read()) != -1){
baos.write(buffer);
responseBody += String.fromCharCode(buffer);
}
BufferedReaderStream.close();
baos.flush();
retval = ByteArrayInputStream.$new(baos.toByteArray());
}
/* --- Payload Header --- */
var send_data = {};
send_data.time = new Date();
send_data.txnType = 'HTTPS';
send_data.lib = 'com.android.okhttp.internal.huc.HttpsURLConnectionImpl';
send_data.method = 'getInputStream';
send_data.artifact = [];
/* --- Payload Body --- */
var data = {};
data.name = "Request/Response";
data.value = methodURL + "\n" + requestHeaders + "\n" + requestBody + "\n\n" + responseHeaders + "\n" + responseBody;
data.argSeq = 0;
send_data.artifact.push(data);
send(JSON.stringify(send_data));
if(retval)
return retval;
return stream;
} catch (e) {
this.getInputStream.overloads[0].apply(this, arguments);
}
}
BTW I am planning on submitting a pull request once i get this working.
Android documentation has an example on how to create an HTTP request:
URL url = new URL("https://wikipedia.org");
URLConnection urlConnection = url.openConnection();
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
Reading this tells me that just calling the getInputStream() method should return the clear text stream, but it doesnt appear to be doing so.
Question: How can i get the clear text data from the HTTPS traffic? I can see the headers, just not the actual data
Update 10/13
I'm wondering if the data I'm seeing isn't encrypted, just encoded. Here is a snipping of the data i receive back:
\\u001f\x8b\\b\\u0000\\u0000\\u0000\\u0000\\u0000\\u0004\\u0000\xed\xbd\\u0007`\\u001cI\x96%&/m\xca{\x7fJ\xf5J\xd7\xe0t\xa1\\b\x80`\\u0013$\xd8\x90#\\u0010\xec\xc1\x88\xcd\xe6\x92\xec\\u001diG#)\xab*\x81\xcaeVe]f\\u0016#\xcc\xed\x9d\xbc\xf7\xde{\xef\xbd\xf7\xde{\xef\xbd\xf7\xba;\x9dN\'\xf7\xdf\xff?\\\\fd\\u0001l\xf6\xceJ\xda\xc9\x9e!\x80\xaa\xc8\\u001f?~|\\u001f?\\"\\u001e\xff\\u001e\xef\\u0016ez\x99\xd7MQ-?\xfbhw\xbc\xf3Q\x9a/\xa7\xd5\xacX^
The response header indicates that its gzip encoded
Content-Encoding: gzip
Content-Length: 438
Content-Type: text/xml; charset=utf-8
I wonder if its related to this stackoverflow answer, although the outgoing request does have the "Accept-Encoding: gzip" header. I tried adding a call to GZIPInputStream, but the application doesnt like the response
Update 2
So I am able to get it to capture the data, it is a problem with gzip. The problem I'm running into now is that the application on the Android device expcts a GZIPed input stream. To display the data within Frida i have to run it through GZIPInputStream, not sure yet how to compress it again to send to the app. I tried breifly with GZIPOutputStream but that didnt work. Here is my updated code.
HttpURLConnection.getInputStream.overloads[0].implementation = function() {
try {
methodURL = "";
responseHeaders = "";
responseBody = "";
var Connection = this;
if("gzip" == Connection.getContentEncoding())
{
var stream = InputStreamReader.$new(GZIPInputStream.$new(this.getInputStream.apply(this, arguments)));
}
else
{
var stream = InputStreamReader.$new(this.getInputStream.apply(this, arguments));
}
}
var requestURL = Connection.getURL().toString();
var requestMethod = Connection.getRequestMethod();
var requestProperties
methodURL = requestMethod + " " + requestURL;
if (Connection.getHeaderFields) {
var Keys = Connection.getHeaderFields().keySet().toArray();
var Values = Connection.getHeaderFields().values().toArray();
responseHeaders = "";
for (var key in Keys) {
if (Keys[key] && Keys[key] !== null && Values[key]) {
responseHeaders += Keys[key] + ": " + Values[key].toString().replace(/\[/gi, "").replace(/\]/gi, "") + "\n";
} else if (Values[key]) {
responseHeaders += Values[key].toString().replace(/\[/gi, "").replace(/\]/gi, "") + "\n";
}
}
}
var retval;
if (stream) {
var baos = ByteArrayOutputStream.$new();
var buffer = -1;
var BufferedReaderStream = BufferedReader.$new(stream);
while ((buffer =stream.read()) != -1){
baos.write(buffer);
responseBody += String.fromCharCode(buffer);
}
BufferedReaderStream.close();
baos.flush();
if("gzip" == Connection.getContentEncoding())
{
retval = GZIPOutputStream.$new(ByteArrayInputStream.$new(baos.toByteArray()));
}
else
{
retval = ByteArrayInputStream.$new(baos.toByteArray());
}
}
/* --- Payload Header --- */
var send_data = {};
send_data.time = new Date();
send_data.txnType = 'HTTP';
send_data.lib = 'com.android.okhttp.internal.http.HttpURLConnectionImpl';
send_data.method = 'getInputStream';
send_data.artifact = [];
/* --- Payload Body --- */
var data = {};
data.name = "Request/Response";
data.value = methodURL + "\n" + requestHeaders + "\n" + requestBody + "\n\n" + responseHeaders + "\n" + responseBody;
data.argSeq = 0;
send_data.artifact.push(data);
send(JSON.stringify(send_data));
if(retval)
return retval;
return stream;
} catch (e) {
this.getInputStream.overloads[0].apply(this, arguments);
}
}

Related

Getting "(CSRF token missing or incorrect.)" in android

I am getting CSRF token mismatch error on Django server while running below piece of code.
Can some one help me identifying the issue here.
try{
loginUrl = new URL(urls[0]);
loginUrlConnection = (HttpURLConnection) loginUrl.openConnection();
loginUrlConnection.setRequestMethod("GET");
String userPass = "aniket" + ":" + "rinku123";
String basicAuth = "Basic " + Base64.encodeToString(userPass.getBytes(), Base64.DEFAULT);
loginUrlConnection.setRequestProperty("Authorization", basicAuth);
loginUrlConnection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
loginUrlConnection.setRequestProperty("X-CSRF-TOKEN", "fetch");
loginUrlConnection.getContent();
if (HttpURLConnection.HTTP_OK == loginUrlConnection.getResponseCode()) {
cookie = loginUrlConnection.getHeaderField("Set-Cookie");
String[] parts = cookie.split("\\=|\\;"); // split response by " and find the string that's 64 characters (csrf token)
for(String s: parts) {
if(s.length() == 64) {
xcsrfToken = s;
break;
}
}
}
loginUrl = new URL(urls[0]);
loginUrlConnection = (HttpURLConnection) loginUrl.openConnection();
loginUrlConnection.setRequestMethod("POST");
userPass = "aniket" + ":" + "rinku123";
basicAuth = "Basic " + Base64.encodeToString(userPass.getBytes(), Base64.DEFAULT);
loginUrlConnection.setRequestProperty("Authorization", basicAuth);
loginUrlConnection.setRequestProperty("cookie", cookie);
loginUrlConnection.setRequestProperty("X-CSRF-TOKEN", xcsrfToken);
loginUrlConnection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
loginUrlConnection.setConnectTimeout(10000);
loginUrlConnection.setDoInput(true);
loginUrlConnection.setDoOutput(true);
loginUrlConnection.setUseCaches(true);
loginUrlConnection.connect();
error :: Forbidden (CSRF token missing or incorrect.): /accounts/login/
Use from django.views.decorators.csrf import csrf_exempt On your view.
You can use it as decorator like this
#csrf_exempt
class Myview(View):
template_name = '1.html'

Android: Generate Oauth1 Signature in Volley Request

I am trying to add Oauth1 Authorization in my android app using volley
in the postman when i add the details like oauth_consumer_key, oauth_consumer_secret , token_key token_secret like the picture below
it generate a header like below picture and response received successfully.
Postman generated header
Authorization:OAuth oauth_consumer_key="4e77abaec9b6fcda9kjgkjgh44c2e1",oauth_token="2da9439r34104293b1gfhse2feaffca9a1",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1482470443",oauth_nonce="cCbH5b",oauth_version="1.0",oauth_signature="A1QPwTATVF4x3cN0%2FN46CZrtSKw%3D"
Problem
I googled a lot to create oauth signature like postmasn created to attach volley ServerConnectionChannel but failed.
oauth_signature="A1QPwTATVF4x3cN0%2FN46CZrtSKw%3D"
Current code
public void doSendJsonRequest(final ERequest ERequest) {
requestMethod = String.valueOf(ERequest.method);
requestUrl = String.valueOf(ERequest.mReqUrl);
if(requestMethod.equals(Request.Method.GET)){
requestMethod = "GET";
}else if(requestMethod.equals(Request.Method.POST)){
requestMethod = "POST";
}else if(requestMethod.equals(Request.Method.PUT)){
requestMethod = "PUT";
}else if(requestMethod.equals(Request.Method.DELETE)){
requestMethod = "DELETE";
}
Long tsLong = System.currentTimeMillis()/1000;
final String ts = tsLong.toString();
final String kk = requestMethod+"&" + encode(requestUrl)+"&";
final String kk = encode("GET"+"&"
+ requestUrl+"&"
+ OAUTH_CONSUMER_KEY + "=\"4e77abaec9b6fcda9b11e89a9744c2e1\"&"
+OAUTH_NONCE + "=\"" + getNonce()+ "\"&"
+OAUTH_SIGNATURE_METHOD + "=\""+OAUTH_SIGNATURE_METHOD_VALUE+"\"&"
+OAUTH_TIMESTAMP + "=\"" + ts + "\"&"
+OAUTH_TOKEN +"=\"2da943934104293b167fe2feaffca9a1\"");
RequestQueue queue = VolleyUtils.getRequestQueue();
try {
JSONObject jsonObject = ERequest.jsonObject;
EJsonRequest myReq = new EJsonRequest(ERequest.method, ERequest.mReqUrl, jsonObject, createReqSuccessListener(ERequest), createReqErrorListener(ERequest)) {
public Map < String, String > getHeaders() throws AuthFailureError {
// Long tsLong = System.currentTimeMillis()/1000;
// String ts = tsLong.toString();
String strHmacSha1 = "";
String oauthStr = "";
strHmacSha1 = generateSignature(kk, oAuthConsumerSecret, oAuthTokenSecret);
strHmacSha1 = toSHA1(strHmacSha1.getBytes());
Log.e("SHA !",strHmacSha1);
oauthStr ="OAuth "+ OAUTH_CONSUMER_KEY + "=\"4e77abaec9b6fcda9b11e89a9744c2e1\","
+OAUTH_TOKEN +"=\"2da943934104293b167fe2feaffca9a1\","
+OAUTH_SIGNATURE_METHOD + "=\""+OAUTH_SIGNATURE_METHOD_VALUE+"\","
+OAUTH_TIMESTAMP + "=\"" + ts + "\","
+OAUTH_NONCE + "=\"" + getNonce()+ "\","
+OAUTH_VERSION + "=\"" + OAUTH_VERSION_VALUE + "\","
+OAUTH_SIGNATURE + "=\"" + strHmacSha1+ "\"";
Log.e("VALUE OF OAuth str",oauthStr);
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json");
params.put("Authorization",oauthStr);
// params.put("Authorization",getConsumer().toString());
return params;
}
};
myReq.setRetryPolicy(new DefaultRetryPolicy(
DefaultRetryPolicy.DEFAULT_TIMEOUT_MS * 4,
BABTAIN_MAX_RETRIES,
BABTAIN_BACKOFF_MULT));
myReq.setHeader("Cache-Control", "no-cache");
//myReq.setHeader("Content-Type", "application/json");
queue.add(myReq);
} catch (Exception e) {
e.printStackTrace();
}
private String generateSignature(String signatueBaseStr, String oAuthConsumerSecret, String oAuthTokenSecret) {
byte[] byteHMAC = null;
try {
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec spec;
if (null == oAuthTokenSecret) {
String signingKey = encode(oAuthConsumerSecret) + '&';
spec = new SecretKeySpec(signingKey.getBytes(), "HmacSHA1");
} else {
String signingKey = encode(oAuthConsumerSecret) + '&' + encode(oAuthTokenSecret);
spec = new SecretKeySpec(signingKey.getBytes(), "HmacSHA1");
}
mac.init(spec);
byteHMAC = mac.doFinal(signatueBaseStr.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
String base64 = Base64.encodeToString(byteHMAC, Base64.DEFAULT);
return base64.trim();
}
private String toSHA1(byte[] convertme) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance("SHA-1");
}
catch(NoSuchAlgorithmException e) {
e.printStackTrace();
}
return byteArrayToHexString(md.digest(convertme));
}
private String byteArrayToHexString(byte[] b) {
String result = "";
for (int i=0; i < b.length; i++)
result += Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
return result;
}
this code create a signature like :oauth_signature="42a611860e29e893a435b555e7a9559a704f4e94" and it failed to get autherization.
getting error like : BasicNetwork.performRequest: Unexpected response code 401 for url
?How to generate oauth_signature like postman provided using volley..
?how can i improve this code ?Is any libraries or default function to do that
?How we add oauth1 signature in volley..
Please Help.. Thank you
I just found a github example for generating signature corresponding to nonce in Oauth1 and successfully integrate into my project
here is the link : https://github.com/rameshvoltella/WoocommerceAndroidOAuth1

Apache HTTP Client Android Exception on Execute only for LG G3 6.0

I've taken over an android app that takes pictures and attaches them to jobs for a larger software system at a company's home base- it has worked fine until recently.
It seems that only on LG G3 phones that have upgraded to Android 6.0 there is an exception in this prodecure:
public static String frapiGetRequest(String transaction, ArrayList<Content> parameters) {
StringBuilder builder = new StringBuilder();
DefaultHttpClient client = new DefaultHttpClient();
HttpHost targetHost = new HttpHost(HOST,PORT,SCHEME);
String url = SCHEME + "://" + HOST + "/" + transaction;
if (parameters != null && parameters.size() > 0) {
url += "?" + buildParameterString(parameters);
}
Utilities.bLog(TAG, "Making FrapiRequest -- " + url);
try {
HttpGet getRequest = new HttpGet(url);
client.getCredentialsProvider().setCredentials(
new AuthScope(targetHost.getHostName(), targetHost.getPort()),
new UsernamePasswordCredentials(USERNAME, PASSWORD));
/**Exception Occurs Here**/
HttpResponse response = client.execute(getRequest);
StatusLine statusLine = response.getStatusLine();
int statusCode = -1;
statusCode = statusLine.getStatusCode();
if (statusCode == 200) {
HttpEntity entity = response.getEntity();
InputStream content = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(content));
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
Utilities.bLog(TAG,"Frapi Request Succeeded");
}
else {
Utilities.bLog(TAG, "Frapi Request Failed: " + url);
}
} catch (ClientProtocolException e) {
e.printStackTrace();
Utilities.eLog(e);
} catch (IOException e) {
e.printStackTrace();
Utilities.eLog(e);
} catch (Exception e) {
e.printStackTrace();
Utilities.eLog(e);
}
return builder.toString();
}
The stack trace
java.lang.ArrayIndexOutOfBoundsException: length=1; index=1
at org.apache.http.impl.auth.DigestScheme.isGbaScheme(DigestScheme.java:210)
at org.apache.http.impl.auth.DigestScheme.processChallenge(DigestScheme.java:176)
at org.apache.http.impl.client.DefaultRequestDirector.processChallenges(DefaultRequestDirector.java:1097)
at org.apache.http.impl.client.DefaultRequestDirector.handleResponse(DefaultRequestDirector.java:980)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:490)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:560)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:492)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:470)
at com.rossware.sd_quickpics.Utilities.frapiGetRequest(Utilities.java:111)
at com.rossware.sd_quickpics.Business.authenticate(Business.java:83)
at com.rossware.sd_quickpics.MainActivity$AuthenticateAsyncTask.doInBackground(MainActivity.java:320)
at com.rossware.sd_quickpics.MainActivity$AuthenticateAsyncTask.doInBackground(MainActivity.java:307)
at android.os.AsyncTask$2.call(AsyncTask.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
This hasn't been reported on any other phone.. I would use HttpURLConnection But it doesn't support Digest Authentication (which is currently what our frapi server is using)
I'm just not sure if there's any way to continue using the authentication mechanism we have or if I have to implement a different protocol in frapi (hopefully without breaking all of our existing applications..) or if there is another way to bypass this issue for the folks with these phones? This issue is pretty restricted (one client who has about 10 phones, not the end of the world, but definitely a major issue for them)
Is there anything in android that I can do to resolve this kind of problem for the affected users? Does it seem like the code is incorrect?
It is possible to use DigestAuth with HttpUrlConnection:
private InputStream connect(String urlStr, String username, String password) throws Exception {
URL url = new URL(urlStr);
HttpURLConnection connection = (HttpURLConnection) new URL(urlStr).openConnection();
connection.setDoInput(true);
connection.setRequestMethod("GET");
try {
return connection.getInputStream();
} catch(Exception e) {
if (connection.getResponseCode() == 401) {
String header = connection.getHeaderField("WWW-Authenticate");
String uri = new URL(urlStr).getFile();
String nonce = Tools.match(header, "nonce=\"([A-F0-9]+)\"");
String realm = match(header, "realm=\"(.*?)\"");
String qop = match(header, "qop=\"(.*?)\"");
String algorithm = match(header, "algorithm=(.*?),");
String cnonce = generateCNonce();
String ha1 = username + ":" + realm + ":" + password;
String ha1String = md5digestHex(ha1);
String ha2 = "GET" + ":" + uri;
String ha2String = md5digestHex(ha2);
int nc = 1;
String response = ha1String + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2String;
String responseString = md5digestHex(response);
String authorization =
"Digest username=\"" + username + "\"" +
", realm=\"" + realm + "\"" +
", nonce=\"" + nonce + "\"" +
", uri=\"" + uri + "\"" +
", qop=\"" + qop + "\"" +
", nc=\"" + nc + "\"" +
", cnonce=\"" + cnonce + "\"" +
", response=\"" + responseString + "\"" +
", algorithm=\"" + algorithm + "\"";
HttpURLConnection digestAuthConnection = prepareConnection(urlStr);
digestAuthConnection.setRequestMethod("GET");
digestAuthConnection.setRequestProperty("Authorization", authorization);
return processResponse(digestAuthConnection);
} else throw e;
}
}
public static String match(String s, String patternString, boolean strict) {
if (!isEmpty(s) && !isEmpty(patternString)) {
Pattern pattern = Pattern.compile(patternString);
if (pattern != null) {
Matcher matcher = pattern.matcher(s);
if (matcher != null && matcher.find() && (matcher.groupCount() == 1 || !strict)) {
return matcher.group(1);
}
}
}
return null;
}
public static String match(String s, String patternString) {
return match(s, patternString, true);
}
public static byte[] md5Digist(String s) {
try {
MessageDigest md5 = MessageDigest.getInstance("md5");
md5.update(s.getBytes());
return md5.digest();
} catch (NoSuchAlgorithmException e) {
return null;
}
}
public static String digest2HexString(byte[] digest) {
String digestString="";
int low, hi;
for (int i = 0; i < digest.length; i++) {
low = (digest[i] & 0x0f ) ;
hi = ((digest[i] & 0xf0) >> 4);
digestString += Integer.toHexString(hi);
digestString += Integer.toHexString(low);
}
return digestString;
}
public static String md5digestHex(String s) {
return digest2HexString(md5Digist(s));
}
public static String generateCNonce() {
String s = "";
for (int i = 0; i < 8; i++) {
s += Integer.toHexString(new Random().nextInt(16));
}
return s;
}
I ran into a similar issue today and just started using HttpClient for Android
Added dependency compile 'org.apache.httpcomponents:httpclient-android:4.3.5.1' to build.gradle.
Replace new DefaultHttpClient() with HttpClientBuilder.create().build()
There are probably some other minor refactors you might need to make in other portions of the code, but that should be pretty straight forward.

Bad Request sending gcm message

I am trying to send gcm message via C#.
I tried several times, but I get http:400-Bad request while trying to send it in json method.
When I try to send it in text, I can't read it (rtl language) - that's why I am trying JSON.
Anyone knows what the problem?
Thanks!
private static string SendNotificationJson2(string id, string msg)
{
var AuthString = "AIzaSyDAtmaqSdutBQemqmd4dQgf33B_6ssbvXA";
var RegistrationID = id;
var Message = msg;
//-- Create GCM request insted of C2DM Web Request Object --//
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://android.googleapis.com/gcm/send");
Request.Method = "POST";
Request.KeepAlive = false;
//-- Create Query String --//
Dictionary<String, String> dict = new Dictionary<string, string>();
dict.Add("registration_ids", RegistrationID);
dict.Add("data", Message);
dict.Add("collapse_key", "1");
string postData = GetPostStringFrom(dict);
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
Request.ContentType = "application/json";
Request.ContentLength = byteArray.Length;
Request.Headers.Add("Authorization", "key=" + AuthString);
//-- Delegate Modeling to Validate Server Certificate --//
ServicePointManager.ServerCertificateValidationCallback += delegate(
object
sender,
System.Security.Cryptography.X509Certificates.X509Certificate
pCertificate,
System.Security.Cryptography.X509Certificates.X509Chain pChain,
System.Net.Security.SslPolicyErrors pSSLPolicyErrors)
{
return true;
};
//-- Create Stream to Write Byte Array --//
Stream dataStream = Request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
//-- Post a Message --//
WebResponse Response = Request.GetResponse();
HttpStatusCode ResponseCode = ((HttpWebResponse)Response).StatusCode;
if (ResponseCode.Equals(HttpStatusCode.Unauthorized) || ResponseCode.Equals(HttpStatusCode.Forbidden))
{
return "Unauthorized - need new token";
}
else if (!ResponseCode.Equals(HttpStatusCode.OK))
{
return "Response from web service isn't OK";
//Console.WriteLine("Response from web service not OK :");
//Console.WriteLine(((HttpWebResponse)Response).StatusDescription);
}
StreamReader Reader = new StreamReader(Response.GetResponseStream());
string responseLine = Reader.ReadLine();
Reader.Close();
return "ok";
}
private static string GetPostStringFrom(Dictionary<string,string> postFieldNameValue)
{
// return Newtonsoft.Json.JsonConvert.SerializeObject(postFieldNameValue);
return "\"data\": {\"Message\": \"" + postFieldNameValue["data"] + "\"},\"registration_ids\":[\"" + postFieldNameValue["registration_ids"] + "\"]}";
}</code>
You forgot first bracket in Json data
use
return "{\"data\": {\"Message\": \"" + postFieldNameValue["data"] + "\"},\"registration_ids\":[\"" + postFieldNameValue["registration_ids"] + "\"]}";
instead of
return "\"data\": {\"Message\": \"" + postFieldNameValue["data"] + "\"},\"registration_ids\":[\"" + postFieldNameValue["registration_ids"] + "\"]}";
Your registration ID needs to be a JSON array so use a list instead of a single string.

Android retrieve return value from WCF

I have an Android app that posts to a web service via the code below and it all works fine. But the myContract method in the service returns a Boolean, true or false. How do I retrieve that value so I can tell my app to move on or not if false?
HttpPost request = new HttpPost(SERVICE_URI + "/myContract/someString");
request.setHeader("Accept", "application/json");
request.setHeader("Content-type", "application/json");
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpResponse response = httpClient.execute(request);
Edit
Sorry about the edit, but using HttpResponse, and then logging or toasting response.toString() returns a string I don’t understand!
Update
Thanks Shereef,
But that seems like a bit too much information and code to do what I was trying to do. I have added some code below that works but I’m not sure if it’s right. The service will return a Boolean true or false as to whether or not the POST was successful, but I seem to be retrieving it as a string!
HttpEntity responseEntity = response.getEntity();
char[] buffer = new char[(int)responseEntity.getContentLength()];
InputStream stream = responseEntity.getContent();
InputStreamReader reader = new InputStreamReader(stream);
reader.read(buffer);
stream.close();
JSONObject jsonResponse = new JSONObject(new String(buffer));
String ServiceResponse = jsonResponse.getString("putCommuniqueResult");
Log.d("WebInvoke", "Saving : " + ServiceResponse);
Is this ok? It works but I’m not sure if its right!
Cheers,
Mike.
private static String getDataFromXML(final String text) {
final String temp = new String(text).split("<")[2].split(">")[1];
final String temp2 = temp.replace("<", "<").replace(">", ">")
.replace("&", "&");
return temp2;
}
/**
* Connects to the web service and returns the pure string returned, NOTE:
* if the generated url is more than 1024 it automatically delegates to
* connectPOST
*
* #param hostName
* : the host name ex: google.com or IP ex:
* 127.0.0.1
* #param webService
* : web service name ex: TestWS
* #param classOrEndPoint
* : file or end point ex: CTest
* #param method
* : method being called ex: TestMethod
* #param parameters
* : Array of {String Key, String Value} ex: { { "Username",
* "admin" }, { "Password", "313233" } }
* #return the trimmed String received from the web service
*
* #author Shereef Marzouk - http://shereef.net
*
*
*/
public static String connectGET(final String hostNameOrIP,
final String webService, final String classOrEndPoint,
final String method, final String[][] parameters) {
String url = "http://" + hostNameOrIP + "/" + webService + "/"
+ classOrEndPoint + "/" + method;
String params = "";
if (null != parameters) {
for (final String[] strings : parameters) {
if (strings.length == 2) {
if (params.length() != 0) {
params += "&";
}
params += strings[0] + "=" + strings[1];
} else {
Log.e(Standards.TAG,
"The array 'parameters' has the wrong dimensions("
+ strings.length + ") in " + method + "("
+ parameters.toString() + ")");
}
}
}
url += "?" + params;
if (url.length() >= 1024) { // The URL will be truncated if it is more
// than 1024
return Communications.connectPOST(hostNameOrIP, webService,
classOrEndPoint, method, parameters);
}
final StringBuffer text = new StringBuffer();
HttpURLConnection conn = null;
InputStreamReader in = null;
BufferedReader buff = null;
try {
final URL page = new URL(url);
conn = (HttpURLConnection) page.openConnection();
conn.connect();
in = new InputStreamReader((InputStream) conn.getContent());
buff = new BufferedReader(in);
String line;
while (null != (line = buff.readLine()) && !"null".equals(line)) {
text.append(line + "\n");
}
} catch (final Exception e) {
Log.e(Standards.TAG,
"Exception while getting " + method + " from " + webService
+ "/" + classOrEndPoint + " with parameters: "
+ params + ", exception: " + e.toString()
+ ", cause: " + e.getCause() + ", message: "
+ e.getMessage());
Standards.stackTracePrint(e.getStackTrace(), method);
return null;
} finally {
if (null != buff) {
try {
buff.close();
} catch (final IOException e1) {
}
buff = null;
}
if (null != in) {
try {
in.close();
} catch (final IOException e1) {
}
in = null;
}
if (null != conn) {
conn.disconnect();
conn = null;
}
}
if (text.length() > 0 && Communications.checkText(text.toString())) {
final String temp = Communications.getDataFromXML(text.toString());
Log.i(Standards.TAG, "Success in " + method + "(" + params
+ ") = " + temp);
return temp;
}
Log.w(Standards.TAG, "Warning: " + method + "(" + params + "), text = "
+ text.toString());
return null;
}
let's say this url makes your service shows it's output
http://google.com/wcfsvc/service.svc/showuserdata/11949
public boolean isWSTrue() {
String data = connectGET("google.com",
"wcfsvc", "service.svc",
"showuserdata/11949", null);
if(null != data && data.length() >0)
return data.toLowerCase().contains("true");
throw new Exception("failed to get webservice data");
}
Note: that within this case you do not actually need to parse the JSON or XML if only checking for boolean then you know if you found true it's true if found anything else it's false.
if you need to get data using XML or JSON you can refer to this answer https://stackoverflow.com/a/3812146/435706

Categories

Resources