スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

[Android] Tomcat5.5&オレオレSSL

お久しぶりの更新です(・ω・)
今回はAndroidでオレオレ証明書を使ってSSL通信する方法について。

まずJavaでSSL通信する方法についてのおさらいですが、フツーに通信したい場合は

URLConnection connection = new URL("https://~").openConnection();
HttpsURLConnection con = (HttpsURLConnection) connection;
StringBuffer buffer = new StringBuffer();
InputStreamReader is = new InputStreamReader(con.getInputStream());
BufferedReader reader = new BufferedReader(is);
String str;
while ((str = reader.readLine()) != null)
{
  buffer.append(str).append("\n");
}
is.close();

等とやればおk。
但し、この方法では自己証明書鯖が相手だとSSLHandShakeErrorでコケます。

こんな場合は以下のように上記のソースコードに下記のソースコードを追加して
署名エラーを無視してしまえば良いのですが。。。

// 自己証明書でも問答無用にする。
static
{
  HostnameVerifier hv = new HostnameVerifier()
  {
    public boolean verify(String arg0, SSLSession arg1)
    {
      return true;
    }
  };

  HttpsURLConnection.setDefaultHostnameVerifier(hv);

  KeyManager[] km = null;
  TrustManager[] tm = { new X509TrustManager()
  {
    public void checkClientTrusted(X509Certificate[] arg0, String arg1)
      throws CertificateException
    {
    }
    public void checkServerTrusted(X509Certificate[] arg0, String arg1)
      throws CertificateException
    {
    }
    public X509Certificate[] getAcceptedIssuers()
    {
      return null;
    }
  } };

  SSLContext sslcon = SSLContext.getInstance("SSL");
  sslcon.init(km, tm, new SecureRandom());
  HttpsURLConnection.setDefaultSSLSocketFactory(sslcon.getSocketFactory());
}

Androidだと、この場合でもNot trusted server certificateでコケます。
んじゃーどしたらええねん(´・ω・`)ってコトで、以下のサイトを参考にやってみました。
http://blog.antoine.li/index.php/2010/10/android-trusting-ssl-certificates/

まず実験で使ったネト環境について。
oreore.png

まずTomcat鯖に適当なWEBサービスを作っておく。今回は以前の記事を参考に
JSONICを使って現在時刻をJSON形式で返す簡単なサービスを作ってみました。

あと、TomcatでSSL/TLSを使えるようにしておく。こちらも以前の記事を参照。
※但しキーストア生成時の姓名(CN)はTomcat鯖のホスト名で生成してる事。
 じゃないと後述のソースを実行した時にホスト名のミスマッチで通信できなくなる。
 (今回は名前解決しないので、CNはそのままIPアドレス(192.168.1.3)にしました)

準備が出来たらウェブブラウザ等でWEBサービスがHTTPSでちゃんと動くか確認。
https://192.168.1.3:8443/TestJson/Test.json

その後、JavaのKeytoolコマンドを使ってBKS形式のキーストアを生成する。
①まずTomcatで使っているキーストアファイルから証明書(certificate)をエクスポートする。
 
 keytool -export -alias tomcat -keystore xxx.jks -file xxx.cer

 ちなみに上記のxxx.jksはTomcatでSSL/TLSを使うのに生成したキーストアファイルのコト。
 (前回の記事でいくとmy.keystoreと同義)
 上記を実行すると、カレントディレクトリにxxx.cerが生成される。

②お次にBouncyCastleProviderを使ってBKS形式のキーストアファイルを生成。
 その際、①でExportした証明書をインポートする。
 以下のサイトからbcprov-jdk16-146.jarを入手。
 http://www.bouncycastle.org/latest_releases.html
 
 keytool -importcert -v -trustcacerts -file xxx.cer -alias tomcat
-provider org.bouncycastle.jce.provider.BouncyCastleProvider
-providerpath bcprov-jdk16-146.jar
-keystore xxx.bks -storetype BKS -storepass changeit

 
 上記のxxx.cerは①で出力した証明書。
 -providerpathには先ほど入手したbcprov-jdk16-146.jarのパスを指定する。
 実行すると、カレントディレクトリにxxx.bksが生成される。

ちなみに何でBKS形式なのかと言うと、keytoolデフォルトのJKSだと
KeyStore JKS Implementation Not Found
というエラーでAndroidが受け付けてくれない(´・ω・`)ホントもぅ手のかかるったらww

生成したxxx.bksをAndroidプロジェクトの/res/raw/にインポート後、以下のソースを実行。

String url = "https://192.168.1.3:8443/TestJson/Test.json";
URLConnection connection = new URL(url).openConnection();
HttpsURLConnection con = (HttpsURLConnection) connection;

// キーストアファイルを読み込み。R.raw.xxxは/res/raw/xxx.bks
InputStream is = this.getResources().openRawResource(R.raw.xxx);
KeyStore ks = KeyStore.getInstance("BKS");
ks.load(is, "changeit".toCharArray());
is.close();

SSLContext sslcon = SSLContext.getInstance("TLS");

TrustManager x509 = new X509TrustManager()
{
public X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException
{
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException
{
}
};
sslcon.init(null, new TrustManager[] { x509 }, null);

con.setSSLSocketFactory(sslcon.getSocketFactory());
con.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

StringBuffer buffer = new StringBuffer();
InputStreamReader is = new InputStreamReader(con.getInputStream());
BufferedReader reader = new BufferedReader(is);
String str;
while ((str = reader.readLine()) != null)
{
  buffer.append(str).append("\n");
}
is.close();

今回、ホスト名がIPアドレスの為、
con.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
にしていますが、名前解決可能なホスト名(FQDN)であれば、
con.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
でも良いと思われる。
関連記事
スポンサーサイト

Pagination

Trackback

Trackback URL

http://morado106.blog106.fc2.com/tb.php/75-3085b6c8

Comment

Post Your Comment

コメント登録フォーム
公開設定

Utility

プロフィール

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。