HTTPS通信で、サーバーに接続するクライアントアプリケーションを開発しているのですが、久々にはまりました。(というか、ただの勉強不足でした。)
当初、HTTP通信に毛が生えた程度だろうと思っていて、以下のコードを実装しましたが、全く受け付けず・・・。
①keytoolを用いて、テスト用証明書をクライアントにストア
②以下のソースを実装 → Connectionが取得できない。
PrintWriter out = null; HttpsURLConnection con = null; Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); System.getProperties().put("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol"); try { // URLオブジェクト生成 URL server = new URL("https://localhost:3000"); // コネクション生成 con = (HttpsURLConnection) server.openConnection(); // 出力を行うように設定します con.setDoOutput(true); // 出力ストリームを取得 out = new PrintWriter(con.getOutputStream()); out.print(aData); out.close(); } catch (Exception e) { throw e; } finally { if (out != null){ out.close(); } ・・・(取得部分は省略)・・・
↑こんなんではだめだったみたいで、ソケット通信しないと駄目っぽいです。
(↓改良版 かなり荒れてますが笑)
SSLSocket socket = null; try { SocketFactory factory = javax.net.ssl.SSLSocketFactory.getDefault(); socket = (SSLSocket)factory.createSocket("localhost",3000); } catch (Exception e) { e.printStackTrace(); } //ハンドシェイク try { socket.startHandshake(); } catch (Exception e) { e.printStackTrace(); } PrintWriter socketOut = null; if (socket.isConnected()) { socket.setSoTimeout(300000); socketOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))); socketOut.print("POST " + aUrl + " HTTP/1.0\r\n"); socketOut.print("Content-Length:" + aData.length() + "\r\n"); socketOut.print("\r\n"); socketOut.print("\r\n"); socketOut.print(aData); socketOut.print("\r\n"); socketOut.print("\r\n"); socketOut.flush(); if (socketOut.checkError()) { throw new IOException(); } else { BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); String inputLine; StringBuffer buf = new StringBuffer(); while*1 != null) { System.out.println(inputLine); } } }
サーバー証明書をkeytoolを用いて、クライアント側にインストールすることがめんどくさいんで、強制的に認証するように組みました。さらに、ソケット通信でなくて、HttpsURLConnectionクラスを使いました。
PrintWriter out = null; // URLオブジェクト生成 URL server = new URL(aUrl); // コネクション生成 SSLSocketFactory sslsf = null; KeyManager km = null; // サーバーの証明書を強制的に認証させる TrustManager tm = { new X509TrustManager() { public X509Certificate getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate chain, String authType) { } public void checkServerTrusted(X509Certificate[] chain, String authType) { } } }; // ソケットプロトコルを実装するSSLContextを作成 try { SSLContext sslContext = SSLContext.getInstance("SSL"); // SSLContextを初期化 sslContext.init(km, tm, new SecureRandom()); // SSLContextのSocketFactoryを取得 sslsf = sslContext.getSocketFactory(); } catch (KeyManagementException e2) { // TODO 自動生成された catch ブロック e2.printStackTrace(); throw e2; } catch (NoSuchAlgorithmException e2) { // TODO 自動生成された catch ブロック e2.printStackTrace(); throw e2; } // URLConnectionにSocketFactoryをセット HttpsURLConnection hCon = (HttpsURLConnection) server .openConnection(); *2; hCon.setDoOutput(true); // ホスト名を無視させる HostnameVerifier hv = new HostnameVerifier() { public boolean verify(String hostName, SSLSession session) { return true; } }; *3; out.print(aData); out.close(); } catch (MalformedURLException e) { throw e; } catch (IOException e1) { throw e1; } finally { if (out != null) { out.close(); } } // 受信処理は省略