web-dev-qa-db-fra.com

CertPathValidatorException: L'ancre de confiance pour le chemin du certificat n'a pas été trouvée - Retrofit Android

Je crée une application Android qui utilise https pour la communication avec le serveur. J'utilise retrofit et OkHttp pour effectuer des requêtes. fonctionne bien pour les requêtes standard http. Voici les étapes que j'ai suivies.

Étape 1: a acquis le fichier de certification du serveur à l'aide de la commande

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

Étape 2: a converti le certificat au format BKS à l'aide des commandes suivantes.

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

Il m'a demandé un mot de passe et le fichier a été créé avec succès.

Étape 3:

Créer un OkHttpClient et utiliser le même pour faire des requêtes 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;
}
}

Étape 4:

RestAdapter doit être créé

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

Mais finalement, quand je lance l'application, elle me jette CertPathValidatorException : Trust anchor for certificate path not found. S'il vous plaît aidez-moi à résoudre ce problème. Je vous remercie.

Autres tentatives d'échec: J'ai essayé d'installer le certificat dans mon Xperia Z2 et il indique que le fichier a été installé, mais que lorsque j'exécute l'application, la même exception est levée.

Journal des erreurs Voici le journal des erreurs que j'ai obtenu lors de l'exécution ...

Journal des erreurs

Collé là pour qu'il soit facile à lire.

52
Gowtham Raj

AVERTISSEMENT: cette réponse provient de juil 2015 et utilise Retrofit et OkHttp à partir de ce moment.
Cochez ce lien pour plus d’informations sur Retrofit v2 et celui-ci pour les méthodes OkHttp actuelles.

D'accord, je l'ai obtenu en utilisant Guide du développeur Android .

Tout comme OP, j'essaie d'utiliser Retrofit et OkHttp pour connectez-vous à un serveur SSL auto-signé.

Voici le code qui fait fonctionner les choses (j'ai supprimé les blocs 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();
}

Pour aider au débogage, j'ai également ajouté .setLogLevel(RestAdapter.LogLevel.FULL) à mes commandes de création RestAdapter et je pouvais le voir se connecter et obtenir la réponse du serveur.

Tout ce que cela a pris a été mon fichier original . Crt enregistré dans main/res/raw. Le fichier . Crt , également appelé certificat, est l'un des deux fichiers créés lors de la création d'un certificat à l'aide de openssl. Généralement, il s'agit d'un fichier .crt ou .cert, tandis que l'autre est un fichier .key.

Après tout, le fichier .crt est votre clé publique et le fichier .key est votre clé privée.

Comme je peux le voir, vous avez déjà un fichier . Cert , qui est identique, essayez donc de l’utiliser.


PS: Pour ceux qui le liront plus tard et qui n’ont qu’un . Pem fichier, selon cette réponse , vous avez seulement besoin de cela pour convertir l'un en l'autre:

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

PS²: Pour ceux qui ne possèdent aucun fichier, vous pouvez utiliser la commande suivante (bash) pour extraire la clé publique (ou certificat). depuis n'importe quel serveur:

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

Il suffit de remplacer le your.server.com Et le port (s'il ne s'agit pas du protocole HTTPS standard) et de choisir un chemin d'accès valide pour la création de votre fichier de sortie.

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

Pour plus de détails, visitez https://mobikul.com/Android-retrofit-handling-sslhandshakeexception/

20
vishnuc156

Je n'utilise pas Retrofit et pour OkHttp , voici la seule solution de certificat auto-signé qui a fonctionné pour moi:

  1. Obtenez un certificat de notre site comme dans la question de Gowtham et mettez-le dans res/raw dir du projet:

    echo -n | openssl s_client -connect elkews.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ./res/raw/elkews_cert.crt
    
  2. Utilisez Paulo answer pour définir la fabrique ssl (de nos jours, utilisez OkHttpClient.Builder ()) mais sans RestAdapter création.

  3. Ajoutez ensuite le suivant solution à corriger: SSLPeerUnverifiedException: nom d'hôte non vérifié

Ainsi, la fin du code de Paulo (après l'initialisation sslContext) qui fonctionne pour moi ressemble à ce qui suit:

...
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

Vous convertissez cert en BKS Keystore, pourquoi n'utilisez-vous pas .cert _ directement, depuis 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

Si vous avez un certificat, vous pouvez le fournir, mais peu de services Web n’auront pas ce certificat, veuillez suivre les instructions ci-dessous.

 // 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

Voici la version Kotlin.
Merci :)

         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

Après une longue recherche et des recherches trop approfondies, j’ai trouvé la solution du certificat: Android et oui, c’est différent d’iOS où nous avons besoin d’un certificat mais Android nous avons juste besoin d'une broche de hachage et c'est tout.

Comment obtenir une broche de hachage pour un certificat?

Initialement, utilisez simplement une mauvaise goupille de hachage et votre classe Java émettra une erreur avec les broches de hachage ou la chaîne de broches correctes, copiez-les simplement et collez-les dans votre code.

Cette solution a résolu mon problème: https://stackoverflow.com/a/45853669/34480

0
Bajrang Hudda

Mise en œuvre dans 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