Aby uzyskać bezpieczne połączenie od urzędów certyfikacji, które nie są uważane za zaufane przez platformę Android, wymagane są następujące główne kroki.
Zgodnie z prośbą wielu użytkowników, odtworzyłem tutaj najważniejsze części z mojego artykułu na blogu :
- Pobierz wszystkie wymagane certyfikaty (główne i pośrednie CA)
- Utwórz magazyn kluczy za pomocą narzędzia keytool i dostawcy BouncyCastle i zaimportuj certyfikaty
- Załaduj magazyn kluczy w aplikacji na Androida i używaj go do zabezpieczonych połączeń (polecam używać Apache HttpClient zamiast standardowego
java.net.ssl.HttpsURLConnection
(łatwiejszy do zrozumienia, bardziej wydajny)
Złap certyfikaty
Musisz uzyskać wszystkie certyfikaty, które tworzą łańcuch od certyfikatu punktu końcowego przez całą drogę do głównego urzędu certyfikacji. Oznacza to wszystkie (jeśli są obecne) certyfikaty pośredniego urzędu certyfikacji, a także certyfikat głównego urzędu certyfikacji. Nie musisz uzyskiwać certyfikatu punktu końcowego.
Utwórz magazyn kluczy
Pobierz dostawcę BouncyCastle i zapisz go w znanej lokalizacji. Upewnij się również, że możesz wywołać polecenie keytool (zwykle znajduje się w folderze bin instalacji JRE).
Teraz zaimportuj uzyskane certyfikaty (nie importuj certyfikatu punktu końcowego) do magazynu kluczy w formacie BouncyCastle.
Nie testowałem tego, ale myślę, że kolejność importu certyfikatów jest ważna. Oznacza to, że najpierw należy zaimportować najniższy certyfikat pośredniego urzędu certyfikacji, a następnie aż do certyfikatu głównego urzędu certyfikacji.
Za pomocą następującego polecenia zostanie utworzony nowy magazyn kluczy (jeśli nie jest jeszcze obecny) z hasłem mysecret i zostanie zaimportowany certyfikat pośredniego ośrodka CA. Zdefiniowałem również dostawcę BouncyCastle, gdzie można go znaleźć w moim systemie plików i formacie magazynu kluczy. Wykonaj to polecenie dla każdego certyfikatu w łańcuchu.
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
Sprawdź, czy certyfikaty zostały poprawnie zaimportowane do magazynu kluczy:
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
Powinien wyprowadzić cały łańcuch:
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
Teraz możesz skopiować magazyn kluczy jako surowiec w aplikacji na Androida pod res/raw/
Użyj magazynu kluczy w swojej aplikacji
Przede wszystkim musimy stworzyć niestandardowego Apache HttpClient, który używa naszego magazynu kluczy do połączeń HTTPS:
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);
}
}
}
Stworzyliśmy nasz niestandardowy HttpClient, teraz możemy go używać do bezpiecznych połączeń. Na przykład, gdy wykonujemy wywołanie GET do zasobu REST:
// 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();
Otóż to ;)