【问题标题】:Disable SSL certificate check in retrofit library在改造库中禁用 SSL 证书检查
【发布时间】:2016-10-07 18:49:55
【问题描述】:

我在 android 中使用改造来连接服务器。

public class ApiClient {
    public static final String BASE_URL = "https://example.com/";
    private static Retrofit retrofit = null;

    public static Retrofit getClient() {
        if (retrofit==null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }
}

这是我的开发者。服务器,我想禁用证书检查。如何在这段代码中实现?

错误:javax.net.ssl.SSLHandshakeException:java.security.cert.CertPathValidatorException:找不到证书路径的信任锚。

【问题讨论】:

  • 如果您的开发服务器上没有 ssl 证书,为什么要使用 https 连接它?
  • @njzk2:这是一个自签名证书。并且 URL aoti 重定向到 https
  • 为什么不将它添加到您的受信任证书中?至少在您完成开发工作之前。
  • @RuchiraRandana:我该怎么做?
  • 我能想到的最简单的方法是给自己发送一封附有自签名证书的电子邮件。收到该电子邮件后,从您的 Android 设备打开并下载。然后选择下载的证书进行安装。它将显示一个提示,您只需按照它进行操作即可。如果安装成功,您可以在设备的“设置->可信凭据->用户”部分看到该证书。

标签: java android ssl kotlin retrofit2


【解决方案1】:

使用此类获取不安全的 Retrofit 实例。我已经包含了导入以避免混淆。

import java.security.cert.CertificateException;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import view.utils.AppConstants;

/**
 * Created by Hitesh.Sahu on 11/23/2016.
 */

public class NetworkHandler {

    public static Retrofit getRetrofit() {

        return new Retrofit.Builder()
                .baseUrl(AppConstants.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(getUnsafeOkHttpClient())
                .build();
    }


    private static OkHttpClient 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);
            builder.hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });

            OkHttpClient okHttpClient = builder.build();
            return okHttpClient;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

然后像这样简单地使用没有 ssl 检查的改造

    private void postFeedbackOnServer() {

        MyApiEndpointInterface apiService =
                NetworkHandler.getRetrofit().create(MyApiEndpointInterface.class);

        Call<ResponseBE> call = apiService.submitFeedbackToServer(requestObject);

        Log.e(TAG ,  "Request is" + new Gson().toJson(requestObject).toString() );

        call.enqueue(new Callback<ResponseBE>() {
            @Override
            public void onResponse(Call<ResponseBE> call, Response<ResponseBE> response) {
                int statusCode = response.code();

                if (statusCode == HttpURLConnection.HTTP_OK) {

              ......

                } else {
                    Toast.makeText(FeedbackActivity.this, "Failed to submit Data" + statusCode, Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<ResponseBE> call, Throwable t) {

                // Log error here since request failed
                Toast.makeText(FeedbackActivity.this, "Failure" + t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();

            }
        });
    }

【讨论】:

    【解决方案2】:

    自 Hitesh Sahu 的答案发布以来,语法发生了一些变化。现在您可以将 lambdas 用于某些方法,删除一些 throw 子句和链式构建器方法调用。

    private static OkHttpClient createOkHttpClient() {
        try {
            final TrustManager[] trustAllCerts = new TrustManager[] {
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
    
                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
    
                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return new java.security.cert.X509Certificate[]{};
                        }
                    }
            };
            final SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            return new OkHttpClient.Builder()
                    .sslSocketFactory(sslContext.getSocketFactory())
                    .hostnameVerifier((hostname, session) -> true)
                    .build();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    

    【讨论】:

      【解决方案3】:

      就我而言,我用 kotlin 解决了这样的问题:

      object Instance {
      
          private const val BASE_URL: String = "https://base_url/"
      
          val service: Service by lazy {
              Retrofit
                  .Builder()
                  .baseUrl(BASE_URL)
                  .client(getUnsafeOkHttpClient())
                  .build().create(Service::class.java)
          }
          private fun getUnsafeOkHttpClient(): OkHttpClient? {
              return try {
                  // Create a trust manager that does not validate certificate chains
                  val trustAllCerts = arrayOf<TrustManager>(
                      object : X509TrustManager {
                          @Throws(CertificateException::class)
                          override fun checkClientTrusted(
                              chain: Array<X509Certificate?>?,
                              authType: String?
                          ) {
                          }
      
                          @Throws(CertificateException::class)
                          override fun checkServerTrusted(
                              chain: Array<X509Certificate?>?,
                              authType: String?
                          ) {
                          }
      
                          override fun getAcceptedIssuers(): Array<X509Certificate?>? {
                              return 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
                  val trustManagerFactory: TrustManagerFactory =
                      TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
                  trustManagerFactory.init(null as KeyStore?)
                  val trustManagers: Array<TrustManager> =
                      trustManagerFactory.trustManagers
                  check(!(trustManagers.size != 1 || trustManagers[0] !is X509TrustManager)) {
                      "Unexpected default trust managers:" + trustManagers.contentToString()
                  }
      
                  val trustManager =
                      trustManagers[0] as X509TrustManager
      
      
                  val builder = OkHttpClient.Builder()
                  builder.sslSocketFactory(sslSocketFactory, trustManager)
                  builder.hostnameVerifier(HostnameVerifier { _, _ -> true })
                  builder.build()
              } catch (e: Exception) {
                  throw RuntimeException(e)
              }
          }
      }
      

      【讨论】:

        【解决方案4】:

        IMO,你可以阅读Google's documentation - Security with HTTPS and SSL

        关于将 Retrofit 与您的自签名证书一起使用的示例代码,请尝试以下操作,希望对您有所帮助!

        ...
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        
            try{
                OkHttpClient client = new OkHttpClient.Builder()
                        .sslSocketFactory(getSSLSocketFactory())
                        .hostnameVerifier(getHostnameVerifier())
                        .build();
        
                Retrofit retrofit = new Retrofit.Builder()
                        .baseUrl(API_URL_BASE)
                        .addConverterFactory(GsonConverterFactory.create())
                        .client(client)
                        .build();
        
                WebAPIService service = retrofit.create(WebAPIService.class);
        
                Call<JsonObject> jsonObjectCall = service.getData(...);
                ...
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        // for SSL...    
        // Read more at https://developer.android.com/training/articles/security-ssl.html#CommonHostnameProbs
        private HostnameVerifier getHostnameVerifier() {
            return new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true; // verify always returns true, which could cause insecure network traffic due to trusting TLS/SSL server certificates for wrong hostnames
                    //HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
                    //return hv.verify("localhost", session);
                }
            };
        }        
        
        private TrustManager[] getWrappedTrustManagers(TrustManager[] trustManagers) {
            final X509TrustManager originalTrustManager = (X509TrustManager) trustManagers[0];
            return new TrustManager[]{
                    new X509TrustManager() {
                        public X509Certificate[] getAcceptedIssuers() {
                            return originalTrustManager.getAcceptedIssuers();
                        }
        
                        public void checkClientTrusted(X509Certificate[] certs, String authType) {
                            try {
                                if (certs != null && certs.length > 0){
                                    certs[0].checkValidity();
                                } else {
                                    originalTrustManager.checkClientTrusted(certs, authType);
                                }
                            } catch (CertificateException e) {
                                Log.w("checkClientTrusted", e.toString());
                            }
                        }
        
                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
                            try {
                                if (certs != null && certs.length > 0){
                                    certs[0].checkValidity();
                                } else {
                                    originalTrustManager.checkServerTrusted(certs, authType);
                                }
                            } catch (CertificateException e) {
                                Log.w("checkServerTrusted", e.toString());
                            }
                        }
                    }
            };
        }
        
        private SSLSocketFactory getSSLSocketFactory()
                throws CertificateException, KeyStoreException, IOException,
                NoSuchAlgorithmException, KeyManagementException {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            InputStream caInput = getResources().openRawResource(R.raw.your_cert); // File path: app\src\main\res\raw\your_cert.cer
            Certificate ca = cf.generateCertificate(caInput);
            caInput.close();
            KeyStore keyStore = KeyStore.getInstance("BKS");
            keyStore.load(null, null);
            keyStore.setCertificateEntry("ca", ca);
            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(keyStore);
            TrustManager[] wrappedTrustManagers = getWrappedTrustManagers(tmf.getTrustManagers());
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, wrappedTrustManagers, null);
            return sslContext.getSocketFactory();
        }
        ...
        

        【讨论】:

          【解决方案5】:

          根据@Hitesh Sahu 的回答在 Kotlin 中添加代码:

          fun getRetrofirApiService(currentBaseURL: String): YourAPIService{
              val TIMEOUT = 2L
              val logging = HttpLoggingInterceptor()
              logging.setLevel(HttpLoggingInterceptor.Level.BODY)
          
              val retrofit = Retrofit.Builder()
                  .baseUrl(currentBaseURL)
                  .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                  .addConverterFactory(NullOnEmptyConverterFactory())
                  .addConverterFactory(GsonConverterFactory.create())
                  .client(createOkHttpClient())
                  .build()
              return retrofit.create(APIService::class.java)
          }
          

          现在创建 Http 客户端,如下所示:

          private fun createOkHttpClient(): OkHttpClient {
              return try {
                  val trustAllCerts: Array<TrustManager> = arrayOf(MyManager())
                  val sslContext = SSLContext.getInstance("SSL")
                  sslContext.init(null, trustAllCerts, SecureRandom())
                  val logging = HttpLoggingInterceptor()
                  logging.level = HttpLoggingInterceptor.Level.BODY
                  OkHttpClient.Builder()
                      .sslSocketFactory(sslContext.getSocketFactory())
                      .addInterceptor(logging)
                      .hostnameVerifier { hostname: String?, session: SSLSession? -> true }
                      .build()
              } catch (e: Exception) {
                  throw RuntimeException(e)
              }
          }
          

          MyManager 类如下图:

          class MyManager : X509TrustManager {
          
              override fun checkServerTrusted(
                  p0: Array<out java.security.cert.X509Certificate>?,
                  p1: String?
              ) {
                  //allow all
              }
          
              override fun checkClientTrusted(
                  p0: Array<out java.security.cert.X509Certificate>?,
                  p1: String?
              ) {
                  //allow all
              }
          
              override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> {
                  return arrayOf()
              }
          }
          

          相同的进口如下所示:

              import okhttp3.MediaType
              import okhttp3.OkHttpClient
              import okhttp3.RequestBody
              import okhttp3.logging.HttpLoggingInterceptor
              import retrofit2.Retrofit
              import retrofit2.adapter.rxjava2.Result
              import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
              import retrofit2.converter.gson.GsonConverterFactory
              import java.security.SecureRandom
              import java.util.concurrent.TimeUnit
              import javax.net.ssl.SSLContext
              import javax.net.ssl.SSLSession
              import javax.net.ssl.TrustManager
              import javax.net.ssl.X509TrustManager
          

          【讨论】:

            【解决方案6】:

            我在此页面上尝试了@whirlwin 的解决方案,但这不适用于 java 9+。一些小的变化导致了这一点:

                private static OkHttpClient createTrustingOkHttpClient() {
                try {
                    X509TrustManager x509TrustManager = new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
            
                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
            
                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return new java.security.cert.X509Certificate[]{};
                        }
                    };
                    final TrustManager[] trustAllCerts = new TrustManager[] {
                            x509TrustManager
                    };
                    final SSLContext sslContext = SSLContext.getInstance("SSL");
                    sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
                    return new OkHttpClient.Builder()
                            .sslSocketFactory(sslContext.getSocketFactory(), x509TrustManager)
                            .hostnameVerifier((hostname, session) -> true)
                            .build();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            

            正如你所想,这对我有用。快乐的时光!不过,使用它时要小心。

            【讨论】:

              【解决方案7】:

              在代码中实现这种变通方法,即使是为了测试目的也是一种不好的做法。

              你可以:

              1. 生成您的 CA。
              2. 使用 CA 签署您的证书。
              3. 将您的 CA 添加为受信任的。

              一些可能有用的链接:

              【讨论】:

                猜你喜欢
                • 2017-02-03
                • 1970-01-01
                • 2011-07-19
                • 2014-06-23
                • 2013-11-12
                • 1970-01-01
                • 2019-06-28
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多