トップ 履歴 一覧 カテゴリ ソース 検索 ヘルプ RSS ログイン

ScrapCode/Java/Https

INDEX

SSL証明書の信頼判定変更

Javaで認証局に署名されていない証明書を利用するサイトに接続すると当然検証できないので例外が発生する。なので、SSLSocketFactory, HostnameVerifier のインスタンスを作成し信頼検証を行う。

信頼マネージャ

 特定の証明書を受け付ける信頼マネージャ

指定したキーストアファイルの証明書のみを信頼する。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;

import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
    /**
     * 特定の証明書を受け付ける信頼マネージャを作成します。
     *
     * @return 信頼マネージャ
     * @throws KeyManagementException 信頼マネージャの作成、キーストアの読み込みに失敗
     * @see TrustManager
     */
    public static TrustManager[] getTrustManagerAllowLoadCerts() throws KeyManagementException {
        final String storeFile = "keystore"; // KeyStore ファイル
        final String storePass = "keypass"; // KeyStore パスワード

        // 特定の証明書を受け付ける信頼マネージャ
        TrustManager[] tm = null;
        try {
            // キーストアのインスタンスを作成
            KeyStore ks = KeyStore.getInstance("JKS");
            // キーストアをロードする
            ks.load(new FileInputStream(storeFile), storePass.toCharArray());
            // 信頼マネージャのファクトリを作成
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            // 信頼マネージャのファクトリの初期化
            tmf.init(ks);
            // 信頼マネージャをセット
            tm = tmf.getTrustManagers();
        } catch (KeyStoreException e) {
            throw new KeyManagementException("SSL:KeyStoreException", e);
        } catch (NoSuchAlgorithmException e) {
            throw new KeyManagementException("SSL:NoSuchAlgorithmException", e);
        } catch (CertificateException e) {
            throw new KeyManagementException("SSL:CertificateException", e);
        // } catch (java.security.GeneralSecurityException e) {
        //  throw new KeyManagementException("SSL:GeneralSecurityException", e);
        } catch (FileNotFoundException e) {
            throw new KeyManagementException("SSL:FileNotFoundException", e);
        } catch (IOException e) {
            throw new KeyManagementException("SSL:IOException", e);
        }

        return tm;
    }

 全ての証明書を受け付ける信頼マネージャ

証明書の発行機関、有効期限の内容を無視する。

import java.security.cert.X509Certificate;

import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    /**
     * すべての証明書を受け付ける信頼マネージャを作成します
     *
     * @return 信頼マネージャ
     * @see TrustManager
     */
    public static TrustManager[] getTrustManagerAllowAllCerts() {
        // すべての証明書を受け付ける信頼マネージャ
        return new TrustManager[] { new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                /*
                 * 認証するピアについて信頼されている、証明書発行局の証明書の配列を返します。
                 */
                // System.out.println("DEBUG:getAcceptedIssuers");
                return null;
            }

            public void checkClientTrusted(X509Certificate[] certs, String authType) {
                /*
                 * ピアから提出された一部のまたは完全な証明書チェーンを使用して、
                 * 信頼できるルートへの証明書パスを構築し、認証タイプに基づいて
                 * クライアント認証を検証できるかどうか、信頼できるかどうかを返します。
                 */
                // System.out.println("DEBUG:checkClientTrusted authType=" + authType);
            }

            public void checkServerTrusted(X509Certificate[] certs, String authType) {
                /*
                 * ピアから提出された一部のまたは完全な証明書チェーンを使用して、
                 * 信頼できるルートへの証明書パスを構築し、認証タイプに基づいて
                 * サーバ認証を検証できるかどうか、また信頼できるかどうかを返します。
                 */
                // System.out.println("DEBUG:checkServerTrusted authType=" + authType);
            }
        } };
    }

鍵マネージャ

 クライアント認証用の鍵マネージャ

クライアント認証でサーバーに通知するクライアントの証明書。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
    /**
     * 鍵マネージャを作成します。
     *
     * @return 鍵マネージャ
     * @throws KeyManagementException 鍵マネージャの作成、キーストアの読み込みに失敗
     * @see KeyManager
     */
    public static KeyManager[] getKeyManagerLoadCerts() throws KeyManagementException {
        final String storeFile = "keystore"; // KeyStore ファイル
        final String storePass = "keypass"; // KeyStore パスワード
        final String keyPass = "keypass"; // Key パスワード

        // 認証用の鍵のロード
        KeyManager[] km = null;
        try {
            // キーストアのインスタンスを作成
            KeyStore ks = KeyStore.getInstance("JKS");
            // キーストアをロードする
            ks.load(new FileInputStream(storeFile), storePass.toCharArray());
            // 鍵マネージャのファクトリを作成
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            // 鍵マネージャのファクトリの初期化
            kmf.init(ks, keyPass.toCharArray());
            // 鍵マネージャをセット
            km = kmf.getKeyManagers();
        } catch (KeyStoreException e) {
            throw new KeyManagementException("SSL:KeyStoreException", e);
        } catch (NoSuchAlgorithmException e) {
            throw new KeyManagementException("SSL:NoSuchAlgorithmException", e);
        } catch (CertificateException e) {
            throw new KeyManagementException("SSL:CertificateException", e);
        } catch (UnrecoverableKeyException e) {
            throw new KeyManagementException("SSL:UnrecoverableKeyException", e);
        // } catch (java.security.GeneralSecurityException e) {
        //  throw new KeyManagementException("SSL:GeneralSecurityException", e);
        } catch (FileNotFoundException e) {
            throw new KeyManagementException("SSL:FileNotFoundException", e);
        } catch (IOException e) {
            throw new KeyManagementException("SSL:IOException", e);
        }

        return km;
    }

ホスト名検証

証明書とリクエスト先のホスト名が一致すれば問題ないが、異なる場合に通常は例外が発生する。

 すべてのホストを信用するホスト名検証

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    /**
     * すべてのホストを信用するホスト名検証メカニズムを作成します。
     *
     * @return ホスト名検証メカニズム
     */
    public static HostnameVerifier getHostnameVerifierAllowAllHosts() {
        return new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                // 証明書のURLと一致しない場合に飛んでくる
                // System.out.println("DEBUG:getHostnameVerifierAllowAllHosts#verify");
                // System.out.println("DEBUG:HostName=" + hostname);
                // すべてのホストを信用
                return true;
            }
        };
    }

HttpsURLConnection の変更

 デフォルトの変更

HostnameVerifier と SSLSocketFactory のデフォルトを変更し、以降のHTTPS通信での設定を変更する。

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
    /**
     * {@link HttpsURLConnection} のデフォルトを変更します。
     * HostnameVerifier と SSLSocketFactory のデフォルトを変更します。
     * クラスごと変更するので、以降のHTTPS通信はこちらが適応されます。
     *
     * @throws RuntimeException SSLContext の処理に失敗した
     * @see #getTrustManagerAllowAllCerts()
     * @see #getTrustManagerAllowLoadCerts()
     * @see #getHostnameVerifierAllowAllHosts()
     */
    public static void setHttpsURLConnectionDefaults() {
        SSLContext sc = null; // SSLContext
        HostnameVerifier hv = null; // HostnameVerifier

        // SSLContext 作成
        try {
            //System.out.println("DEBUG:SSLContext 作成");
            sc = SSLContext.getInstance("SSL");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SSLContext#getInstance: NoSuchAlgorithmException", e);
        }

        // SSLContext 初期化
        try {
            //System.out.println("DEBUG:SSLContext 初期化");
            sc.init(null, getTrustManagerAllowAllCerts(), null);
        } catch (KeyManagementException e) {
            throw new RuntimeException("SSLContext#init: KeyManagementException", e);
        }

        // HTTPSで使用する SSLソケットの作成(SSLSocketFactory) をセット
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        // HostnameVerifier を作成
        hv = getHostnameVerifierAllowAllHosts();

        // HTTPSで使用する ホスト名の検証(HostnameVerifier) をセット
        // デフォルトの検証が失敗した場合(証明書の氏名[CN]が異なる場合など)に呼び出される
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
    }

 接続ごとに設定

HttpsURLConnection インスタンスごとに設定する場合は、接続に使用する HttpsURLConnection インスタンスに setSSLSocketFactory および setHostnameVerifier で設定する。

HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory( sslSocketFactory );
conn.setHostnameVerifier( hostnameVerifier );

参考

最終更新時間:2009年10月24日 14時11分59秒 指摘や意見などあればSandBoxのBBSへ。