CertPathValidatorException: nie znaleziono kotwicy zaufania dla ścieżki certyfikatu - Retrofit Android

Tworzę aplikację na Androida, która wykorzystuje https do komunikacji z serwerem. Używam retrofit i OkHttp do składania wniosków. Działa to dobrze w przypadku standardowych zapytań http. Oto kroki, które wykonałem.

Krok 1: Pozyskanie pliku cert z serwera za pomocą polecenia

echo -n | openssl s_client -connect api.****.tk:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > gtux.cert

Krok 2: Konwertowanie cert do formatu BKS za pomocą następujących poleceń

keytool -importcert -v -trustcacerts -file "gtux.cert" -alias imeto_alias -keystore "my_keystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-146.jar" -storetype BKS

Poprosił mnie o hasło i plik został pomyślnie utworzony.

Krok 3 :

Utwórz OkHttpClient i użyj tego samego do tworzenia żądań https

public class MySSLTrust {
public static OkHttpClient trustcert(Context context){
    OkHttpClient okHttpClient = new OkHttpClient();
    try {
        KeyStore ksTrust = KeyStore.getInstance("BKS");
        InputStream instream = context.getResources().openRawResource(R.raw.my_keystore);
        ksTrust.load(instream, "secret".toCharArray());
        // TrustManager decides which certificate authorities to use.
        TrustManagerFactory tmf = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ksTrust);
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), null);
        okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
    } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | KeyManagementException e) {
        e.printStackTrace();
    }
    return okHttpClient;
}
}

Krok 4:

RestAdapter musi zostać utworzony

RestAdapter.Builder()
.setRequestInterceptor(intercept)
.setEndpoint("https://api.****.tk")
.setClient(new OkClient(this))
.setLogLevel(RestAdapter.LogLevel.FULL)
.setLog(new AndroidLog("RETROFIT"))
.build();

Ale w końcu po uruchomieniu aplikacji rzuca mnie CertPathValidatorException : Trust anchor for certificate path not found. Proszę, pomóż mi to rozwiązać. Dziękuję.

Inne próby niepowodzenia: Próbowałem zainstalować certyfikat w moim Xperia Z2 i mówi, że plik został zainstalowany, ale kiedy uruchamiam aplikację ten sam wyjątek jest wyrzucany.

Dziennik Błędów Oto dziennik błędów, który dostałem podczas wykonywania...

Dziennik Błędów

Wklejone tam, aby było łatwe do odczytania..

Author: Gowtham Raj, 2015-03-26

8 answers

Uwaga: ta odpowiedź pochodzi zJul 2015 i używaRetrofit iOkHttp od tego czasu.
Sprawdź ten link aby uzyskać więcej informacji na temat modernizacji v2 i ten dla obecnych metod OkHttp.

Okay, mam go działa Android Developers guide.

Tak samo jak OP, próbuję użyćRetrofit i OkHttp aby połączyć się z własnoręcznie podpisanym SSL-enabled serwer.

Oto kod, który działa (usunąłem bloki try/catch):

public static RestAdapter createAdapter(Context context) {
  // loading CAs from an InputStream
  CertificateFactory cf = CertificateFactory.getInstance("X.509");
  InputStream cert = context.getResources().openRawResource(R.raw.my_cert);
  Certificate ca;
  try {
    ca = cf.generateCertificate(cert);
  } finally { cert.close(); }

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

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

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

  // creating an OkHttpClient that uses our SSLSocketFactory
  OkHttpClient okHttpClient = new OkHttpClient();
  okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());

  // creating a RestAdapter that uses this custom client
  return new RestAdapter.Builder()
              .setEndpoint(UrlRepository.API_BASE)
              .setClient(new OkClient(okHttpClient))
              .build();
}

Aby pomóc w debugowaniu, dodałem również .setLogLevel(RestAdapter.LogLevel.FULL) do moich poleceń tworzenia Restadaptera i widziałem, jak łączy się i otrzymuje odpowiedź z serwera.

Wystarczyło moje oryginalne .plik crt zapisany w main/res/raw. .plik crt , zwany także certyfikatem, jest jednym z dwóch plików utworzonych podczas tworzenia certyfikatu za pomocą openssl. Ogólnie rzecz biorąc, jest to .crt lub .cert plik, podczas gdy drugi jest a .plik klucza.

Afaik, the .plik crt to twój klucz publiczny iplik klucza jest Twoim kluczem prywatnym.

Jak widzę, masz już .plik cert , który jest taki sam, więc spróbuj go użyć.


PS: dla tych, którzy przeczytają ją w przyszłości i mają tylko .pem Plik, zgodnie z ta odpowiedź , wystarczy, że przekonwertujesz jeden na drugi:

openssl x509 -outform der -in your-cert.pem -out your-cert.crt

PS2: dla tych, którzy nie ma żadnego pliku, możesz użyć następującego polecenia (bash), aby wyodrębnić klucz publiczny (aka certificate) z dowolnego serwera: {]}

echo -n | openssl s_client -connect your.server.com:443 | \
  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/my_cert.crt

Wystarczy zastąpić your.server.com i port (jeśli nie jest to standardowy HTTPS) i wybrać prawidłową ścieżkę dla pliku wyjściowego do utworzenia.

 89
Author: Paulo Avelar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-11 03:05:29
 Use the below code to solve the CertPathValidatorException issue.


 Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(YOUR_BASE_URL)
        .client(getUnsafeOkHttpClient().build())
        .build();


  public static OkHttpClient.Builder getUnsafeOkHttpClient() {

    try {
        // Create a trust manager that does not validate certificate chains
        final TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                    }

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

                    @Override
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return new java.security.cert.X509Certificate[]{};
                    }
                }
        };

        // Install the all-trusting trust manager
        final SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

        // Create an ssl socket factory with our all-trusting manager
        final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
        builder.hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });
        return builder;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Więcej szczegółów na stronie https://mobikul.com/android-retrofit-handling-sslhandshakeexception/

 33
Author: vishnuc156,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-02 05:15:22

Nie używam Retrofit i dla OkHttp Oto jedyne rozwiązanie dla własnoręcznie podpisanego certyfikatu, które zadziałało dla mnie:

  1. Pobierz certyfikat z naszej strony jak w pytaniu Gowthama i umieść go w res/raw dir projektu:

    echo -n | openssl s_client -connect elkews.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ./res/raw/elkews_cert.crt
    
  2. Użyj Paulo answer aby ustawić fabrykę ssl (obecnie używając OkHttpClient.Builder () ) ale bez RestAdapter creation.

  3. Następnie dodaj następujący rozwiązanie do naprawy: SSLPeerUnverifiedException: Nazwa hosta nie została zweryfikowana

Więc koniec kodu (po sslContext initialization) który działa u mnie wygląda następująco:

...
OkHttpClient.Builder builder = new OkHttpClient.Builder().sslSocketFactory(sslContext.getSocketFactory());
builder.hostnameVerifier(new HostnameVerifier() {
  @Override
  public boolean verify(String hostname, SSLSession session) {
    return "secure.elkews.com".equalsIgnoreCase(hostname);
});
OkHttpClient okHttpClient = builder.build();
 10
Author: goRGon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-05 14:15:38

Retrofit 2.3.0

    // Load CAs from an InputStream
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");

    InputStream inputStream = context.getResources().openRawResource(R.raw.ssl_certificate); //(.crt)
    Certificate certificate = certificateFactory.generateCertificate(inputStream);
    inputStream.close();

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

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

    TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
    X509TrustManager x509TrustManager = (X509TrustManager) trustManagers[0];


    // Create an SSLSocketFactory that uses our TrustManager
    SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(null, new TrustManager[]{x509TrustManager}, null);
    sslSocketFactory = sslContext.getSocketFactory();

    //create Okhttp client
    OkHttpClient client = new OkHttpClient.Builder()
                .sslSocketFactory(sslSocketFactory,x509TrustManager)
                .build();

    Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(url)
                    .addConverterFactory(GsonConverterFactory.create())
                    .client(client)
                    .build();
 9
Author: Gowsik K C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-24 19:36:47

Oto Wersja Kotlina.
Dzięki:)

         fun unSafeOkHttpClient() :OkHttpClient.Builder {
            val okHttpClient = OkHttpClient.Builder()
            try {
                // Create a trust manager that does not validate certificate chains
                val trustAllCerts:  Array<TrustManager> = arrayOf(object : X509TrustManager {
                    override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?){}
                    override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
                    override fun getAcceptedIssuers(): Array<X509Certificate>  = arrayOf()
                })

                // Install the all-trusting trust manager
                val  sslContext = SSLContext.getInstance("SSL")
                sslContext.init(null, trustAllCerts, SecureRandom())

                // Create an ssl socket factory with our all-trusting manager
                val sslSocketFactory = sslContext.socketFactory
                if (trustAllCerts.isNotEmpty() &&  trustAllCerts.first() is X509TrustManager) {
                    okHttpClient.sslSocketFactory(sslSocketFactory, trustAllCerts.first() as X509TrustManager)
                    okHttpClient.hostnameVerifier { _, _ -> true }
                }

                return okHttpClient
            } catch (e: Exception) {
                return okHttpClient
            }
        }
 8
Author: hanyluv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-23 06:20:53

Konwertujesz cert na Keystore BKS, dlaczego nie używasz .cert bezpośrednio, z https://developer.android.com/training/articles/security-ssl.html :

CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream instream = context.getResources().openRawResource(R.raw.gtux_cert);
Certificate ca;
try {
    ca = cf.generateCertificate(instream);
} finally {
    caInput.close();
}

KeyStore kStore = KeyStore.getInstance(KeyStore.getDefaultType());
kStore.load(null, null);
kStore.setCertificateEntry("ca", ca);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm(););
tmf.init(kStore);

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);

okHttpClient.setSslSocketFactory(context.getSocketFactory());
 4
Author: moonzai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-02 11:22:07

Implementacja w Kotlinie: Retrofit 2.3.0

private fun getUnsafeOkHttpClient(mContext: Context) : 
OkHttpClient.Builder? {


var mCertificateFactory : CertificateFactory = 
CertificateFactory.getInstance("X.509")
var mInputStream = mContext.resources.openRawResource(R.raw.cert)
            var mCertificate : Certificate = mCertificateFactory.generateCertificate(mInputStream)
        mInputStream.close()
val mKeyStoreType = KeyStore.getDefaultType()
val mKeyStore = KeyStore.getInstance(mKeyStoreType)
mKeyStore.load(null, null)
mKeyStore.setCertificateEntry("ca", mCertificate)

val mTmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
val mTrustManagerFactory = TrustManagerFactory.getInstance(mTmfAlgorithm)
mTrustManagerFactory.init(mKeyStore)

val mTrustManagers = mTrustManagerFactory.trustManagers

val mSslContext = SSLContext.getInstance("SSL")
mSslContext.init(null, mTrustManagers, null)
val mSslSocketFactory = mSslContext.socketFactory

val builder = OkHttpClient.Builder()
builder.sslSocketFactory(mSslSocketFactory, mTrustManagers[0] as X509TrustManager)
builder.hostnameVerifier { _, _ -> true }
return builder

}

 1
Author: Ahamed Mujeeb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-02 08:37:28

Po długim reserchu i zbyt głębokim kopaniu znalazłem rozwiązanie przypinania certyfikatów w Androidzie i tak różni się od iOS, gdzie potrzebujemy samego certyfikatu, ale w Androidzie potrzebujemy tylko hash Pina i to wszystko.

Jak uzyskać hash pin dla certyfikatu?

Początkowo po prostu użyj niewłaściwego hash Pina, a twoja klasa java wyrzuci błąd z poprawnymi hash pinami lub łańcuchem pinów, po prostu skopiuj i wklej do kodu.

To rozwiązanie naprawiło mój problem : https://stackoverflow.com/a/45853669/3448003

 0
Author: Bajrang Hudda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-24 07:25:57