0

I am able to get the certificate loaded properly and can access my server (baby steps...) My problem now is that the parameters ("username=admin&password=admin") are not getting into the server (the request is coming as null). Here is my Android code (some of which) I got from here:

    String host = "turbo";
    int port = 8081;
    String command = "get_status";
    String user = "admin";
    String password = "admin";
    HttpsURLConnection connection = null;

    try {
        Certificate ca;
        String keyStoreType;
        KeyStore keyStore;
        String tmfAlgorithm;
        TrustManagerFactory tmf;
        Scanner scanner;
        boolean hasInput;
        /**
         * Set up certificate stuff first
         */
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream ins = myContext.getResources().openRawResource(
                myContext.getResources().getIdentifier("server_crt", "raw",
                        myContext.getPackageName()));
        BufferedInputStream caInput = new BufferedInputStream(ins);
        ca = cf.generateCertificate(caInput);
        caInput.close();
        keyStoreType = KeyStore.getDefaultType();
        keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);
        tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, tmf.getTrustManagers(), null);

        URL url = new URL(String.format("https://%s:%d/test/%s", host, port, command));

        connection = (HttpsURLConnection) url.openConnection();
        connection.setSSLSocketFactory(context.getSocketFactory());
        connection.setReadTimeout(1500);
        connection.setConnectTimeout(1500);
        connection.setRequestMethod("POST");
        connection.setDoInput(true);
        connection.setDoOutput(true);
        DataOutputStream os = new DataOutputStream(connection.getOutputStream());
        os.writeBytes("username=admin&password=admin");
        String b = getPostDataString(params);
        os.flush();
        os.close();

I had to change my Flask run method to have host='0.0.0.0' and now I am getting connected with my Android code below, but I am having other issues: I am having problems getting the parameters across (username and password).

On the server side, I am getting an error where a parameter (username) is null.

I have tried this also:

        Map<String,Object> params = new LinkedHashMap<>();
        params.put("username", user);
        params.put("password", password);
        StringBuilder postData = new StringBuilder();
        for (Map.Entry<String,Object> param : params.entrySet()) {
            if (postData.length() != 0) postData.append('&');
            postData.append(URLEncoder.encode(param.getKey(), "UTF-8"));
            postData.append('=');
            postData.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
        }
        byte[] postDataBytes = postData.toString().getBytes("UTF-8");
        connection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
        connection.setDoOutput(true);
        connection.getOutputStream().write(postDataBytes);

        connection.setReadTimeout(30000);
        connection.setConnectTimeout(30000);
        int responseCode = connection.getResponseCode();

But the responseCode is 400 and I am getting the same error on the server (username not defined) Any suggestions?

app.run(debug=server_debug, ssl_context=context, host='0.0.0.0', port=server_port)

I am having problems connecting via HTTPS from my Android app with a self-signed certification.

I created the create_key.cfg:

[req]
default_bits=2048
prompt=no
default_md=sha256
x509_extensions=v3_req
#req_extensions=req_ext
distinguished_name=dn

[dn]
C=US
ST=Florida
L=Satellite Beach
O=ThompCo, Inc
OU=turbo
emailAddress=Jordan@ThompCo.com
CN=turbo

[v3_req]
subjectAltName=@alt_names

[alt_names]
DNS.1=turbo
DNS.2=turbo.thompco.com
DNS.3=thompco.com

Then I ran the following commands:

openssl req -x509 -nodes -days 730 -newkey rsa:2048 -keyout server.key -out server.crt -config create_key.cfg -sha256
openssl x509 -in server.crt -out server.pem -outform PEM

which creates the files:

server.crt  server.key  server.pem

When I access the site with Chrome, I get the correct certificate information (obviously there is a warning that the certificate is not secure)

When I run my test python scripts (which use urllib2) everything works as expected.

I tried running straight curl:

curl https://turbo:8081

But I got this error:

curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

I tried adding the crt file to /usr/lib/ssl/certs as suggested here but it didn't help (perhaps this is the root of the problem?)

I am dabbling (read "I have no idea what I am doing") in Android and have written (with the help of Google and many fine developers around the world) the following (Note that I put server.pem into the res/raw folder):

package com.thompco.test;

import android.content.Context;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Scanner;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

class DoCurl extends Thread {
    Context myParentContext;

    public DoCurl(Context context) {
        myParentContext = context;
    }

    public void run() {
        String host = "turbo";
        int port = 8081;
        HttpsURLConnection connection = null;
        String command = "get_status";
        String http = "https";
        String user = "admin";
        String password = "admin";

        try {
            Certificate ca;
            String keyStoreType;
            KeyStore keyStore;
            String tmfAlgorithm;
            TrustManagerFactory tmf;
            SSLContext context;
            URL url;
            InputStream in;
            Scanner scanner;
            boolean hasInput;
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            InputStream ins = myParentContext.getResources().openRawResource(
                    myParentContext.getResources().getIdentifier("server",
                            "raw", myParentContext.getPackageName()));
            BufferedInputStream caInput = new BufferedInputStream(ins);
            try {
                ca = cf.generateCertificate(caInput);
                System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
            } finally {
                caInput.close();
            }
            keyStoreType = KeyStore.getDefaultType();
            keyStore = KeyStore.getInstance(keyStoreType);
            keyStore.load(null, null);
            keyStore.setCertificateEntry("ca", ca);
            tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(keyStore);
            context = SSLContext.getInstance("TLS");
            context.init(null, tmf.getTrustManagers(), null);
            url = new URL(String.format("%s://%s:%d/test/%s", http, host, port, command));
            connection = (HttpsURLConnection) url.openConnection();
            connection.setSSLSocketFactory(context.getSocketFactory());
            connection.setRequestProperty("username", user);
            connection.setRequestProperty("password", password);
            connection.setReadTimeout(30000);
            connection.setConnectTimeout(30000);
            connection.setRequestMethod("POST");
            connection.setDoInput(true);
            connection.connect();
            if (connection.getResponseCode() == 200) {
                // Success
                // Further processing here
            } else {
                // Error handling code goes here
            }
            in = connection.getInputStream();

            scanner = new Scanner(in);
            scanner.useDelimiter("\\A");

            hasInput = scanner.hasNext();
            if (hasInput) {
                String tmp = scanner.next();
            }
        } catch (Exception e) {
            Exception blah = e;
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
}

When this runs, it throws a java.security.cert.CertPathValidatorException: Trust anchor for certification path not found when trying to connect.

My goal is to be able to deploy this device (currently running Flask) and be able to access it from an Android phone on my local network. I don't want to install a truly trusted certificate if I can get away with it.

The server happens to be running in python on a windows machine. It works fine with my curl tests and I know that that is not the best way to do this, but I am just testing the Android app for now.

I went back to my Python test code and confirmed that I am connecting correctly (note that I am using the appropriate .crt file) which should validate the validity of the self-signed certificate:

def send_curl(command, host, port, data, use_ssh=True):
    logger = get_logger()
    logger.debug("host:{}, port:{}".format(host, port))
    context = ssl.create_default_context()
    context.load_verify_locations("../keys/server.crt")
    url = '{}://{}:{}/test/{}'.format("https", host, port, command)
    for k, v in data.items():
        data[k] = str(v)
    data = json.dumps(data)
    logger.debug("URL is {}".format(url))
    logger.debug("data is {}".format(data))
    req = urllib2.Request(url, data, {'Content-Type': 'application/json'})
    try:
        rtn = urllib2.urlopen(req, context=context)
        pp = pprint.PrettyPrinter()
        pp.format = my_safe_repr
        print "Success:" + str(rtn.code)
        pp.pprint(data)
    except urllib2.HTTPError as err:
        print str(err) + ":"
        print err.read()
        data = None
    return data
Cœur
  • 37,241
  • 25
  • 195
  • 267
jordanthompson
  • 888
  • 1
  • 12
  • 29

2 Answers2

0

you can create this class on your android app.

    import java.security.KeyManagementException;
    import java.security.NoSuchAlgorithmException;
    import java.security.SecureRandom;
    import java.security.cert.X509Certificate;

    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;

    public class HttpsTrustManager implements X509TrustManager {

        private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
        private static TrustManager[] trustManagers;

        public static void allowAllSSL() {
    //        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
    //
    //            @Override
    //            public boolean verify(String arg0, SSLSession arg1) {
    //                return true;
    //            }
    //        });

            SSLContext context = null;
            if (trustManagers == null) {
                trustManagers = new TrustManager[]{new HttpsTrustManager()};
            }

            try {
                context = SSLContext.getInstance("TLS");
                context.init(null, trustManagers, new SecureRandom());
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (KeyManagementException e) {
                e.printStackTrace();
            }

            HttpsURLConnection.setDefaultSSLSocketFactory(context
                    .getSocketFactory());
        }

        @Override
        public void checkClientTrusted(
                X509Certificate[] x509Certificates, String s)
                throws java.security.cert.CertificateException {

        }

        @Override
        public void checkServerTrusted(
                X509Certificate[] x509Certificates, String s)
                throws java.security.cert.CertificateException {

        }

        public boolean isClientTrusted(X509Certificate[] chain) {
            return true;
        }

        public boolean isServerTrusted(X509Certificate[] chain) {
            return true;
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return _AcceptedIssuers;
        }

    }

and use HttpsTrustManager.allowAllSSL(); method on OnCreate()

sudesh regmi
  • 536
  • 4
  • 12
  • I'm not really sure that I want to allow ALL SSL connections (even though this is the only place the app will connect.) I was hoping to use the same certificate that I used for the web site and do it the "proper" way. For what its worth, I did try your suggestion and am getting the same exception, which makes me think there maybe something else going on? – jordanthompson Jul 24 '18 at 16:48
0

Here is my working solution that solves several issues (securely connecting with a self-signed certificate, passing and receiving json to/from the server, getting the Flask server to listen on all interfaces). Hopefully this will help someone:

    String host = "turbo";
    int port = 8081;
    String command = "get_status";
    String user = "admin";
    String password = "admin";
    HttpsURLConnection connection = null;

    try {
        Certificate ca;
        String keyStoreType;
        KeyStore keyStore;
        String tmfAlgorithm;
        TrustManagerFactory tmf;

        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream ins = myContext.getResources().openRawResource(
                myContext.getResources().getIdentifier("server_crt", "raw",
                        myContext.getPackageName()));
        BufferedInputStream caInput = new BufferedInputStream(ins);
        ca = cf.generateCertificate(caInput);
        caInput.close();
        keyStoreType = KeyStore.getDefaultType();
        keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);
        tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, tmf.getTrustManagers(), null);
        URL url = new URL(String.format("https://%s:%d/test/%s", host, port, command));

        JSONObject params = new JSONObject();
        params.put("username", user);
        params.put("password", password);

        connection = (HttpsURLConnection) url.openConnection();
        connection.setSSLSocketFactory(context.getSocketFactory());
        connection.setReadTimeout(4500);
        connection.setConnectTimeout(4500);
        connection.setRequestMethod("POST");
        connection.setRequestProperty("User-Agent", "Mozilla/5.0");
        connection.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
        connection.setRequestProperty("Content-Type", "application/json");
        connection.setDoInput(true);
        connection.setDoOutput(true);
        OutputStreamWriter wr= new OutputStreamWriter(connection.getOutputStream());
        wr.write(params.toString());
        wr.flush();
        wr.close();

       switch (connection.getResponseCode()) {
           case HttpURLConnection.HTTP_CREATED:
           case HttpURLConnection.HTTP_OK:
               BufferedReader br = new BufferedReader(
                       new InputStreamReader(connection.getInputStream()));
               StringBuilder sb = new StringBuilder();
               String line;
               while ((line = br.readLine()) != null) {
                   sb.append(line+"\n");
               }
               br.close();;
               JSONObject jsonObj = new JSONObject(sb.toString());
        }
    } catch (Exception e) {
        Exception blah = e;
    } finally {
        if (connection != null) {
            connection.disconnect();
        }
    }
}

I had to change my Flask run() method to include the parameter:

host='0.0.0.0'

I created the create_key.cfg:

[req]
default_bits=2048
prompt=no
default_md=sha256
x509_extensions=v3_req
#req_extensions=req_ext
distinguished_name=dn

[dn]
C=US
ST=Florida
L=Satellite Beach
O=ThompCo, Inc
OU=turbo
emailAddress=Jordan@ThompCo.com
CN=turbo

[v3_req]
subjectAltName=@alt_names

[alt_names]
DNS.1=turbo
DNS.2=turbo.thompco.com
DNS.3=thompco.com

Then I ran the following commands:

openssl req -x509 -nodes -days 730 -newkey rsa:2048 -keyout server.key -out server.crt -config create_key.cfg -sha256

which creates the file:

server.crt  server.key
Cœur
  • 37,241
  • 25
  • 195
  • 267
jordanthompson
  • 888
  • 1
  • 12
  • 29