LoginSignup
32
41

More than 5 years have passed since last update.

Javaでオレオレ証明書のサーバにアクセスする

Last updated at Posted at 2014-11-27

オレオレ証明書が入っているサーバに、Android等JavaのコードでHTTPSアクセスする方法です。

コード

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class RequestTest {
    public static void main(String[] args) throws KeyManagementException, NoSuchAlgorithmException, IOException {
        URL url = new URL("https://example.com/cert");

        HttpsURLConnection connection = makeOreOreHttpsURLConnection(url);

        connection.setRequestMethod("POST");
        connection.setDoOutput(true);

        BufferedWriter requestBody = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream()));
        requestBody.write("{\"key\":\"value\"}");
        requestBody.flush();
        requestBody.close();

        connection.connect();

        BufferedReader responseBody = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        String response = "";
        while (responseBody.ready()) {
            response += responseBody.readLine();
        }

        System.out.println(response);
    }

    private static HttpsURLConnection makeOreOreHttpsURLConnection(URL url) throws IOException, NoSuchAlgorithmException, KeyManagementException {
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();

        // 証明書に書かれているCommon NameとURLのホスト名が一致していることの検証をスキップ
        connection.setHostnameVerifier(new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession sslSession) {
                return true;
            }
        });

        // 証明書チェーンの検証をスキップ
        KeyManager[] keyManagers = null;
        TrustManager[] transManagers = { new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        } };
        SSLContext sslcontext = SSLContext.getInstance("SSL");
        sslcontext.init(keyManagers, transManagers, new SecureRandom());
        connection.setSSLSocketFactory(sslcontext.getSocketFactory());

        return connection;
    }
}

となります。

通常なら

HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();

でHttpsURLConnectionのオブジェクトを作成する代わりに、ここでは

HttpsURLConnection connection = makeOreOreHttpsURLConnection(url);

を呼び出して、証明書の正当性を無視するHttpsURLConnection を作成しています。

おまけ

やっている内容はmakeOreOreHttpsURLConnectionメソッド内のコメントの通り、

  • HttpsURLConnection#setHostnameVerifier()でホスト名の検証スキップ
  • HttpsURLConnection#setSSLSocketFactory()証明書チェーンの検証スキップ

の2つです。

ところで、それぞれの代わりの手段として、

  • HttpsURLConnection#getDefaultHostnameVerifier()
  • HttpsURLConnection#setDefaultSSLSocketFactory()

といったメソッドもあります。
こちらは、API仕様には

このクラスの新しいインスタンスに継承されるデフォルトの HostnameVerifier を設定します。

とあります。
オレオレ証明書を信用するという変則的な挙動をデフォルトとして設定するのは気味が悪いですが、
HttpsURLConnectionオブジェクトを作成している箇所が多い場合は、どこか1か所でこれらのメソッドを呼んでおけば処理をまとめられるメリットもあります。

32
41
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
41