akceptowanie połączeń HTTPS z certyfikatami z podpisem własnym

153

Próbuję nawiązywać połączenia HTTPS, używając HttpClientlib, ale problem polega na tym, że certyfikat nie jest podpisany przez uznany urząd certyfikacji (CA), taki jak Verisign , GlobalSIgn itp., Wymieniony na zestawie zaufanych certyfikatów Androida, Ciągle dostaję javax.net.ssl.SSLException: Not trusted server certificate.

Widziałem rozwiązania, w których po prostu akceptujesz wszystkie certyfikaty, ale co jeśli chcę zapytać użytkownika?

Chcę uzyskać okno dialogowe podobne do okna przeglądarki, pozwalające użytkownikowi zdecydować, czy kontynuować, czy nie. Najlepiej, gdybym korzystał z tego samego magazynu certyfikatów co przeglądarka. Jakieś pomysły?

Morten
źródło
To zaakceptowane rozwiązanie działało dla mnie
Venkatesh

Odpowiedzi:

171

Pierwszą rzeczą, którą musisz zrobić, jest ustawienie poziomu weryfikacji. Takie poziomy to nie tyle:

  • ALLOW_ALL_HOSTNAME_VERIFIER
  • BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
  • STRICT_HOSTNAME_VERIFIER

Chociaż metoda setHostnameVerifier () jest przestarzała dla nowej biblioteki Apache, ale dla wersji w Android SDK jest normalna. Więc bierzemy ALLOW_ALL_HOSTNAME_VERIFIERi ustawiamy to w fabryce metody SSLSocketFactory.setHostnameVerifier().

Następnie musisz ustawić naszą fabrykę dla protokołu na https. Aby to zrobić, po prostu wywołaj SchemeRegistry.register()metodę.

Następnie musisz utworzyć plik DefaultHttpClientz SingleClientConnManager. Również w poniższym kodzie możesz zobaczyć, że domyślnie użyje również naszej flagi ( ALLOW_ALL_HOSTNAME_VERIFIER) według metodyHttpsURLConnection.setDefaultHostnameVerifier()

Poniższy kod działa u mnie:

HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;

DefaultHttpClient client = new DefaultHttpClient();

SchemeRegistry registry = new SchemeRegistry();
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
registry.register(new Scheme("https", socketFactory, 443));
SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry);
DefaultHttpClient httpClient = new DefaultHttpClient(mgr, client.getParams());

// Set verifier     
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);

// Example send http request
final String url = "https://encrypted.google.com/";
HttpPost httpPost = new HttpPost(url);
HttpResponse response = httpClient.execute(httpPost);
Nikolay Moskvin
źródło
6
Niestety nie mogę sprawić, by ten kod działał, nadal otrzymuję komunikat „Niezaufany certyfikat serwera”. Czy są jakieś dodatkowe uprawnienia, które muszę ustawić, aby to działało?
Juriy
1
Czy ten kod nie akceptuje tylko wszystkich certyfikatów? Potrzebuję wyskakującego okienka, aby to zaakceptować.
Morten
3
Używam, org.apache.http.conn.ssl.SSLSocketFactorydlaczego chcę używać javax.net.ssl.HttpsURLConnection?
Ktoś gdzieś
9
Czy możesz wyjaśnić, dlaczego ten kod jest lepszy niż całkowite wyłączenie weryfikacji certyfikatu? Nie jestem zaznajomiony z interfejsem API ssl w Androidzie, ale na pierwszy rzut oka wydaje się to całkowicie niebezpieczne dla aktywnych atakujących.
CodesInChaos
3
Sugerowałbym użycie ThreadSafeClientConnManager zamiast SingleClientConnManager
Farma
124

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 :

  1. Pobierz wszystkie wymagane certyfikaty (główne i pośrednie CA)
  2. Utwórz magazyn kluczy za pomocą narzędzia keytool i dostawcy BouncyCastle i zaimportuj certyfikaty
  3. 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 ;)

saksos
źródło
8
Jest to przydatne tylko do uzyskiwania certyfikatów przed wysłaniem aplikacji. Naprawdę nie pomaga użytkownikom w akceptowaniu własnych certyfikatów. do aplikacji
Fuzzy
Cześć wszystkim, czy ktoś może mi powiedzieć o procesie walidacji magazynu kluczy z zaufanym magazynem dla powyższej implementacji? z góry dzięki ..
andriod_testing
To działało dobrze… ale teraz mam problem z ponownym kluczem klucza na serwerze. Wydaje się dziwne, że za każdym razem, gdy aktualizuję certyfikat na serwerze, należy również zaktualizować magazyn po stronie klienta. Musi być lepszy sposób: |
bpn
Odpowiedź Gr8, sugerowałbym użycie ThreadSafeClientConnManager zamiast SingleClientConnManager
Farma
Dodałem /res/raw/mykeystore.bks, chociaż nie mogę rozwiązać odniesienia do niego. jak to rozwiązać?
uniruddh
16

Jeśli masz niestandardowy / samopodpisany certyfikat na serwerze, którego nie ma na urządzeniu, możesz użyć poniższej klasy, aby go załadować i używać po stronie klienta w systemie Android:

Umieść *.crtplik certyfikatu w /res/raw, aby był dostępny zR.raw.*

Użyj poniższej klasy, aby uzyskać HTTPClientlub, HttpsURLConnectionktóra będzie miała fabrykę gniazd używającą tego certyfikatu:

package com.example.customssl;

import android.content.Context;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;

public class CustomCAHttpsProvider {

    /**
     * Creates a {@link org.apache.http.client.HttpClient} which is configured to work with a custom authority
     * certificate.
     *
     * @param context       Application Context
     * @param certRawResId  R.raw.id of certificate file (*.crt). Should be stored in /res/raw.
     * @param allowAllHosts If true then client will not check server against host names of certificate.
     * @return Http Client.
     * @throws Exception If there is an error initializing the client.
     */
    public static HttpClient getHttpClient(Context context, int certRawResId, boolean allowAllHosts) throws Exception {


        // build key store with ca certificate
        KeyStore keyStore = buildKeyStore(context, certRawResId);

        // init ssl socket factory with key store
        SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keyStore);

        // skip hostname security check if specified
        if (allowAllHosts) {
            sslSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier());
        }

        // basic http params for client
        HttpParams params = new BasicHttpParams();

        // normal scheme registry with our ssl socket factory for "https"
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));

        // create connection manager
        ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);

        // create http client
        return new DefaultHttpClient(cm, params);
    }

    /**
     * Creates a {@link javax.net.ssl.HttpsURLConnection} which is configured to work with a custom authority
     * certificate.
     *
     * @param urlString     remote url string.
     * @param context       Application Context
     * @param certRawResId  R.raw.id of certificate file (*.crt). Should be stored in /res/raw.
     * @param allowAllHosts If true then client will not check server against host names of certificate.
     * @return Http url connection.
     * @throws Exception If there is an error initializing the connection.
     */
    public static HttpsURLConnection getHttpsUrlConnection(String urlString, Context context, int certRawResId,
                                                           boolean allowAllHosts) throws Exception {

        // build key store with ca certificate
        KeyStore keyStore = buildKeyStore(context, certRawResId);

        // Create a TrustManager that trusts the CAs in our KeyStore
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);

        // Create an SSLContext that uses our TrustManager
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), null);

        // Create a connection from url
        URL url = new URL(urlString);
        HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
        urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());

        // skip hostname security check if specified
        if (allowAllHosts) {
            urlConnection.setHostnameVerifier(new AllowAllHostnameVerifier());
        }

        return urlConnection;
    }

    private static KeyStore buildKeyStore(Context context, int certRawResId) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
        // init a default key store
        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);

        // read and add certificate authority
        Certificate cert = readCert(context, certRawResId);
        keyStore.setCertificateEntry("ca", cert);

        return keyStore;
    }

    private static Certificate readCert(Context context, int certResourceId) throws CertificateException, IOException {

        // read certificate resource
        InputStream caInput = context.getResources().openRawResource(certResourceId);

        Certificate ca;
        try {
            // generate a certificate
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            ca = cf.generateCertificate(caInput);
        } finally {
            caInput.close();
        }

        return ca;
    }

}

Kluczowe punkty:

  1. Certificateobiekty są generowane z .crtplików.
  2. Tworzona KeyStorejest wartość domyślna .
  3. keyStore.setCertificateEntry("ca", cert)dodaje certyfikat do magazynu kluczy pod aliasem „ca”. Modyfikujesz kod, aby dodać więcej certyfikatów (pośredni urząd certyfikacji itp.).
  4. Głównym celem jest wygenerowanie pliku, SSLSocketFactoryktóry może być następnie użyty przez HTTPClientlub HttpsURLConnection.
  5. SSLSocketFactory można skonfigurować dalej, na przykład w celu pominięcia weryfikacji nazwy hosta itp.

Więcej informacji na: http://developer.android.com/training/articles/security-ssl.html

SD
źródło
Gdzie mogę pobrać .crtpliki? Pobrać z serwera?
zionpi
@zionpi Plik certyfikatu będzie taki sam, jak używany przez serwer z włączonym TLS, z którym się łączysz.
SD
Dzięki! To było takie proste!
kapil thadani
@SD Jak mogę używać pliku .P12 zamiast .crt?
Rakesh R Nair
Mam podobne wątpliwości, że jesteś zimny, pomóż stackoverflow.com/questions/57389622/ ...
StezPet
8

Byłem sfrustrowany próbą połączenia mojej aplikacji na Androida z usługą RESTful przy użyciu protokołu HTTPS. Byłem też trochę zirytowany wszystkimi odpowiedziami, które sugerowały całkowite wyłączenie sprawdzania certyfikatów. Jeśli to zrobisz, jaki jest sens HTTPS?

Po dłuższym googlowaniu na ten temat w końcu znalazłem to rozwiązanie, w którym nie są potrzebne zewnętrzne jary, tylko Android API. Podziękowania dla Andrew Smitha, który opublikował go w lipcu 2014 roku

 /**
 * Set up a connection to myservice.domain using HTTPS. An entire function
 * is needed to do this because myservice.domain has a self-signed certificate.
 * 
 * The caller of the function would do something like:
 * HttpsURLConnection urlConnection = setUpHttpsConnection("https://littlesvr.ca");
 * InputStream in = urlConnection.getInputStream();
 * And read from that "in" as usual in Java
 * 
 * Based on code from:
 * https://developer.android.com/training/articles/security-ssl.html#SelfSigned
 */
public static HttpsURLConnection setUpHttpsConnection(String urlString)
{
    try
    {
        // Load CAs from an InputStream
        // (could be from a resource or ByteArrayInputStream or ...)
        CertificateFactory cf = CertificateFactory.getInstance("X.509");

        // My CRT file that I put in the assets folder
        // I got this file by following these steps:
        // * Go to https://littlesvr.ca using Firefox
        // * Click the padlock/More/Security/View Certificate/Details/Export
        // * Saved the file as littlesvr.crt (type X.509 Certificate (PEM))
        // The MainActivity.context is declared as:
        // public static Context context;
        // And initialized in MainActivity.onCreate() as:
        // MainActivity.context = getApplicationContext();
        InputStream caInput = new BufferedInputStream(MainActivity.context.getAssets().open("littlesvr.crt"));
        Certificate ca = cf.generateCertificate(caInput);
        System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());

        // Create a KeyStore containing our trusted CAs
        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);

        // Create a TrustManager that trusts the CAs in our KeyStore
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);

        // Create an SSLContext that uses our TrustManager
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, tmf.getTrustManagers(), null);

        // Tell the URLConnection to use a SocketFactory from our SSLContext
        URL url = new URL(urlString);
        HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
        urlConnection.setSSLSocketFactory(context.getSocketFactory());

        return urlConnection;
    }
    catch (Exception ex)
    {
        Log.e(TAG, "Failed to establish SSL connection to server: " + ex.toString());
        return null;
    }
}

To działało dobrze dla mojej makiety aplikacji.

Gonzalo Fernández
źródło
X509Certyfikat, który powinienem zaimportować java lub javax?
Siddharth
Sprowadziłemimport java.security.cert.X509Certificate;
Gonzalo Fernández
Dzięki za to. To naprawdę działa i jest proste
Anuradhe Dilshan
6

Najlepsza odpowiedź mi nie pomogła. Po pewnym dochodzeniu znalazłem wymagane informacje na temat „Android Developer”: https://developer.android.com/training/articles/security-ssl.html#SelfSigned

Utworzenie pustej implementacji X509TrustManager załatwiło sprawę:

private static class MyTrustManager implements X509TrustManager
{

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType)
         throws CertificateException
    {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType)
        throws CertificateException
    {
    }

    @Override
    public X509Certificate[] getAcceptedIssuers()
    {
        return null;
    }

}

...

HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
try
{
    // Create an SSLContext that uses our TrustManager
    SSLContext context = SSLContext.getInstance("TLS");
    TrustManager[] tmlist = {new MyTrustManager()};
    context.init(null, tmlist, null);
    conn.setSSLSocketFactory(context.getSocketFactory());
}
catch (NoSuchAlgorithmException e)
{
    throw new IOException(e);
} catch (KeyManagementException e)
{
    throw new IOException(e);
}
conn.setRequestMethod("GET");
int rcode = conn.getResponseCode();

Należy pamiętać, że ta pusta implementacja TustManager to tylko przykład, a używanie jej w środowisku produkcyjnym spowodowałoby poważne zagrożenie bezpieczeństwa!

Markus Lenger
źródło
1
po prostu fyi - nie wiem, czy tak było w tamtym czasie, ale wydaje się, że teraz zdecydowanie zniechęcają do tego podejścia (patrz uwaga)
Saik Caskey
6

Google zaleca używanie Android Volley do połączeń HTTP / HTTPS , ponieważ HttpClientjest on przestarzały. Znasz więc właściwy wybór :).

A także NIGDY NUKE certyfikatów SSL (NIGDY !!!).

Nuke SSL Certificates jest całkowicie sprzeczny z celem SSL, którym jest promowanie bezpieczeństwa . Nie ma sensu używać SSL, jeśli planujesz zbombardować wszystkie otrzymane certyfikaty SSL. Lepszym rozwiązaniem byłoby niestosowanie SSL lub lepszym rozwiązaniem byłoby utworzenie niestandardowej TrustManageraplikacji + przy użyciu Android Volley dla połączeń HTTP / HTTPS.

Oto Gist, który utworzyłem, z podstawową aplikacją logowania, wykonującą połączenia HTTPS, przy użyciu certyfikatu z podpisem własnym po stronie serwera, zaakceptowanego w aplikacji.

Oto kolejna treść, która może pomóc w tworzeniu samopodpisanych certyfikatów SSL do konfiguracji na serwerze, a także korzystania z certyfikatu w aplikacji. Bardzo ważne: musisz skopiować plik .crt, który został wygenerowany przez powyższy skrypt, do katalogu „raw” z projektu Android.

ivanleoncz
źródło
Witaj Ivan, nigdy nie pracowałem z certyfikatami SSL. Czy zechciałbyś trochę rozwinąć, jak mogę uzyskać plik .crt?
żwawo
Cześć Jively! Widzę. Tak oczywiście. Ale najpierw, czy mógłbyś rzucić okiem na drugi Gist, o którym wspomniałem powyżej? Umieściłem dwa pliki na tym Gist: jeden to plik używany przez skrypt, a drugi to sam skrypt, który używa binarnego "openssl" w celu odczytania pliku, a następnie buduje plik zawierający Certyfikat SSL ( .crt). Daj mi znać, jeśli wszystko zrozumiałeś. Pozdrowienia :).
ivanleoncz
Hmm tak, przyjrzałem się tym dwóm streszczeniom, ale naprawdę nie rozumiem, jak ich używam?
wesoło
4

Oto jak możesz dodać dodatkowe certyfikaty do swojego magazynu kluczy, aby uniknąć tego problemu: Ufanie wszystkim certyfikatom przy użyciu HttpClient przez HTTPS

Nie będzie monitował użytkownika, tak jak pytasz, ale zmniejszy prawdopodobieństwo, że użytkownik napotka błąd „Niezaufany certyfikat serwera”.

emmby
źródło
Tylko w celach testowych nie możesz opublikować aplikacji w Sklepie Play z tą sztuczką, ponieważ zostanie odrzucona
ariel
3

Najprostszy sposób na utworzenie certyfikatu SSL

Otwórz Firefoksa (przypuszczam, że jest to również możliwe z Chrome, ale jest mi łatwiej z FF)

Odwiedź swoją witrynę programistyczną, korzystając z certyfikatu SSL z podpisem własnym.

Kliknij certyfikat (obok nazwy strony)

Kliknij „Więcej informacji”

Kliknij „Wyświetl certyfikat”

Kliknij „Szczegóły”

Kliknij „Eksportuj ...”

Wybierz „Certyfikat X.509 z łańcuchem (PEM)”, wybierz folder i nazwę, aby go zapisać i kliknij „Zapisz”

Przejdź do wiersza poleceń, do katalogu, w którym pobrałeś plik pem i wykonaj polecenie „openssl x509 -inform PEM -outform DM -in .pem -out .crt”

Skopiuj plik .crt do katalogu głównego folderu / sdcard na urządzeniu z systemem Android W urządzeniu z systemem Android: Ustawienia> Zabezpieczenia> Zainstaluj z pamięci.

Powinien wykryć certyfikat i umożliwić dodanie go do urządzenia. Przejdź do witryny programistycznej.

Za pierwszym razem powinien poprosić o potwierdzenie wyjątku bezpieczeństwa. To wszystko.

Certyfikat powinien działać z każdą przeglądarką zainstalowaną na Twoim Androidzie (Browser, Chrome, Opera, Dolphin ...)

Pamiętaj, że jeśli obsługujesz swoje pliki statyczne z innej domeny (wszyscy jesteśmy sukami szybkości stron), musisz również dodać certyfikat dla tej domeny.

Mani kandan
źródło
2

Napisałem małą bibliotekę ssl-utils-android, aby ufać konkretnemu certyfikatowi na Androida.

Możesz po prostu załadować dowolny certyfikat, podając nazwę pliku z katalogu zasobów.

Stosowanie:

OkHttpClient client = new OkHttpClient();
SSLContext sslContext = SslUtils.getSslContextForCertificateFile(context, "BPClass2RootCA-sha2.cer");
client.setSslSocketFactory(sslContext.getSocketFactory());
klimat
źródło
1

Żadna z tych poprawek nie działała na mojej platformie deweloperskiej ukierunkowanej na SDK 16, wydanie 4.1.2, więc znalazłem obejście.

Moja aplikacja przechowuje dane na serwerze przy użyciu „ http://www.example.com/page.php?data=somedata

Ostatnio page.php został przeniesiony na „ https://www.secure-example.com/page.php ” i ciągle otrzymuję „javax.net.ssl.SSLException: Not trust server certificate”.

Zamiast akceptować wszystkie certyfikaty tylko dla jednej strony, zaczynając od tego przewodnika rozwiązałem swój problem pisząc własną stronę page.php opublikowaną pod adresem " http://www.example.com/page.php "

<?php

caronte ("https://www.secure-example.com/page.php");

function caronte($url) {
    // build curl request
    $ch = curl_init();
    foreach ($_POST as $a => $b) {
        $post[htmlentities($a)]=htmlentities($b);
    }
    curl_setopt($ch, CURLOPT_URL,$url);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS,http_build_query($post));

    // receive server response ...
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $server_output = curl_exec ($ch);
    curl_close ($ch);

    echo $server_output;
}

?>
Rudy
źródło
1

19 stycznia 2020 Certyfikat z podpisem własnym POPRAWKA PROBLEMU:

Aby odtworzyć wideo, obraz, wywołać usługę internetową dla dowolnego certyfikatu z podpisem własnym lub połączyć się z dowolnym niezabezpieczonym adresem URL, po prostu wywołaj tę metodę przed wykonaniem jakiejkolwiek czynności, rozwiąże to problem dotyczący problemu z certyfikatem:

KOD KOTLIN

  private fun disableSSLCertificateChecking() {
        val hostnameVerifier = object: HostnameVerifier {
            override fun verify(s:String, sslSession: SSLSession):Boolean {
                return true
            }
        }
        val trustAllCerts = arrayOf<TrustManager>(object: X509TrustManager {
            override fun getAcceptedIssuers(): Array<X509Certificate> {
                TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
            }

            //val acceptedIssuers:Array<X509Certificate> = null
            @Throws(CertificateException::class)
            override fun checkClientTrusted(arg0:Array<X509Certificate>, arg1:String) {// Not implemented
            }
            @Throws(CertificateException::class)
            override fun checkServerTrusted(arg0:Array<X509Certificate>, arg1:String) {// Not implemented
            }
        })
        try
        {
            val sc = SSLContext.getInstance("TLS")
            sc.init(null, trustAllCerts, java.security.SecureRandom())
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory())
            HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier)
        }
        catch (e: KeyManagementException) {
            e.printStackTrace()
        }
        catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
        }
    }
Shobhakar Tiwari
źródło
0

Może to będzie pomocne ... działa na klientach java używających certyfikatów z podpisem własnym (nie ma sprawdzenia certyfikatu). Bądź ostrożny i używaj go tylko w przypadku programowania, ponieważ nie jest to wcale bezpieczne!

Jak zignorować błędy certyfikatów SSL w Apache HttpClient 4.0

Mam nadzieję, że będzie działać na Androidzie po prostu dodając bibliotekę HttpClient ... powodzenia !!

EmP
źródło
1
Nie, nie działa na Androidzie, ponieważ opiera się na przestarzałych metodach, których nie ma w wariancie Androida :-(
kellyfj