Symfoware

Symfowareについての考察blog

Java HttpsURLConnectionで、証明書エラーを無視して接続する

Riakで管理画面を使いたい時はhttpsで閲覧する必要があります。
インストール時に添付されている証明書をそのまま使用すると、もちろん証明書の警告が表示されます。

252_01.png

そのまま続行して閲覧すると、Riakの情報を表示させることができます。

252_02.png


JavaからHttpsURLConnectionを使用して接続するとエラーになるので、
httpは8097
httpsは8098
と、2つのURLを用意して、Javaからはhttp経由で接続してました。

ちゃんと調べてみると、証明書を無視してhttps接続できたのでメモしておきます。





発生するエラー



例えば、こんなプログラムを実行してみます。


  1. package sample;
  2. import java.io.BufferedReader;
  3. import java.io.InputStreamReader;
  4. import java.net.URL;
  5. import javax.net.ssl.HttpsURLConnection;
  6. public class HttpsTest {
  7.     public static void main(String[] arsg) throws Exception {
  8.         
  9.         try {
  10.             URL connectUrl = new URL("https://192.168.1.6:8098/buckets?buckets=true");
  11.             HttpsURLConnection urlconn = (HttpsURLConnection)connectUrl.openConnection();
  12.             urlconn.setRequestMethod("GET");
  13.             
  14.             urlconn.setInstanceFollowRedirects(false);
  15.             
  16.             urlconn.connect();
  17.             
  18.             BufferedReader reader = new BufferedReader(new InputStreamReader(urlconn.getInputStream(), "utf8"));
  19.             
  20.             String line;
  21.             StringBuilder sb = new StringBuilder();
  22.             while ((line = reader.readLine()) != null){
  23.                 sb.append(line);
  24.             }
  25.             
  26.             reader.close();
  27.             urlconn.disconnect();
  28.             
  29.             System.out.println(sb.toString());
  30.             
  31.             
  32.         } catch(Exception e){
  33.             e.printStackTrace();
  34.         }
  35.         
  36.         
  37.     }
  38.     
  39. }




発生するエラーはこんな感じ。


javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1715)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:257)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:251)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1168)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:153)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:609)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:545)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:963)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1208)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1235)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1219)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:440)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153)
    at sample.HttpsTest.main(HttpsTest.java:20)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:324)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:224)
    at sun.security.validator.Validator.validate(Validator.java:235)
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:147)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:230)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:270)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1147)
    ... 11 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:197)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:255)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:319)
    ... 17 more









修正したソース



以下に紹介されている方法を参考にしました。

javassl
Disable Certificate Validation in Java SSL Connections
JAVA: How to configure a client for using SSL


ソースはこのようになります。


  1. package sample;
  2. import java.io.BufferedReader;
  3. import java.io.InputStreamReader;
  4. import java.net.URL;
  5. import java.security.cert.CertificateException;
  6. import java.security.cert.X509Certificate;
  7. import javax.net.ssl.HostnameVerifier;
  8. import javax.net.ssl.HttpsURLConnection;
  9. import javax.net.ssl.SSLContext;
  10. import javax.net.ssl.SSLSession;
  11. import javax.net.ssl.TrustManager;
  12. import javax.net.ssl.X509TrustManager;
  13. public class HttpsTest {
  14.     public static void main(String[] arsg) throws Exception {
  15.         try {
  16.             
  17.             //証明書情報 全て空を返す
  18.             TrustManager[] tm = { new X509TrustManager() {
  19.                 public X509Certificate[] getAcceptedIssuers() {
  20.                     return null;
  21.                 }
  22.                 @Override
  23.                 public void checkClientTrusted(X509Certificate[] chain,
  24.                         String authType) throws CertificateException {
  25.                 }
  26.                 @Override
  27.                 public void checkServerTrusted(X509Certificate[] chain,
  28.                         String authType) throws CertificateException {
  29.                 }
  30.             } };
  31.             SSLContext sslcontext = SSLContext.getInstance("SSL");
  32.             sslcontext.init(null, tm, null);
  33.             //ホスト名の検証ルール 何が来てもtrueを返す
  34.             HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
  35.                 @Override
  36.                 public boolean verify(String hostname,
  37.                         SSLSession session) {
  38.                     return true;
  39.                 }
  40.             });
  41.             
  42.             
  43.             //https接続を試す
  44.             URL connectUrl = new URL(
  45.                     "https://192.168.1.6:8098/buckets?buckets=true");
  46.             
  47.             HttpsURLConnection urlconn = (HttpsURLConnection) connectUrl
  48.                     .openConnection();
  49.             urlconn.setRequestMethod("GET");
  50.             urlconn.setSSLSocketFactory(sslcontext.getSocketFactory());
  51.             
  52.             urlconn.connect();
  53.             
  54.             //データの読み取り
  55.             BufferedReader reader = new BufferedReader(new InputStreamReader(
  56.                     urlconn.getInputStream(), "utf8"));
  57.             String line;
  58.             StringBuilder sb = new StringBuilder();
  59.             while ((line = reader.readLine()) != null) {
  60.                 sb.append(line);
  61.             }
  62.             reader.close();
  63.             urlconn.disconnect();
  64.             System.out.println(sb.toString());
  65.         } catch (Exception e) {
  66.             e.printStackTrace();
  67.         }
  68.     }
  69. }




実行してみると、エラーなしで情報が取得出来ました。


{"buckets":["myBucket"]}






自分のミスなのですが、はまった点としてソースを修正してもこんなエラーが発生しました。


java.io.IOException: HTTPS hostname wrong: should be <192.168.1.6>
    at sun.net.www.protocol.https.HttpsClient.checkURLSpoofing(HttpsClient.java:529)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:454)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153)
    at sample.HttpsTest.main(HttpsTest.java:70)




実行しようとしていたプログラムはこちら。


  1. package sample;
  2. import java.io.BufferedReader;
  3. import java.io.InputStreamReader;
  4. import java.net.URL;
  5. import java.security.cert.CertificateException;
  6. import java.security.cert.X509Certificate;
  7. import javax.net.ssl.HostnameVerifier;
  8. import javax.net.ssl.HttpsURLConnection;
  9. import javax.net.ssl.SSLContext;
  10. import javax.net.ssl.SSLSession;
  11. import javax.net.ssl.TrustManager;
  12. import javax.net.ssl.X509TrustManager;
  13. public class HttpsTest {
  14.     public static void main(String[] arsg) throws Exception {
  15.         try {
  16.             
  17.             //https接続を試す
  18.             URL connectUrl = new URL(
  19.                     "https://192.168.1.6:8098/buckets?buckets=true");
  20.             
  21.             HttpsURLConnection urlconn = (HttpsURLConnection) connectUrl
  22.                     .openConnection();
  23.             urlconn.setRequestMethod("GET");
  24.             
  25.             //証明書情報 全て空を返す
  26.             TrustManager[] tm = { new X509TrustManager() {
  27.                 public X509Certificate[] getAcceptedIssuers() {
  28.                     return null;
  29.                 }
  30.                 @Override
  31.                 public void checkClientTrusted(X509Certificate[] chain,
  32.                         String authType) throws CertificateException {
  33.                 }
  34.                 @Override
  35.                 public void checkServerTrusted(X509Certificate[] chain,
  36.                         String authType) throws CertificateException {
  37.                 }
  38.             } };
  39.             SSLContext sslcontext = SSLContext.getInstance("SSL");
  40.             sslcontext.init(null, tm, null);
  41.             //ホスト名の検証ルール 何が来てもtrueを返す
  42.             HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
  43.                 @Override
  44.                 public boolean verify(String hostname,
  45.                         SSLSession session) {
  46.                     return true;
  47.                 }
  48.             });
  49.             
  50.             
  51.             //ここでsetSSLSocketFactoryを実行
  52.             urlconn.setSSLSocketFactory(sslcontext.getSocketFactory());
  53.             //接続
  54.             urlconn.connect();
  55.             
  56.             
  57.             //データの読み取り
  58.             BufferedReader reader = new BufferedReader(new InputStreamReader(
  59.                     urlconn.getInputStream(), "utf8"));
  60.             String line;
  61.             StringBuilder sb = new StringBuilder();
  62.             while ((line = reader.readLine()) != null) {
  63.                 sb.append(line);
  64.             }
  65.             reader.close();
  66.             urlconn.disconnect();
  67.             System.out.println(sb.toString());
  68.         } catch (Exception e) {
  69.             e.printStackTrace();
  70.         }
  71.     }
  72. }




冷静に見ると当たり前なのですが、openConnectionする前にsetDefaultHostnameVerifierを
実行しないといけないですね。








多少汎用的に



http,httpsを意識せずにURLConnectionするにはこんな感じでしょうか。


  1. package sample;
  2. import java.io.BufferedReader;
  3. import java.io.InputStreamReader;
  4. import java.net.HttpURLConnection;
  5. import java.net.URL;
  6. import java.security.cert.CertificateException;
  7. import java.security.cert.X509Certificate;
  8. import javax.net.ssl.HostnameVerifier;
  9. import javax.net.ssl.HttpsURLConnection;
  10. import javax.net.ssl.SSLContext;
  11. import javax.net.ssl.SSLSession;
  12. import javax.net.ssl.TrustManager;
  13. import javax.net.ssl.X509TrustManager;
  14. public class HttpsTest {
  15.     public static void main(String[] arsg) throws Exception {
  16.         try {
  17.             
  18.             //String url ="http://192.168.1.6:8097/buckets?buckets=true";
  19.             String url ="https://192.168.1.6:8098/buckets?buckets=true";
  20.             HttpURLConnection urlconn = getHttpsConnection(url);
  21.             
  22.             
  23.             //データの読み取り
  24.             BufferedReader reader = new BufferedReader(new InputStreamReader(
  25.                     urlconn.getInputStream(), "utf8"));
  26.             String line;
  27.             StringBuilder sb = new StringBuilder();
  28.             while ((line = reader.readLine()) != null) {
  29.                 sb.append(line);
  30.             }
  31.             reader.close();
  32.             urlconn.disconnect();
  33.             System.out.println(sb.toString());
  34.         } catch (Exception e) {
  35.             e.printStackTrace();
  36.         }
  37.     }
  38.     
  39.     
  40.     private static HttpURLConnection getHttpsConnection(String url) throws Exception {
  41.         
  42.         HttpURLConnection urlconn = null;
  43.         
  44.         URL connectURL = new URL(url);
  45.         
  46.         // https接続の場合
  47.         if ("https".equals(connectURL.getProtocol())) {
  48.             
  49.             //証明書情報 全て空を返す
  50.             TrustManager[] tm = { new X509TrustManager() {
  51.                 public X509Certificate[] getAcceptedIssuers() {
  52.                     return null;
  53.                 }
  54.                 @Override
  55.                 public void checkClientTrusted(X509Certificate[] chain,
  56.                         String authType) throws CertificateException {
  57.                 }
  58.                 @Override
  59.                 public void checkServerTrusted(X509Certificate[] chain,
  60.                         String authType) throws CertificateException {
  61.                 }
  62.             } };
  63.             SSLContext sslcontext = SSLContext.getInstance("SSL");
  64.             sslcontext.init(null, tm, null);
  65.             //ホスト名の検証ルール 何が来てもtrueを返す
  66.             HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
  67.                 @Override
  68.                 public boolean verify(String hostname,
  69.                         SSLSession session) {
  70.                     return true;
  71.                 }
  72.             });
  73.             
  74.             urlconn = (HttpsURLConnection) connectURL.openConnection();
  75.             
  76.             ((HttpsURLConnection)urlconn).setSSLSocketFactory(sslcontext.getSocketFactory());
  77.         
  78.             
  79.         // http接続の場合
  80.         } else {
  81.             
  82.             urlconn = (HttpURLConnection) connectURL.openConnection();
  83.             
  84.         }
  85.         
  86.         // http,https共通
  87.         
  88.         urlconn.setRequestMethod("GET");
  89.         //接続
  90.         urlconn.connect();
  91.         
  92.         
  93.         return urlconn;
  94.     }
  95. }






関連記事

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2013/11/11(月) 23:04:16|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
<<Ubuntu(Debian)で、Javaのインストールパスを確認する(update-java-alternatives) | ホーム | 分散SQLエンジン「Presto」をDebian(wheezy)にインストールする>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://symfoware.blog68.fc2.com/tb.php/1165-25d2dc73
この記事にトラックバックする(FC2ブログユーザー)