简体   繁体   中英

Client can't connect to REST API using self-signed certificate

I'm trying to connect my Android client to TOMCAT REST API. Everything works fine as long as HTTP is used. Unfortunately HTTPS has to be implemented.

After server side HTTPS implementation server looks fine (server is using self-signed certificate. Created using JAVA KEYTOOL).

Unfortunately I can't connect Android client due to some reason.

At first the error was: "Trust anchor for certification path not found". I tried use Android Developer guides but I had problem with access to .crt / cer file using "AsyncTask". So I decided that "ALLOW_ALL_HOSTNAME_VERIFIER" will work for the moment. HTTPS CLIENT class look like this:

(commented part is HTTP version)

import android.util.Base64;
import android.os.Build;
import android.annotation.TargetApi;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.SingleClientConnManager;

import android.os.AsyncTask;
import android.util.Log;

public class UrlUtil extends AsyncTask<String, Void, String> {

    @TargetApi(Build.VERSION_CODES.O_MR1)
    @Override
    protected String doInBackground(String... strings) {

        StringBuilder jsonResult = new StringBuilder();
        String message = null;

        try {
            //setup SSL
            HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
            DefaultHttpClient client = new DefaultHttpClient();
            SchemeRegistry registry = new SchemeRegistry();
            SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
            socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
            registry.register(new Scheme("https", socketFactory, 443));
            SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry);
            DefaultHttpClient httpClient = new DefaultHttpClient(mgr, client.getParams());
            // Set verifier
            HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);

            String cred = "xxxx:yyyy";
            String encoding = Base64.encodeToString(cred.getBytes(), Base64.DEFAULT);
            // Example send http request
            HttpPost httpPost = new HttpPost(strings[0]);
            httpPost.setHeader("Authorization", "Basic " + encoding);
            HttpResponse response = httpClient.execute(httpPost);
            InputStream is = response.getEntity().getContent(); //outputs an inputstream

            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();
            String line = null;

            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }

            is.close();
            Log.d("httpstest",sb.toString());
            return sb.toString();

        } catch (IOException e) {  }
        return null;

//            try {
//                URL url = new URL(strings[0]);
//
//                Authenticator.setDefault(new Authenticator() {
//                    protected PasswordAuthentication getPasswordAuthentication() {
//                        return new PasswordAuthentication("user", "userPass".toCharArray());
//                    }
//                });
//                HttpURLConnection connection = (HttpURLConnection) new URL(url.toString()).openConnection();
//                connection.setUseCaches(false);
//                connection.connect();
//                InputStream inputStream = connection.getInputStream();
//                InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
//                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
//                message = org.apache.commons.io.IOUtils.toString(bufferedReader);
//
//            } catch (MalformedURLException e) {
//                e.printStackTrace();
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//
//
//
//        } catch (Exception e) {  }
//
//        return message;
    }
}

Error info:

07/09 18:27:29: Launching app
$ adb install-multiple -r -t -p org.pilat.eko_raporty20 C:\Users\Pilat\Desktop\AndroidStudioProjects\ekoraporty20\app\build\intermediates\split-apk\debug\slices\slice_9.apk 
Split APKs installed in 2 s 675 ms
$ adb shell am start -n "org.pilat.eko_raporty20/org.pilat.eko_raporty20.activity.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Client not ready yet..Waiting for process to come online
Connected to process 944 on device lenovo-lenovo_p2a42-ZY223TJMN2
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
D/LenovoAppIconTheme: ExtraResources;cleanCachedIcon;clear cache..
W/ActivityThread: Application org.pilat.eko_raporty20 can be debugged on port 8100...
W/System: ClassLoader referenced unknown path: /data/app/org.pilat.eko_raporty20-1/lib/arm64
I/InstantRun: starting instant run server: is main process
I/Typeface: setThemeFont(): sThemeFontPath = ,fontPath = 
W/Typeface: setThemeFont(): FontPath Not Changed!
W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
V/BoostFramework: mAcquireFunc method = public int com.qualcomm.qti.Performance.perfLockAcquire(int,int[])
V/BoostFramework: mReleaseFunc method = public int com.qualcomm.qti.Performance.perfLockRelease()
    mAcquireTouchFunc method = public int com.qualcomm.qti.Performance.perfLockAcquireTouch(android.view.MotionEvent,android.util.DisplayMetrics,int,int[])
    mIOPStart method = public int com.qualcomm.qti.Performance.perfIOPrefetchStart(int,java.lang.String)
    mIOPStop method = public int com.qualcomm.qti.Performance.perfIOPrefetchStop()
V/BoostFramework: BoostFramework() : mPerf = com.qualcomm.qti.Performance@e5acceb
    BoostFramework() : mPerf = com.qualcomm.qti.Performance@fdb0a48
D/NetworkSecurityConfig: No Network Security Config specified, using platform default
I/DpmTcmClient: RegisterTcmMonitor from: org.apache.http.impl.conn.TcmIdleTimerMonitor
I/System.out: [socket][0] connection /"ip":"port";LocalPort=-1(0)
I/System.out: [socket][/100.82.169.168:35907] connected
I/System.out: close [socket][/100.82.169.168:35907]
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: org.pilat.eko_raporty20, PID: 944
    java.lang.RuntimeException: Unable to start activity ComponentInfo{org.pilat.eko_raporty20/org.pilat.eko_raporty20.activity.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.String.toString()' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2659)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2720)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1466)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6111)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.String.toString()' on a null object reference
        at org.pilat.eko_raporty20.activity.MainActivity.onCreate(MainActivity.java:126)
        at android.app.Activity.performCreate(Activity.java:6734)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2612)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2720) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1466) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6111) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755) 
Application terminated.
  1. Why I get no data from server?

    (I don't understend: I/System.out: [socket][0] connection /"ip":"port";LocalPort=-1(0))

  2. How to do it the right way?

    The preferred option is to make it work using self-signed cert if there is no other option it can work accepting all certs.

Thanks in advance :-)

Result of two days of internet searching and trying many options is to use Android Developer option: https://developer.android.com/training/articles/security-ssl Than you will have problem with adding cert file. Android will not see it. You can add certificate by adding new constructor to your AsyncTask class. It should look something like this:

    Public class UrlUtil extends AsyncTask<String, String, String> {

        private Context context;

        public UrlUtil (Context myContext) {
            this.context = myContext;
        }

        public UrlUtil () {};

        @TargetApi(Build.VERSION_CODES.O_MR1)
        @Override
        protected String doInBackground(String... params) {...}

...}

You execute AsyncTask like this:

new UrlUtil(this).execute("https://myserver:myport/restofpath").get()

Where "this" is ACTIVITY context.

Certificate file should be placed in ASSETS folder. If you don't have one in your project the easiest way to add one is to right click on your project NEW / FOLDER / ASSETS FOLDER. To add file to ASSETS FOLDER you can't simply use Drag&Drop. You have to find folder in your OS explorer and place it into the folder.

I hope you will find it useful :-)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM