webentwicklung-frage-antwort-db.com.de

CertPathValidatorException: Vertrauensanker für Zertifikatspfad nicht gefunden - Retrofit Android

Ich erstelle eine Android) - Anwendung, die https für die Kommunikation mit dem Server verwendet. Ich benutze retrofit und OkHttp, um Anfragen zu stellen. Diese Funktioniert gut für Standard-http -Anfragen. Die folgenden Schritte habe ich ausgeführt.

Schritt 1: Mit dem Befehl die Zertifikatsdatei vom Server abgerufen

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

Schritt 2: Konvertiert das Zertifikat mit den folgenden Befehlen in ein BKS-Format

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

Ich wurde nach dem Passwort gefragt und die Datei wurde erfolgreich erstellt.

Schritt 3:

Erstellen Sie einen OkHttpClient und verwenden Sie denselben für die Erstellung von https-Anforderungen

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;
}
}

Schritt 4:

RestAdapter muss erstellt werden

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

Aber schließlich, wenn die App ausgeführt wird, wirft es mich CertPathValidatorException : Trust anchor for certificate path not found. Bitte helfen Sie mir, das zu lösen. Vielen Dank.

Andere Fehlversuche: Ich habe versucht, das Zertifikat auf meinem Xperia Z2 zu installieren, und es heißt, dass die Datei installiert wurde, aber beim Ausführen der App wird dieselbe Ausnahme ausgelöst.

Fehlerprotokoll Hier ist das Fehlerprotokoll, das ich beim Ausführen erhalten habe ...

Fehlerprotokoll

Dort eingefügt, damit es leicht zu lesen ist.

52
Gowtham Raj

HAFTUNGSAUSSCHLUSS: Diese Antwort stammt vom Jul 2015 und verwendet Nachrüsten und OkHttp aus dieser Zeit.
Überprüfen Sie diesen Link für weitere Informationen zu Retrofit v2 und diesen für die aktuellen OkHttp-Methoden.

Okay, ich habe es mit Android Developers Guide zum Laufen gebracht.

Gerade als OP versuche ich Retrofit und OkHttp zu verwenden Stellen Sie eine Verbindung zu einem selbst signierten SSL-fähigen Server her.

Hier ist der Code, mit dem alles funktioniert (ich habe die try/catch-Blöcke entfernt):

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();
}

Um das Debuggen zu erleichtern, habe ich meinen RestAdapter-Erstellungsbefehlen .setLogLevel(RestAdapter.LogLevel.FULL) hinzugefügt und festgestellt, dass eine Verbindung hergestellt und die Antwort vom Server abgerufen wurde.

Alles, was es brauchte, war meine ursprüngliche .crt Datei, die in main/res/raw Gespeichert wurde. Die .crt-Datei , auch als Zertifikat bezeichnet, ist eine der beiden Dateien, die beim Erstellen eines Zertifikats mit openssl erstellt werden. Im Allgemeinen handelt es sich um eine CRT- oder CERT-Datei, während es sich bei der anderen um eine KEY-Datei handelt.

Afaik, die .crt-Datei ist Ihr öffentlicher Schlüssel und die .key-Datei ist Ihr privater Schlüssel.

Wie ich sehen kann, haben Sie bereits eine . Cert -Datei, die identisch ist. Versuchen Sie also, sie zu verwenden.


PS: Für diejenigen, die es in Zukunft lesen und nur ein .pem haben Datei, laut diese Antwort , brauchen Sie nur das eine in das andere zu konvertieren:

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

PS²: Für diejenigen, die überhaupt keine Datei haben, können Sie den öffentlichen Schlüssel (aka Zertifikat) mit dem folgenden Befehl (bash) extrahieren von jedem Server:

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

Ersetzen Sie einfach den your.server.com Und den Port (falls es sich nicht um Standard-HTTPS handelt) und wählen Sie einen gültigen Pfad für die zu erstellende Ausgabedatei.

86
Paulo Avelar
 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);
    }
}

Weitere Informationen finden Sie unter https://mobikul.com/Android-retrofit-handling-sslhandshakeexception/

20
vishnuc156

Ich verwende Retrofit nicht und für OkHttp ist hier die einzige Lösung für selbstsignierte Zertifikate, die für mich funktioniert hat:

  1. Holen Sie sich ein Zertifikat von unserer Site, wie in Gowthams Frage beschrieben, und fügen Sie es in das res/raw Verzeichnis des Projekts ein:

    echo -n | openssl s_client -connect elkews.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ./res/raw/elkews_cert.crt
    
  2. Verwenden Sie Paulo answer , um ssl factory einzustellen (heutzutage mit OkHttpClient.Builder ()), aber ohne RestAdapter Schaffung.

  3. Fügen Sie dann Folgendes hinzu, um Folgendes zu beheben: SSLPeerUnverifiedException: Hostname nicht verifiziert

Das Ende von Paulos Code (nach sslContext Initialisierung), das für mich funktioniert, sieht also folgendermaßen aus:

...
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();
9
goRGon

Retrofit 2.3.

    // 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();
7
Gowsik K C

Sie konvertieren Cert in BKS Keystore, warum verwenden Sie nicht .cert direkt von 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());
3
moonzai

Wenn Sie ein Zertifikat haben, das Sie bereitstellen können, aber nur wenige Webservices nicht über das Zertifikat verfügen, folgen Sie bitte den nachstehenden Anweisungen.

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

    // 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);
    okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
    // creating a RestAdapter using the custom client
    return new RestAdapter.Builder()
            .setEndpoint(UrlRepository.API_BASE)
            .setClient(new OkClient(okHttpClient))
            .build();
3
Sampath Kumar

Hier ist die Kotlin-Version.
Danke :)

         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
            }
        }
0
hanyluv

Nach langem Nachforschen und zu tiefem Graben fand ich die Lösung für das Anheften von Zertifikaten in Android und ja, es unterscheidet sich von iOS, wo wir ein Zertifikat selbst benötigen, aber in Android) Wir brauchen nur einen Hash-Pin und das wars.

Wie erhalte ich die Hash-PIN für das Zertifikat?

Verwenden Sie zunächst nur einen falschen Hash-Pin und Ihre Java Klasse wird einen Fehler mit den richtigen Hash-Pins oder der richtigen Pin-Kette auslösen. Kopieren Sie einfach den Code und fügen Sie ihn in Ihren Code ein.

Diese Lösung hat mein Problem behoben: https://stackoverflow.com/a/45853669/34480

0
Bajrang Hudda

Implementierung in Kotlin: 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

}

0
Ahamed Mujeeb