2

I have a Spring Boot API that runs locally, with a self-signed certificate, using the HTTPS protocol. Obviously, when I send GET Requests from the browser, I receive the io.netty.handler.codec.DecoderException: javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown error on the server side, which is normal, because the self-signed is not trusted by the browser. Postman works just fine for GET and POST.

However, I want to send GET requests from an Android client to the Spring API but, even I've used a function to allow all SSL traffic (yes, I know it's not recommended), I still can't send requests to the API, receiving the following output:

I/STATUS: 405
I/MSG: Method Not Allowed

I thought my allowAllSSL() function (HttpsTrustManager class) would solve the issue, because if I remove the function call, I receive the following error, which seems to match the one on the server side:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
    at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:239)

Now, you may think that the GET request is not implemented correctly in Spring, but it's not true, since the same GET request works just fine from Postman. I believe that the problem is still linked to the certificate, but I can't figure out what do I need to change. Here is my code:

Spring BOOT Rest Controller

@RestController
@RequestMapping("/post")


public class PostRequest {
    @GetMapping("")
    public String string(@RequestBody ImageRequest newEmployee){
....

The ImageRequest class contains just three private String members.

HttpsTrustManager class (to allow all SSL)

package com.example.androidclient;

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

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

public class HttpsTrustManager implements X509TrustManager {
    private static TrustManager[] trustManagers;
    private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};

    @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;
    }

    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 | KeyManagementException e) {
            e.printStackTrace();
        }

        HttpsURLConnection.setDefaultSSLSocketFactory(context != null ? context.getSocketFactory() : null);
    }
}

Android Request

        HttpsTrustManager.allowAllSSL();
        URL url = new URL("https://192.168.1.106:8443/post");
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
        conn.setRequestProperty("Accept", "application/json");
        conn.setDoOutput(true);
        conn.setDoInput(true);

        JSONObject jsonParam = new JSONObject();
        jsonParam.put("location", "Somewhere");
        jsonParam.put("date", "22.05.2020");
        jsonParam.put("imageBytes", strings[0]);


        Log.i("JSON", jsonParam.toString());
        DataOutputStream os = new DataOutputStream(conn.getOutputStream());
        //os.writeBytes(URLEncoder.encode(jsonParam.toString(), "UTF-8"));
        os.writeBytes(jsonParam.toString());

        os.flush();
        os.close();

        Log.i("STATUS", String.valueOf(conn.getResponseCode()));
        Log.i("MSG", conn.getResponseMessage());

        conn.disconnect();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return "ok";
}

2 Answers 2

0

Use this function in your android application.

Please note this will allow all ssl certificates without verification. I would encourage you to follow the recommended approach when dealing with self-signed certificates outlined here: https://developer.android.com/training/articles/security-ssl#java

// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}

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

// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
Sign up to request clarification or add additional context in comments.

1 Comment

Sorry, but your method leads to the following exception: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. I think the method that I've used for by-passing the SSL verification is better. Also, this is also working.
0

I have found the solution on my own. Apparently, the Connection.setDoOutput(true) method is working just for POST and PUT requests, but not for GET.

Thus, I have changed my RequestMapping to work on POST, like, this:

@RequestMapping(
    value = "/post",
    produces = "application/json",
    method = RequestMethod.POST)

Now I receive 200 OK.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.