web-dev-qa-db-ja.com

TrustManagerFactoryを複数の信頼ソースで初期化するにはどうすればよいですか?

私のアプリケーションには、ローカルネットワークで使用するための信頼できる自己署名証明書を含む個人用キーストアがあります-たとえばmykeystore.jks。ローカルでプロビジョニングされた自己署名証明書を使用して、パブリックサイト(たとえばgoogle.com)とローカルネットワーク内のサイトに接続できるようにしたいと考えています。

ここでの問題は、 https://google.com に接続すると、パスの構築が失敗することです。これは、独自のキーストアを設定すると、JREにバンドルされているルートCAを含むデフォルトのキーストアが上書きされ、例外が報告されるためです。

Sun.security.validator.ValidatorException: PKIX path building failed: Sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

ただし、CA証明書を自分のキーストアにインポートすると(mykeystore.jks)正常に動作します。両方をサポートする方法はありますか?

私はこの目的のために私自身のTrustMangerを持っています、

public class CustomX509TrustManager implements X509TrustManager {

        X509TrustManager defaultTrustManager;

        public MyX509TrustManager(KeyStore keystore) {
                TrustManagerFactory trustMgrFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                trustMgrFactory.init(keystore);
                TrustManager trustManagers[] = trustMgrFactory.getTrustManagers();
                for (int i = 0; i < trustManagers.length; i++) {
                    if (trustManagers[i] instanceof X509TrustManager) {
                        defaultTrustManager = (X509TrustManager) trustManagers[i];
                        return;
                    }
                }

        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            try {
                defaultTrustManager.checkServerTrusted(chain, authType);
            } catch (CertificateException ce) {
            /* Handle untrusted certificates */
            }
        }
    }

次に、SSLContextを初期化します。

TrustManager[] trustManagers =
            new TrustManager[] { new CustomX509TrustManager(keystore) };
SSLContext customSSLContext =
        SSLContext.getInstance("TLS");
customSSLContext.init(null, trustManagers, null);

ソケットファクトリを設定し、

HttpsURLConnection.setDefaultSSLSocketFactory(customSSLContext.getSocketFactory());

メインプログラム、

URL targetServer = new URL(url);
HttpsURLConnection conn = (HttpsURLConnection) targetServer.openConnection();

独自のトラストマネージャーを設定しない場合は、 https://google.com に接続します。デフォルトのキーストアを指す「デフォルトの信頼マネージャー」を取得するにはどうすればよいですか?

9
varrunr

trustMgrFactory.init(keystore);では、システムのデフォルトのキーストアではなく、独自の個人用キーストアを使用してdefaultTrustManagerを構成しています。

Sun.security.ssl.TrustManagerFactoryImplのソースコードを読むと、trustMgrFactory.init((KeyStore) null);は必要なことを正確に実行するように見えます(システムのデフォルトのキーストアをロードします)。クイックテストに基づくと、次のように機能するようです。私。

16
Pasi

答え ここ は私がこれを行う方法を理解するようになった方法です。システムCA証明書と証明書のカスタムキーストアを受け入れるだけの場合は、いくつかの便利なメソッドを使用して単一のクラスに簡略化しました。ここで利用可能な完全なコード:

https://Gist.github.com/HughJeffner/6eac419b18c6001aeadb

KeyStore keystore; // Get your own keystore here
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] tm = CompositeX509TrustManager.getTrustManagers(keystore);
sslContext.init(null, tm, null);
9
Hugh Jeffner

CommonsHttpClientで同じ問題が発生しました。私の場合の実用的な解決策は、次の方法でPKIXTrustManagerの委任チェーンを作成することでした。

public class TrustManagerDelegate implements X509TrustManager {
    private final X509TrustManager mainTrustManager;
    private final X509TrustManager trustManager;
    private final TrustStrategy trustStrategy;

    public TrustManagerDelegate(X509TrustManager mainTrustManager, X509TrustManager trustManager, TrustStrategy trustStrategy) {
        this.mainTrustManager = mainTrustManager;
        this.trustManager = trustManager;
        this.trustStrategy = trustStrategy;
    }

    @Override
    public void checkClientTrusted(
            final X509Certificate[] chain, final String authType) throws CertificateException {
        this.trustManager.checkClientTrusted(chain, authType);
    }

    @Override
    public void checkServerTrusted(
            final X509Certificate[] chain, final String authType) throws CertificateException {
        if (!this.trustStrategy.isTrusted(chain, authType)) {
            try {
                mainTrustManager.checkServerTrusted(chain, authType);
            } catch (CertificateException ex) {
                this.trustManager.checkServerTrusted(chain, authType);
            }
        }
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return this.trustManager.getAcceptedIssuers();
    }

}

そして、次の方法でHttpClientを初期化します(はい、それは醜いです):

final SSLContext sslContext;
try {
    sslContext = SSLContext.getInstance("TLS");
    final TrustManagerFactory javaDefaultTrustManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    javaDefaultTrustManager.init((KeyStore)null);
    final TrustManagerFactory customCaTrustManager = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    customCaTrustManager.init(getKeyStore());

    sslContext.init(
        null,
        new TrustManager[]{
            new TrustManagerDelegate(
                    (X509TrustManager)customCaTrustManager.getTrustManagers()[0],
                    (X509TrustManager)javaDefaultTrustManager.getTrustManagers()[0],
                    new TrustSelfSignedStrategy()
            )
        },
        secureRandom
    );

} catch (final NoSuchAlgorithmException ex) {
    throw new SSLInitializationException(ex.getMessage(), ex);
} catch (final KeyManagementException ex) {
    throw new SSLInitializationException(ex.getMessage(), ex);
}

SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext);

PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(
        RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build()
);
//maximum parallel requests is 500
cm.setMaxTotal(500);
cm.setDefaultMaxPerRoute(500);

CredentialsProvider cp = new BasicCredentialsProvider();
cp.setCredentials(
        new AuthScope(apiSettings.getIdcApiUrl(), 443),
        new UsernamePasswordCredentials(apiSettings.getAgencyId(), apiSettings.getAgencyPassword())
);

client = HttpClients.custom()
                    .setConnectionManager(cm)
                    .build();

単純なHttpsURLConnectionを使用する場合は、委任クラスの簡略化されたバージョンでうまくいく可能性があります。

public class TrustManagerDelegate implements X509TrustManager {
    private final X509TrustManager mainTrustManager;
    private final X509TrustManager trustManager;

    public TrustManagerDelegate(X509TrustManager mainTrustManager, X509TrustManager trustManager) {
        this.mainTrustManager = mainTrustManager;
        this.trustManager = trustManager;
    }

    @Override
    public void checkClientTrusted(
            final X509Certificate[] chain, final String authType) throws CertificateException {
        this.trustManager.checkClientTrusted(chain, authType);
    }

    @Override
    public void checkServerTrusted(
            final X509Certificate[] chain, final String authType) throws CertificateException {
        try {
            mainTrustManager.checkServerTrusted(chain, authType);
        } catch (CertificateException ex) {
            this.trustManager.checkServerTrusted(chain, authType);
        }
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return this.trustManager.getAcceptedIssuers();
    }

}
2
Novoj

Android開発者の場合、これははるかに簡単です。要約すると、xmlresファイルを追加してカスタム証明書を構成できます。

ステップ1:マニフェストxmlを開き、属性を追加します。

<manifest ... >
    <application Android:networkSecurityConfig="@xml/network_security_config"
                    ... >
        ...
    </application>
</manifest>

手順2:network_security_config.xmlをres/xmlに追加し、必要に応じて証明書を構成します。

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="@raw/extracas"/>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>
</network-security-config>

注:このxmlは他の多くの使用法をサポートでき、このソリューションはapi24 +でのみ機能します。

公式リファレンス: ここ

0
KFJK