Androidプラットフォームによって信頼されていると見なされない認証局からの安全な接続を実現するには、次の主な手順が必要です。
多くのユーザーからの要望に応じて、私はここの私のブログ記事から最も重要な部分をミラーリングしました:
- 必要なすべての証明書(ルートおよび中間CA)を取得します。
- keytoolとBouncyCastleプロバイダーを使用してキーストアを作成し、証明書をインポートします
- Androidアプリにキーストアをロードし、セキュリティで保護された接続に使用します
java.net.ssl.HttpsURLConnection
(標準の代わりにApache HttpClientを使用することをお勧めします(理解しやすく、パフォーマンスが高い))
証明書をつかむ
エンドポイント証明書からルートCAまでのチェーンを構築するすべての証明書を取得する必要があります。つまり、中間CA証明書(存在する場合)とルートCA証明書があります。エンドポイント証明書を取得する必要はありません。
キーストアを作成する
BouncyCastleプロバイダーをダウンロードし、既知の場所に保存します。また、keytoolコマンド(通常はJREインストールのbinフォルダーの下にあります)を起動できることを確認します。
次に、取得した証明書(エンドポイント証明書はインポートしないでください)をBouncyCastle形式のキーストアにインポートします。
テストはしませんでしたが、証明書をインポートする順序は重要だと思います。つまり、最下位の中間CA証明書を最初にインポートしてから、ルートCA証明書までインポートします。
次のコマンドを使用すると、パスワードmysecretを持つ新しいキーストア(存在しない場合)が作成され、中間CA証明書がインポートされます。また、BouncyCastleプロバイダーも定義しました。このプロバイダーは、私のファイルシステムとキーストア形式にあります。チェーン内の証明書ごとにこのコマンドを実行します。
keytool -importcert -v -trustcacerts -file "path_to_cert/interm_ca.cer" -alias IntermediateCA -keystore "res/raw/mykeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret
証明書がキーストアに正しくインポートされたかどうかを確認します。
keytool -list -keystore "res/raw/mykeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret
チェーン全体を出力する必要があります:
RootCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 24:77:D9:A8:91:D1:3B:FA:88:2D:C2:FF:F8:CD:33:93
IntermediateCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 98:0F:C3:F8:39:F7:D8:05:07:02:0D:E3:14:5B:29:43
これで、キーストアをAndroidアプリのrawリソースとしてコピーできます。 res/raw/
アプリでキーストアを使用する
まず、HTTPS接続にキーストアを使用するカスタムApache HttpClientを作成する必要があります。
import org.apache.http.*
public class MyHttpClient extends DefaultHttpClient {
final Context context;
public MyHttpClient(Context context) {
this.context = context;
}
@Override
protected ClientConnectionManager createClientConnectionManager() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// Register for port 443 our SSLSocketFactory with our keystore
// to the ConnectionManager
registry.register(new Scheme("https", newSslSocketFactory(), 443));
return new SingleClientConnManager(getParams(), registry);
}
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
try {
// Initialize the keystore with the provided trusted certificates
// Also provide the password of the keystore
trusted.load(in, "mysecret".toCharArray());
} finally {
in.close();
}
// Pass the keystore to the SSLSocketFactory. The factory is responsible
// for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);
// Hostname verification from certificate
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
カスタムHttpClientを作成しました。これで安全な接続に使用できます。たとえば、RESTリソースに対してGET呼び出しを行う場合:
// Instantiate the custom HttpClient
DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpGet get = new HttpGet("https://www.mydomain.ch/rest/contacts/23");
// Execute the GET call and obtain the response
HttpResponse getResponse = client.execute(get);
HttpEntity responseEntity = getResponse.getEntity();
それでおしまい ;)