javax.net.ssl.SSLSocket を使ってHTTPSにアクセスする

java.net.URLConnection を使えば簡単なのだが、あえて javax.net.ssl.SSLSocket を使ってHTTPSにアクセスしてみるサンプル。

このサンプルはScalaだがJavaでも同じようなもんだろう。

// SunのJavaであれば、$JAVA_HOME/jre/lib/security/cacerts というパスに
// 必要なファイルがあるらしいのだが、
// 私の環境ではSunのではなかったらしく、みつからなかった。
// $ locate cacerts
// としたらいくつかあり、そのうちの /etc/ssl/certs/java/cacerts というパスを使ってみた。
// このファイルがなにものかあまり理解していないが、
// このファイルを読み込むにはパスワードが必要で、
// Sunのものや、上記パスのものは "changeit" というパスワードが設定されているらしい。
val keyStore = java.security.KeyStore.getInstance("JKS");
keyStore.load(new java.io.FileInputStream("/etc/ssl/certs/java/cacerts"), "changeit".toCharArray);

val tmf = javax.net.ssl.TrustManagerFactory.getInstance("PKIX");
tmf.init(keyStore);

val context = javax.net.ssl.SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);

val sf = context.getSocketFactory();

// https://twitter.com/ にアクセスしてみる。
val socket = sf.createSocket("twitter.com", 443).asInstanceOf[javax.net.ssl.SSLSocket];

socket.startHandshake();

// ここで指定している文字コードはなんでもいいと思う。
val op = new java.io.OutputStreamWriter(socket.getOutputStream(), "UTF-8");

// https://twitter.com/ へのリクエストを投げる
op.write("GET / HTTP/1.1\r\nHost: twitter.com\r\n\r\n");
op.flush();

// ここで指定している文字コードはなんでもいいと思う。
val ip = new java.io.InputStreamReader(socket.getInputStream(), "UTF-8");

// ループはソケットからの入力を標準出力にそのまま出しているだけ。
// HTTPSのレスポンスが出力される。
while({
  val c = ip.read();
  if(c < 0){
    false;
  } else {
    print(c.asInstanceOf[Char]);
    true;
  }
}){}
socket.close();