Как правильно установить соединение https с самозаверяющим сертификатом в Android? - PullRequest
0 голосов
/ 12 сентября 2018

Я видел почти все вопросы, связанные с этой темой, о переполнении стека, но не смог найти решение.

Согласно документации , мы можем использовать самозаверяющий сертификат следующим образом:

// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(new FileInputStream("mycert.crt"));
Certificate ca;
try {
    ca = cf.generateCertificate(caInput);
    System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} 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);

// Tell the URLConnection to use a SocketFactory from our SSLContext
URL url = new URL("https://ip/address");
HttpsURLConnection urlConnection =
    (HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);

Но это вызовет исключение java.security.cert.CertPathValidatorException:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: доверять привязке дляпуть сертификации не найден.

Также пробовал это:

InputStream keystoreInput = context.getResources().openRawResource(R.raw.cert_keystore);

KeyStore keyStore = KeyStore.getInstance("bks");
keyStore.load(keystoreInput, "pass".toCharArray());

KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(keyStore, "pass".toCharArray());

TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(keyStore);

SSLContext context= SSLContext.getInstance("TLS");


context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);


URL url = new URL("https://ip/address");
HttpsURLConnection urlConnection =
    (HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();

Но это вызовет ту же ошибку.

Также я уверен, что сертификатдействителен, потому что я могу опубликовать запрос следующим образом:

curl -v -s -k --key mykey.key--cert mycert.crt -X GET "https://ip/path"

Как правильно реализовать соединение https с самозаверяющим сертификатом в Android (без дополнительных библиотек, просто используя HttpsUrlConnection)?

Примечание. Я не хочу использовать какую-либо библиотеку, включая OkHttp, Apache и т. Д.

ОБНОВЛЕНИЕ

Наконец-то найдено решение, вот основнаячасть:

InputStream keystoreInput = context.getResources().openRawResource(R.raw.cert_keystore);

KeyStore keyStore = KeyStore.getInstance("bks");
keyStore.load(keystoreInput, "pass".toCharArray());

KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(keyStore, "pass".toCharArray());


SSLContext context= SSLContext.getInstance("TLS");


context.init(kmf.getKeyManagers(),  new TrustManager[] {new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}

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

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


URL url = new URL("https://ip/address");
HttpsURLConnection urlConnection =
    (HttpsURLConnection)url.openConnection();

urlConnection.setSSLSocketFactory(context.getSocketFactory());

// skip hostname security check
urlConnection.setHostnameVerifier(new AllowAllHostnameVerifier());
InputStream in = urlConnection.getInputStream();

Но я не уверен, существует ли более безопасный способ реализации HTTPS-соединения с самозаверяющим сертификатом.

"A TrustManager - это то, что система использует для проверки сертификатов с сервера и - путем создания одного из KeyStore с одним или несколькими CA - это будут единственные CA, которым доверяет TrustManager«. doc

Следовательно, нам нужно реализовать TrustManager, но как мы должны это сделать?Любое предложение поможет.

Спасибо

1 Ответ

0 голосов
/ 12 сентября 2018

в вашем классе WebServiceHelper используйте эти методы:

public class WebServiceHelper {
public static final String ENDPOINT = "your address";
private static WebServiceHelper instance;
private Services service;
private Context mContext;

private OkHttpClient getMyOkHttpClient(String serverUrl) {
    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    if (BuildConfig.DEBUG) {
        builder.interceptors().add((new HttpLoggingInterceptor()).setLevel(HttpLoggingInterceptor.Level.BODY));
    } else {
        builder.interceptors().add((new HttpLoggingInterceptor()).setLevel(HttpLoggingInterceptor.Level.NONE));
    }
    builder.cookieJar(new JavaNetCookieJar(new CookieManager()))
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(120, TimeUnit.SECONDS);

    if ((serverUrl.toLowerCase()).contains("https")) initUnSafeSSL(builder);
    return builder.build();
}

private void initUnSafeSSL(OkHttpClient.Builder builder1) {
    try {
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return new java.security.cert.X509Certificate[]{};
                    }
                }
        };
        final SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
        final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        builder1.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
        builder1.hostnameVerifier((hostname, session) -> true);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

private WebServiceHelper(Context context) {
    mContext = context;
    Retrofit retrofit = createAdapter(ENDPOINT).build();
    service = retrofit.create(Services.class);
}

public static WebServiceHelper getInstance(Context context) {
    if (instance == null) {
        instance = new WebServiceHelper(context);
    }
    return instance;
}

private Retrofit.Builder createAdapter(String serverChoice) {
    return new Retrofit.Builder()
            .baseUrl(serverChoice)
            .client(getMyOkHttpClient(serverChoice))
            .addConverterFactory(GsonConverterFactory.create());
}
...