7

EDITTED: "The code below works fine, no errors, no exceptions"

I'm aware of the grand amount of questions in regards to this topic, as well as the many blogs that google conjures up. I have read through them and have managed to come up with what I'm about to explain. My doubt lies in "is my approach correct? Does it have any side-effects?" and another question that is better asked as I explain my method.

I based this approach following this Android.Developres tutorial.

 System.setProperty("jsse.enableSNIExtension", "false");  
 //Java 7 introduced SNI (enabled by default). The server I use is
 // misconfigured I suppose and
 // it sends an "Unrecognized Name" warning in the SSL handshake
 // which breaks my web service.

// Load CA from an InputStream (CA would be saved in Raw file,
// and loaded as a raw resource)
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    InputStream caInput = new BufferedInputStream(new FileInputStream("PATH_TO_CERT.crt"));

    Certificate ca;
    try {
        ca = cf.generateCertificate(caInput);
    } finally {
        caInput.close();
    }

    // Create a KeyStore containing our trusted CAs
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", ca); 

    // Create a TrustManager that trusts the CAs in our KeyStore
    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
    tmf.init(keyStore);

    // Create an SSLContext that uses our TrustManager
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, tmf.getTrustManagers(), null);

 // Create all-trusting host name verifier 
    //  to avoid the following :
    //   java.security.cert.CertificateException: No name matching
    // This is because Java by default verifies that the certificate CN (Common Name) is
   // the same as host name in the URL. If they are not, the web service client fails.

     HostnameVerifier allHostsValid = new HostnameVerifier() {
        @Override
        public boolean verify(String arg0, SSLSession arg1) {
            return true;
        }
    };

    //Install it
    HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);


    // Tell the URLConnection to use a SocketFactory from our SSLContext
    URL url = new URL("https....");
            urlConnection.setSSLSocketFactory(context.getSocketFactory());

    try {

        HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
        urlConnection.setRequestMethod("GET");

        urlConnection.connect();

        switch(urlConnection.getResponseCode()){
        case 401:


            BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getErrorStream()));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line+"\n");
            }
            br.close();

            System.out.println( sb.toString());

        }



    } catch (MalformedURLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ProtocolException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }



}

Here is my other question, in the following line:

InputStream caInput = new BufferedInputStream(new FileInputStream("PATH_TO_CERT.crt"));

You see that the method forces me to have the certificate.crt preloaded onto raw file inside res folder. Is there a way (I have looked but have found 0 answers) to connect to the server and download said certificate.crt and save it on a private folder not accessible by the user?

Chayemor
  • 3,577
  • 4
  • 31
  • 54
  • A complete solution to your problem you can find here: http://stackoverflow.com/a/16759793/1817029 – My-Name-Is Feb 28 '14 at 17:14
  • @My-Name-Is , I don´t have a problem. My code works fine and it does the same as yours more or less(load local certificate, create keystore, give it to trustmanager...etc). My questions are in regards to if the approach is correct and if there is a way to NOT have the certificate as a RAW RESOURCE but rather download it dynamically. – Chayemor Feb 28 '14 at 23:20
  • Joy did you solve the problem? I'm facing exactly the same problem as you. – Pedro Teran May 22 '14 at 21:44
  • @PedroTeran Sadly I haven't found a way to download the certificate dynamically. It must be preloaded onto the res folders before shipping out the apk. Is that what you are asking? – Chayemor May 27 '14 at 17:50
  • @Joy thanks for answering.. now I'm preloading a bks keystore on raw folder. but I'm not completely happy with it since we will have problems with certificates rotation. Hope to find a better solution. – Pedro Teran May 27 '14 at 20:38
  • Same with me. If I ever find anything, I´ll be sure to post it. Good luck! – Chayemor May 28 '14 at 10:17
  • To the users that have downvoted the question, please comment as to why. I'd like to learn. – Chayemor Dec 16 '15 at 09:42
  • "_Is there a way (I have looked but have found 0 answers) to connect to the server and download said certificate.crt and save it on a private folder not accessible by the user?_" You could download the certificate from a server which does not expect a self-signed cert, then store it in [internal storage](https://developer.android.com/guide/topics/data/data-storage.html#filesInternal), which is private to your application. Alternatively, you could still package the certificate in the resources, but only use that cert to encrypt the request with which you download the real cert. – cbr Mar 26 '17 at 01:58

0 Answers0