2012年2月16日木曜日

GoogleのC2DMサーバーのセキュリティ例外を無視させる

GoogleのC2DMサーバーはオレオレ証明書を使っているので、Javaのプログラムでメッセージをを送るとき、https経由だとSSLHandshakeExceptionが発生してしまう。

それを回避するプログラム。

private static final String SERVER_URL = "https://android.apis.google.com/c2dm/send";

java.net.URL c2dmUrl = new java.net.URL(SERVER_URL);

javax.net.ssl.HttpsURLConnection conn = (javax.net.ssl.HttpsURLConnection) c2dmUrl.openConnection();
conn.setDoOutput(true);

// C2DMサーバーの自己証明書の例外を無視する
javax.net.ssl.KeyManager[] km = null;
javax.net.ssl.TrustManager[] tm = {
 new javax.net.ssl.X509TrustManager() {
  public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws java.security.cert.CertificateException {}
  public void checkServerTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws java.security.cert.CertificateException {}
  public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; }
 }
};
javax.net.ssl.SSLContext sslcontext= javax.net.ssl.SSLContext.getInstance("SSL");
sslcontext.init(km, tm, new java.security.SecureRandom());
conn.setSSLSocketFactory(sslcontext.getSocketFactory());

この処置でもまだ'HTTPS hostname wrong:'エラーが発生する。証明書のホスト名とアクセスしているホスト名も違うようだ。

よって更にホスト名違いも無視するように設定を追加。

conn.setSSLSocketFactory(sslcontext.getSocketFactory());

// 証明書にあるホスト名とアクセスしているホスト名の違いを無視する
conn.setHostnameVerifier(
 new javax.net.ssl.HostnameVerifier() {
  public boolean verify(String host, javax.net.ssl.SSLSession ses) { return true; }
 }
);


もう、どれだけずさんな証明書なんだか ┐('~`;)┌