java - Trust Only Certificates Signed by Specific CA on Android 6 -
dear community of awesomeness,
i'm building secure app deals sensitive information. app communicates own restful api on ssl. don't want limit app specific certificate issued, rather trust certificates issued provider, e.g. comodo. way can extend , reissue certificate without having release app update.
i found great resource getting this done here android 6 deprecated httpclient
, switched httpsurlconnection
. google has their own approach posted here. on implementation, however, noticed instead of throwing "not trusted" exception different certificate, forced usage of local ca cert not behavior intended.
does have reference trusting specific ca using httpsurlconnection
?
ok solved it, figured post solution in case else hits same problem. here code use json file using httpsurlconnection
:
(...) public static class getjsontask extends asynctask<void, integer, asyncresponse> { protected string jsondata; protected igetjsonlistener listener; protected context context = null; protected string strurl; public getjsontask(context c, igetjsonlistener l, string strurl) { super(); listener = l; context = c; this.strurl = strurl; } @override protected asyncresponse doinbackground(void... void) { jsonobject jsonobjectresult = new jsonobject(); apistatus status; if (isconnected(context)) { httpsurlconnection httpsurlconnection=null; try { //this key: context contains our ca cert sslcontext sslcontext = getsslcontext(context); if (sslcontext != null) { //for http basic auth if server implements //string encoded = base64.encodetostring( // ("your_user_name" + ":" + "your_pwd").getbytes(), // base64.default); url url = new url(strurl); httpsurlconnection = (httpsurlconnection) url.openconnection(); httpsurlconnection.setrequestmethod("get"); httpsurlconnection.setrequestproperty("content-length", "0"); httpsurlconnection.setusecaches(false); httpsurlconnection.setallowuserinteraction(false); //for http basic auth //httpsurlconnection.setrequestproperty("authorization", "basic " + encoded); //this key: set connection use custom socket factory httpsurlconnection.setsslsocketfactory(sslcontext.getsocketfactory()); //httpsurlconnection.setconnecttimeout(timeout); //httpsurlconnection.setreadtimeout(timeout); httpsurlconnection.connect(); status = getstatusfromcode(httpsurlconnection.getresponsecode()); listener.getjsonshowprogress(90); if (status == apistatus.ok) { bufferedreader bufferedreader = new bufferedreader(new inputstreamreader(httpsurlconnection.getinputstream())); stringbuilder stringbuilder = new stringbuilder(); string line; while ((line = bufferedreader.readline()) != null) { stringbuilder.append(line); } bufferedreader.close(); jsonparser parser = new jsonparser(); string s = stringbuilder.tostring(); jsonobjectresult = (jsonobject) parser.parse(s); } } else status = apistatus.auth_error; listener.getjsonshowprogress(99); //this key: exception thrown if certificate //is signed ca not our ca } catch (sslhandshakeexception e) { status = apistatus.auth_error; //react man-in-the-middle attack } catch (ioexception e) { status = apistatus.net_error; } catch (jsonparseexception e) { status = apistatus.json_error; } catch (exception e) { status = apistatus.unknown_error; } { if (httpsurlconnection != null) httpsurlconnection.disconnect(); } } else { status = apistatus.net_error; } // if not successful issue call next hour. asyncresponse response = new asyncresponse(); response.jsondata = jsonobjectresult; response.opstatus = status; return response; } @override protected void onpreexecute() { super.onpreexecute(); if (listener != null) listener.getjsonstartprogress(); } @override protected void onprogressupdate(integer... progress) { listener.getjsonshowprogress(progress[0]); } @override protected void onpostexecute(asyncresponse result) { listener.getjsonfinished(result.jsondata, result.opstatus); } public interface igetjsonlistener { void getjsonstartprogress(); void getjsonshowprogress(int percent); void getjsonfinished(jsonobject resjson, apistatus status); } } private static sslcontext getsslcontext(context context){ //mostly taken google code link in question. try { certificatefactory cf = certificatefactory.getinstance("x.509"); assetmanager = context.getassets(); //this key: ca's cert stored in /assets/ inputstream cainput = new bufferedinputstream(am.open("rootca.crt")); certificate ca; try { ca = cf.generatecertificate(cainput); //system.out.println("ca=" + ((x509certificate) ca).getsubjectdn()); } { cainput.close(); } // create keystore containing our trusted cas string keystoretype = keystore.getdefaulttype(); keystore keystore = keystore.getinstance(keystoretype); keystore.load(null, null); keystore.setcertificateentry("ca", ca); // create trustmanager trusts cas in our keystore string tmfalgorithm = trustmanagerfactory.getdefaultalgorithm(); trustmanagerfactory tmf = trustmanagerfactory.getinstance(tmfalgorithm); tmf.init(keystore); // create sslcontext uses our trustmanager sslcontext sslcontext = sslcontext.getinstance("tls"); sslcontext.init(null, tmf.gettrustmanagers(), null); return sslcontext; } catch (exception e){ return null; } } public enum apistatus { ok("ok.", 200), //all went json_error("error parsing response.", 1), net_error("network error.", 2), //we couldn't reach server unknown_error("unknown error.", 3), //some sh*t went down auth_error("authentication error.", 401), //credentials wrong server_error("internal server error.", 500), //server code crashed timeout("operation timed out.", 408); //network slow or server overloaded private string stringvalue; private int intvalue; private apistatus(string tostring, int value) { stringvalue = tostring; intvalue = value; } @override public string tostring() { return stringvalue; } } private static apistatus getstatusfromcode(int code) { if (code==200 || code==201) { return apistatus.ok; }else if (code == 401) { return apistatus.auth_error; } else if (code == 500) { return apistatus.server_error; } else if (code == 408) { return apistatus.timeout; } else { return apistatus.unknown_error; } } private static class asyncresponse { public apistatus opstatus; public jsonobject jsondata; } (...)
usage straightforward:
public class myclass implements igetjsonlistener { (...) new getjsontask(context, this, "https://your.url.com/").execute(); @override public void getjsonfinished(jsonobject resjson, apistatus status) { //handle json content web here (...) } (...) }
i'd love hear improvements have.
Comments
Post a Comment