* Javaでhttps通信時の証明書検証について [#lcdff1a8]

#setlinebreak(on)

#contents
-- 関連
--- [[Apache+openSSLでクライアント認証]]
--- [[クライアント認証付きのHTTPSリクエスト発行]]
--- [[ApacheでSSL(SNI)設定]]

** JavaからHTTP接続する際に行われる検証 [#f7d537f7]

#html(<div style="padding-left:20px;">)

https接続する際には、以下の2つの検証が行われる。

(1) 証明書/認証局の検証 ・・・ 有効な証明書か。(信頼できる認証局によって署名された証明書か)
(2) サーバ名の検証 ・・・ 証明書に書かれているホスト名と、アクセス先のホスト名が一致しているか。

#html(</div>)

** 検証を通過させる為のコード [#k8b602f5]

テスト環境等で、まっとうな証明書がインストールされたサーバがない場合、以下のように証明書の検証をスルーする事ができる。
ただし、この方法は結構な注意換気が行われているので、&color(red){本番リリースに含めるのは控えた方が良い。};(Androidアプリ等は特に)


以下は、Android関連の注意換気、ガイド等
https://www.ipa.go.jp/about/press/20140919_1.html
http://www.slideshare.net/jpcert_securecoding/cert-verif-javadaytokyo2015
https://www.jssec.org/dl/android_securecoding.pdf

&br;

#mycode2(){{

    /**
     * HTTPS通信
     */
    private void sendHttpsRequest(String uri, String method, Map param) {

            URL urlObj = new URL(uri);
            HttpURLConnection http = (HttpsURLConnection) urlObj.openConnection();

            // デフォルトのホスト名検証ルールを設定
            setDefaultHostnameVerifier();

            // 証明書情報を取得
            TrustManager[] tm = getTrustManager();
            SSLContext sslcontext = SSLContext.getInstance("SSL");
            sslcontext.init(null, tm, null);

            // 空の証明書情報が設定されたオブジェクトをセット
            ((HttpsURLConnection) http).setSSLSocketFactory(sslcontext.getSocketFactory());

             .
             .
             .
    }

    /**
     * 信頼する証明書情報を取得する。<br />
     * @return 証明書情報
     */
    private TrustManager[] getTrustManager() {

        TrustManager[] tm = {new X509TrustManager() {
            @Override
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] chain,
                    String authType) throws CertificateException {
                   // 何も検証しない
            }

            @Override
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] chain,
                    String authType) throws CertificateException {
                   // 何も検証しない
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                // 証明書情報として空を返す(つまり検証を行わない)
                return null;
            }
        } }; 
        return tm;
    } 

    /**
     * https通信時のデフォルトのホスト名検証ルールを設定する<br />
     */
    private void setDefaultHostnameVerifier() {
        // テスト用なので全てtrueを返す(つまり検証を行わない)
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            } 
        }); 
    } 
}}

** 検証を通過させる為のコードを書かない為のテスト環境構築 [#be6496b2]

上記のコードを書かない為に、できるだけ本番に近い環境構築を行う方法を記載する。
※ただし、本番ではまっとうな認証局に署名された正式な証明書を利用し、普通に名前解決ができるドメインを取得した方が良い。

#html(<div style="padding-left:10px;">)
*** 署名用の自己認証局(CA)の作成 [#jb785d54]
#myterm2(){{
mkdir -p /var/certs/myCA
cd /var/certs/myCA
mkdir private
mkdir newcerts
chmod 700 private
echo 01>serial
touch index.txt
sudo openssl req -new -x509 -newkey rsa:2048 -out myca.crt -keyout private/myca.key -days 365 -sha256

・
いろいろ聞かれるので入力する。
・
}}

*** 証明書情報の Organization Name が認証局と一致していなくても署名できるようにする。 [#ie6a75fb]
※オリジナルの openssl.cnf からコピーして作成する。
#myterm2(){{
cd /var/certs
cp /etc/ssl/openssl.cnf ./
}}

↓旧メモ
openssl.cnf
#mycode2(){{
[ CA_default ]
#dir     = ./demoCA
dir     = ./myCA

[ policy_match ]
#organizationName = match
organizationName = optional
}}

↓新(2017/10)
↓新メモ
openssl.cnf
#mycode2(){{
[ca]
default_ca    = my_ca_default

[my_ca_default]
new_certs_dir = ./myCA/newcerts
database      = ./myCA/index.txt
serial        = ./myCA/serial
policy        = my_ca_policy
default_md    = md5 
default_days  = 365 

[my_ca_policy]
commonName             = supplied
countryName            = optional
stateOrProvinceName    = optional
localityName           = optional
organizationName       = optional
organizationalUnitName = optional
emailAddress           = optional
}}

URLにIPアドレスを指定してアクセスする場合は以下の設定も追記
※IPアドレスでアクセスする場合は、証明書の Subject Alternative Name もチェックされるので、これを含む証明書を作成する必要がある。
#myterm2(){{
vim openssl.cnf

[ v3_req ]
subjectAltName = @alt_names

[ alt_names ]
IP.1 = XXX.XXX.XXX.XXX      # 実際のIPアドレス
}}

*** サーバ秘密鍵 及び 証明書署名要求(CSR)の作成 [#ya826837]

# サーバ秘密鍵の作成
#myterm2(){{
openssl genrsa -des3 -out example_com.key 1024
Generating RSA private key, 1024 bit long modulus
.++++++
...............++++++
e is 65537 (0x10001)
Enter pass phrase for example_com.key:
Verifying - Enter pass phrase for example_com.key:
}}

# サーバ秘密鍵のパスフレーズを解除(Apache自動起動用)
#myterm2(){{
openssl rsa -in example_com.key -out example_com_nopass.key
Enter pass phrase for example_com.key:
writing RSA key
}}

# サーバ証明書の署名要求(CSR)を作成
#myterm2(){{
openssl req -new -sha256 -key example_com.key -out example_com.csr
・
・
いろいろ聞かれるので入力する。
(注意!)
Common Name、Organization Name は 実際のドメイン名と合わせておく事。(IPアドレスでアクセスする場合はIPアドレスをそのまま入力)
・
・
}}

*** 自己認証局で署名 [#m47e81c9]

#myterm2(){{
sudo openssl ca -config ./openssl.cnf -md sha256 -cert myCA/myca.crt -keyfile myCA/private/myca.key -out example_com.crt -in example_com.csr

# IPアドレスでアクセスする場合は引数に -extensions v3_req も追加
#sudo openssl ca -config ./openssl.cnf -md sha256 -cert myCA/myca.crt -keyfile myCA/private/myca.key -out 192.168.1.10.crt -in 192.168.1.10.csr -extensions v3_req

・
・
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
}}


*** Apache設定ファイルの編集 [#ya5ae18b]

作成した鍵、サーバ証明書のpathを設定する(他のSSL周りの設定は [[ApacheでSSL(SNI)設定]] 等を参照 )
#myterm2(){{
SSLCertificateFile /path_to/example_com.crt
SSLCertificateKeyFile /path_to/example_com_nopass.key
}}

*** クライアント端末へのインストール [#v0954e6b]

上記で作成したCA証明書(myca.crt)を対象の端末にインストールする。


#html(</div>)

トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS