CertPathValidatorException : Trust anchor for certificate path not found - Retrofit Android

前端 未结 9 1996
庸人自扰
庸人自扰 2020-11-29 16:07

I am creating an android application which uses https for communication with the server. I am using retrofit and OkHttp for making req

相关标签:
9条回答
  • 2020-11-29 16:14

    Here is Kotlin version.
    Thanks you :)

             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 讨论(0)
  • 2020-11-29 16:21

    After a long reserch and digging too deep i found the solution of certificate pinning in android and yes its different from iOS where we need a certificate itself but in android we just need a hash pin and that's it.

    How to get hash pin for certificate?

    Initially just use a wrong hash pin and your java class will throw an error with correct hash pins or pin chain, just copy and paste into your code thats it.

    This solution fixed my problem : https://stackoverflow.com/a/45853669/3448003

    0 讨论(0)
  • 2020-11-29 16:27

    You are converting cert into BKS Keystore, why aren't you using .cert directly, from 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());
    
    0 讨论(0)
  • 2020-11-29 16:27

    Implementation 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 讨论(0)
  • 2020-11-29 16:30
     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);
        }
    }
    

    For more details visit https://mobikul.com/android-retrofit-handling-sslhandshakeexception/

    0 讨论(0)
  • 2020-11-29 16:31

    DISCLAIMER: this answer is from Jul 2015 and uses Retrofit and OkHttp from that time.
    Check this link for more info on Retrofit v2 and this one for the current OkHttp methods.

    Okay, I got it working using Android Developers guide.

    Just as OP, I'm trying to use Retrofit and OkHttp to connect to a self-signed SSL-enabled server.

    Here's the code that got things working (I've removed the try/catch blocks):

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

    To help in debugging, I also added .setLogLevel(RestAdapter.LogLevel.FULL) to my RestAdapter creation commands and I could see it connecting and getting the response from the server.

    All it took was my original .crt file saved in main/res/raw. The .crt file, aka the certificate, is one of the two files created when you create a certificate using openssl. Generally, it is a .crt or .cert file, while the other is a .key file.

    Afaik, the .crt file is your public key and the .key file is your private key.

    As I can see, you already have a .cert file, which is the same, so try to use it.


    PS: For those that read it in the future and only have a .pem file, according to this answer, you only need this to convert one to the other:

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

    PS²: For those that don't have any file at all, you can use the following command (bash) to extract the public key (aka certificate) from any server:

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

    Just replace the your.server.com and the port (if it is not standard HTTPS) and choose a valid path for your output file to be created.

    0 讨论(0)
提交回复
热议问题