web-dev-qa-db-ja.com

RetrofitでSSL証明書を使用してhttpsリクエストを行う方法

.p12証明書ファイルがあり、 SSL Converter を使用して.pem証明書ファイルに変換します。次に、このAndroidコードでこのpem証明書ファイルを次のように使用します。

OkHttpClient okHttpClient = new OkHttpClient();
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            InputStream instream = context.getResources().openRawResource(R.raw.pem_certificate);
            Certificate ca;
            ca = cf.generateCertificate(instream);
            KeyStore kStore = KeyStore.getInstance(KeyStore.getDefaultType());
            kStore.load(null, null);
            kStore.setCertificateEntry("ca", ca);
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(kStore);
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, tmf.getTrustManagers(), null);
            okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
        } catch (CertificateException
                | KeyStoreException
                | NoSuchAlgorithmException
                | IOException
                | KeyManagementException e) {
            e.printStackTrace();
        }

        baseURL = endpoint;
        RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(baseURL)
                .setClient(new OkClient(okHttpClient))
                .build();

        service = restAdapter.create(DishService.class);

しかし、このコードは機能しません。 「ca = cf.generateCertificate(instream);」という行で失敗しました。 CertificateExceptionメッセージ付き。

12
congtrungvnit
public class RetrofitBuilder {

private static Retrofit retrofit = null;
private static final String BASE_URL = BuildConfig.BASE_URL;
private static final String API_VERSION = BuildConfig.VERSION;

private static OkHttpClient.Builder httpClientBuilder = null;

public static Retrofit getInstance(Context context) {
    if (retrofit == null) {

        httpClientBuilder = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS);
        initHttpLogging();
        initSSL(context);

        Retrofit.Builder builder = new Retrofit.Builder()
                .baseUrl(BASE_URL + API_VERSION)
                .addConverterFactory(GsonConverterFactory.create())
                .client(httpClientBuilder.build());


        retrofit = builder.build();

    }
    return retrofit;
}


private static void initHttpLogging() {
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);
    if (BuildConfig.DEBUG) httpClientBuilder.addInterceptor(logging);
}

private static void initSSL(Context context) {

    SSLContext sslContext = null;
    try {
        sslContext = createCertificate(context.getResources().openRawResource(R.raw.cert));
    } catch (CertificateException | IOException | KeyStoreException | KeyManagementException | NoSuchAlgorithmException e) {
        e.printStackTrace();
    }

    if(sslContext!=null){
        httpClientBuilder.sslSocketFactory(sslContext.getSocketFactory(), systemDefaultTrustManager());
    }

}

private static SSLContext createCertificate(InputStream trustedCertificateIS) throws CertificateException, IOException, KeyStoreException, KeyManagementException, NoSuchAlgorithmException{

    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    Certificate ca;
    try {
        ca = cf.generateCertificate(trustedCertificateIS);
    } finally {
        trustedCertificateIS.close();
    }

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

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

    // creating an SSLSocketFactory that uses our TrustManager
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, tmf.getTrustManagers(), null);
    return sslContext;

}

private static X509TrustManager systemDefaultTrustManager() {

    try {
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init((KeyStore) null);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
            throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
        }
        return (X509TrustManager) trustManagers[0];
    } catch (GeneralSecurityException e) {
        throw new AssertionError(); // The system has no TLS. Just give up.
    }

}

}

たくさんの投稿、ブログ、Gistを読んだ後、ようやく方法を見つけました。これは私にとってはうまくいきます。

4
Jay

多分あなたはR.raw.pem_certificateに問題があります...

1)opensslを使用してサーバーから生のパブリック証明書を取得しようとします:openssl s_client -connect {HOSTNAME}:{PORT} -showcerts

(詳細はこちらをご覧ください: https://superuser.com/questions/97201/how-to-save-a-remote-server-ssl-certificate-locally-as-a-file

2)カスタムSSL証明書を使用してRetrofit2をセットアップする方法 https://adiyatmubarak.wordpress.com/tag/add-ssl-certificate-in-retrofit-2/

またはRetrofit1: https://number1.co.za/use-retrofit-self-signed-unknown-ssl-certificate-Android/

PS:それは私のために働きます、PEMファイルをBKSに変換しないでください。

3
Maxim Firsoff

Jayからの回答で始めましたが、次のエラーが発生しました: 認証パスの信頼アンカーが見つかりません

KeyToolコマンド を使用して、持っていたp12証明書をキーストアにインポートできました。

すなわち:

手順1.デフォルトの証明書を使用して新しいキーストアを生成し、それを削除する

keytool -genkey -alias mycert -keyalg RSA -keysize 2048 -keystore mykeystore
keytool -delete -alias mycert -keystore mykeystore
keytool -v -list -keystore mykeystore

ステップ2.証明書をインポートする

keytool -v -importkeystore -srckeystore <path to certificate> -srcstoretype PKCS12 -destkeystore mykeystore -deststoretype PKCS12
keytool -v -list -keystore mykeystore

ステップ3:次に、次のコードを使用してSSLContextをロードします。

    // open the keystore
    KeyStore keyStore = KeyStore.getInstance(type);
    try {
        keyStore.load(keystoreInputStream, password.toCharArray());
    } finally {
        try {
            keystoreInputStream.close();
        } catch (IOException e) {
            // swallow this
        }
    }

    // create and initialise a key manager factory
    KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
    if (password != null && !password.trim().equals("")) {
        kmf.init(keyStore, password.toCharArray());
    } else {
        kmf.init(keyStore, null);
    }

    // build an SSL context
    KeyManager[] keyManagers = kmf.getKeyManagers();
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(keyManagers, null, null);

ここにあるコードのおかげで: https://chariotsolutions.com/blog/post/https-with-client-certificates-on/

0
Dagmar

Step1:src/main/res/raw/client_certificate.cerのようにrawディレクトリ内にセキュリティ証明書を配置します

Step2:この証明書を使用してSSLContextオブジェクトを作成します。

SSLContext sslContext = createCertificate(getApplicationContext().getResources().openRawResource(R.raw.client_certificate));

Step3:作成したSSLContextオブジェクトをOkHttpClientビルダーに追加します。

addClientCertificate(okHttpClientBuilder);

Step4: OkHttpClientビルダーをクライアントのように改造するように設定します。

retrofitBuilder.client(okHttpClientBuilder.build());

次に、RetrofitManager.Javaファイルでの上記のすべてのステップの完全な実装を示します。

RetrofitManager.Java

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import Java.io.IOException;
import Java.io.InputStream;
import Java.security.GeneralSecurityException;
import Java.security.KeyManagementException;
import Java.security.KeyStore;
import Java.security.KeyStoreException;
import Java.security.NoSuchAlgorithmException;
import Java.security.cert.CertificateException;
import Java.security.cert.CertificateFactory;
import Java.security.cert.X509Certificate;
import Java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import Java.security.cert.Certificate;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * Created by Rajoo Kannaujiya on 02/16/2020.
 */

public class RetrofitManager {
    private static final String URL = "https://stackoverflow.com"; // Fetch url from config file.
    private static final String TLS = "TLS";
    private static final String SSL = "SSL";
    private static final String CA = "ca";
    private static final String X_509 = "X.509";
    private static final String PROD = "prod";
    private static X509TrustManager trustManager = null;
    private static final String CONTENT_TYPE = "Content-Type";
    private static final String CONTENT_TYPE_VALUE = "application/json";
    public static final String BUILD_TYPE = "prod"; // Fetch build type from config file (Whether it is a dev, qa, stage or prod build).

    public static Retrofit getRetrofitForAPIUrl() {
        return getRetrofit(URL);
    }

    private static Retrofit getRetrofit(String url) {
        Gson gson = new GsonBuilder().setLenient().create();

        Retrofit.Builder retrofitBuilder = new Retrofit.Builder()
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(gson))
                .baseUrl(url);

        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.level(HttpLoggingInterceptor.Level.BODY);

        OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
        okHttpClientBuilder.addInterceptor(chain -> {
            Request origReq = chain.request();
            Request.Builder requestBuilder = origReq.newBuilder()
                    .addHeader(CONTENT_TYPE, CONTENT_TYPE_VALUE);

            Request request = requestBuilder.build();
            return chain.proceed(request);
        });

        addClientCertificate(okHttpClientBuilder);

        OkHttpClient.Builder okHttpBuilder = okHttpClientBuilder
                .connectTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(20, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .addInterceptor(logging);
        retrofitBuilder.client(okHttpBuilder.build());

        return retrofitBuilder.build();
    }

    /**
     * Method to create SSLContext object for the client certificate and return it.
     *
     * @param clientCertificate
     * @return SSLContext Object
     */
    private static SSLContext getSSLContextForClientCertificate(InputStream clientCertificate) {
        SSLContext sslContext = null;
        // If given client certificate is for production environment only, Then check put check for build type.
        if (PROD.contentEquals(BUILD_TYPE)) {
            // Creating X.509 certificate factory instance
            CertificateFactory cf = null;
            try {
                cf = CertificateFactory.getInstance(X_509);
            } catch (CertificateException e) {
                e.printStackTrace();
            }
            // Generating client certificate
            Certificate ca = null;
            try {
                try {
                    if (cf != null) {
                        ca = cf.generateCertificate(clientCertificate);
                    }
                } catch (CertificateException e) {
                    e.printStackTrace();
                }
            } finally {
                try {
                    clientCertificate.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            // Creating a KeyStore containing our trusted CAs
            String keyStoreType = KeyStore.getDefaultType();
            KeyStore keyStore = null;
            try {
                keyStore = KeyStore.getInstance(keyStoreType);
            } catch (KeyStoreException e) {
                e.printStackTrace();
            }
            if (keyStore != null) {
                try {
                    keyStore.load(null, null);
                } catch (CertificateException | IOException | NoSuchAlgorithmException e) {
                    e.printStackTrace();
                }
                try {
                    keyStore.setCertificateEntry(CA, ca);
                } catch (KeyStoreException e) {
                    e.printStackTrace();
                }
            }
            // Creating a TrustManager that trusts the CAs in our KeyStore
            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = null;
            try {
                tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            try {
                if (tmf != null) {
                    tmf.init(keyStore);
                }
            } catch (KeyStoreException e) {
                e.printStackTrace();
            }
            // Creating an SSLContext instance that uses our TrustManager
            try {
                sslContext = SSLContext.getInstance(TLS);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            try {
                if (sslContext != null && tmf != null) {
                    sslContext.init(null, tmf.getTrustManagers(), null);
                }
            } catch (KeyManagementException e) {
                e.printStackTrace();
            }
        } else { // For non prod environment build. This section is useful only if you don't have client certificate for lower environment.
            try {
                sslContext = SSLContext.getInstance(SSL);
                sslContext.init(null, new TrustManager[]{getTrustManager()}, new Java.security.SecureRandom());
            } catch (NoSuchAlgorithmException | KeyManagementException e) {
                e.printStackTrace();
            }
        }
        trustManager = getTrustManager();
        return sslContext;
    }

    /**
     * Method to add client certificate to OkHttpClient.Builder object.
     *
     * @param okHttpClientBuilder
     * @return void
     */
    private static void addClientCertificate(OkHttpClient.Builder okHttpClientBuilder) {
        SSLContext sslContext = getSSLContextForClientCertificate(getApplicationContext().getResources().openRawResource(R.raw.myaccount_coxbusiness_com));
        if (sslContext != null) {
            okHttpClientBuilder.sslSocketFactory(sslContext.getSocketFactory(), trustManager);
            okHttpClientBuilder.hostnameVerifier((hostname, session) -> true);
        }
    }

    private static X509TrustManager getTrustManager() {
        X509TrustManager trustManager = null;
        if (PROD.contentEquals(BUILD_TYPE)) { // If client certificate is only available for prod environment.
            try {
                TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                trustManagerFactory.init((KeyStore) null);
                TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
                if (trustManagers[0] instanceof X509TrustManager) {
                    trustManager = (X509TrustManager) trustManagers[0];
                }
            } catch (GeneralSecurityException e) {
                throw new AssertionError();
            }
        } else { // For non prod environment build. This section is useful only if you don't have client certificate for lower environment.
            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];
                }
            };
        }
        return trustManager;
    }
}
0

それが私のために働いた以下のリンクで答えを試してください

レトロフィット2.3.0

自己署名SSL証明書の変更

0
Gowsik K C