/*
* http://forum.java.sun.com/thread.jspa?threadID=521779&tstart=90
* File name: TestServlet.java
*
* Created on 2005.01.21.
*/
package georgie.test.servlet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author György Novák
*/
public class TestServlet extends HttpServlet
{
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
try
{
trustAllHttpsCertificates();
String urlStr = request.getParameter("url");
HttpsURLConnection.setDefaultHostnameVerifier(hv);
URL url = new URL(urlStr == null ? "https://www.verisign.com/"
: urlStr);
debug("URL READY");
BufferedReader in = new BufferedReader(new InputStreamReader(url
.openStream()));
debug("INPUT READY");
int buff;
while ((buff = in.read()) != -1)
{
}
in.close();
debug("EVERYTHING IS DONE!!!");
}
catch (Exception e)
{
e.printStackTrace();
}
}
HostnameVerifier hv = new HostnameVerifier()
{
public boolean verify(String urlHostName, SSLSession session)
{
System.out.println("Warning: URL Host: " + urlHostName + " vs. "
+ session.getPeerHost());
return true;
}
};
private void debug(String s)
{
System.out.println("[DEBUG] -- TestServlet -- \n" + s);
}
private static void trustAllHttpsCertificates() throws Exception
{
// Create a trust manager that does not validate certificate chains:
javax.net.ssl.TrustManager[] trustAllCerts =
new javax.net.ssl.TrustManager[1];
javax.net.ssl.TrustManager tm = new miTM();
trustAllCerts[0] = tm;
javax.net.ssl.SSLContext sc =
javax.net.ssl.SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, null);
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(
sc.getSocketFactory());
}
public static class miTM implements javax.net.ssl.TrustManager,
javax.net.ssl.X509TrustManager
{
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}
public boolean isServerTrusted(
java.security.cert.X509Certificate[] certs)
{
return true;
}
public boolean isClientTrusted(
java.security.cert.X509Certificate[] certs)
{
return true;
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType)
throws java.security.cert.CertificateException
{
return;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType)
throws java.security.cert.CertificateException
{
return;
}
}
}
インターネットバンキングやネットショッピングなどのように、インターネットを通じてWebブラウザから操作する仕組みを使って金融取引や通信販売などのサービスを提供する場合、第三者による不正行為を防止するために、利用者とサービス提供者以外に対して情報が漏洩することを防ぐ必要がある。
そこで現在、一般的に採用されている方法が、SSLによる暗号化通信機能を付加したHTTPによる通信である。SSL通信やHTTPS通信と呼ばれることが多く、実質的に業界標準といえる。JavaもHTTPS通信に対応しており、HTTPSによって提供されているサイトにアクセスすることができる。
URLConnection - HTTPS通信にも対応
アクセス先はURIで保持しておき、使う段階でURLに変換して使う。そして実際の通信はURLConnectionを経由して実施する。これはHTTPプログラミングを実施する際の基本だ。これについては第54回「HTTP通信の基礎(1) - URIとURL、URLConnection」で説明した。
同回において、実際にURLConnectionを使ってアクセスし、ヘッダとコンテンツを取得するソースコードを紹介した。同ソースコードを使ってHTTPSでサービスを提供しているサイトのコンテンツを取得させてみてほしい。
リスト1 NetCat.java
import java.net.*;
import java.util.*;
public class NetCat {
public static void main(String[] argv)
throws Exception {
URI uri = new URI(argv[0]);
URLConnection connection = uri.toURL().openConnection();
// ヘッダ情報を出力
Map headers = connection.getHeaderFields();
for (Object key : headers.keySet()) {
System.out.println(key + ": " + headers.get(key));
}
// コンテンツを出力
BufferedReader reader =
new BufferedReader(new InputStreamReader
(connection.getInputStream(), "JISAutoDetect"));
String buffer = reader.readLine();
System.out.println();
while (null != buffer) {
System.out.println(buffer);
buffer = reader.readLine();
}
}
}
プロンプト1 NetCat.java を使ってHTTPS通信で提供されているサイトにアクセス
Connection: [close]
Date: [Mon, 24 Jul 2006 04:07:53 GMT]
null: [HTTP/1.1 200 OK]
Pragma: [no-cache]
Server: [Apache]
Content-Type: [text/html; charset=EUC-JP]
Transfer-Encoding: [chunked]
Cache-Control: [no-cache]
Vary: [Accept-Encoding]
<html>
<head>
...
%
プロンプト1が実行例だ。HTTPSでサービスが提供されているサイトも、HTTPの場合と同じように表示されていることがわかる。URLConnectionは抽象クラスであり、実際にはHttpURLConnectionやHttpsURLConnection、JarURLConnectionなどのサブクラスを経由して使うことになる。つまり、ただ単に通信を実施してコンテンツのやりとりをおこなう程度であれば、URLConnectionを使っておけばよいことがわかる。
HTTPSに特化した処理は HttpsURLConnection を使う
HTTPSに特化した処理はURLConnectionではなく、HttpsURLConnectionを使う。HttpsURLConnectionはHttpURLConnectionを継承したクラスであるため、HTTPと共通の機能はHttpURLConnectionを使えばよく、SSL特有の処理が必要な場合にだけHttpsURLConnectionを使えばいい。HttpsURLConnectionを使った例をリスト2に示す。URLConnectionインスタンスをHttpsURLConnectionインスタンスにキャストして使っていることがわかるだろう。HTTPSで通信を実施した場合、実体はHttpsURLConnectionだからキャストが可能だ。
リスト2 SSLNetCat.java
import java.net.*;
import java.util.*;
import javax.net.ssl.*;
public class SSLNetCat {
public static void main(String[] argv)
throws Exception {
URI uri = new URI(argv[0]);
URLConnection connection = uri.toURL().openConnection();
// ヘッダ情報を出力
Map headers = connection.getHeaderFields();
for (Object key : headers.keySet()) {
System.out.println(key + ": " + headers.get(key));
}
// SSL情報を出力
HttpsURLConnection sslconnection =
(HttpsURLConnection)connection;
System.out.println();
System.out.println("符号化方式:" + sslconnection.getCipherSuite());
// コンテンツを出力
BufferedReader reader =
new BufferedReader(new InputStreamReader
(connection.getInputStream(), "JISAutoDetect"));
String buffer = reader.readLine();
System.out.println();
while (null != buffer) {
System.out.println(buffer);
buffer = reader.readLine();
}
}
}
プロンプト2 SSLNetCat.java を使ってHTTPS通信で提供されているサイトにアクセス
Connection: [close]
Date: [Mon, 24 Jul 2006 04:30:09 GMT]
null: [HTTP/1.1 200 OK]
Pragma: [no-cache]
Server: [Apache]
Content-Type: [text/html; charset=EUC-JP]
Transfer-Encoding: [chunked]
Cache-Control: [no-cache]
Vary: [Accept-Encoding]
符号化方式:SSL_RSA_WITH_RC4_128_MD5
<html>
<head>
...
% l
ためしにリスト2をHTTPS通信ではないサイトの通信に適用してみるといい。キャストに失敗して処理が終了する。HTTPS通信でない場合はHttpsURLConnectionへはキャストできない。
HttpsURLConnection
HttpsURLConnectionにはほかにもハンドシェーク中にサーバとやりとりした証明書や、セッションを定義する段階における主体などを得るためのメソッドも用意されている。接続先の制御や細かい制御をおこないたい場合は、これらメソッドを通じて情報を取得することになる。
HttpsURLConnectionを使わなくとも、URLConnectionを使っていればHTTP/HTTPSの両方が同じように扱える点は、Java APIの便利なところといえるだろう。ちなみにHttpsURLConnectionはjava.netパッケージではなく、javax.net.sslパッケージにまとめられている。importを忘れないようにしたい。