如何在改造中使用 ssl 证书发出 https 请求
Posted
技术标签:
【中文标题】如何在改造中使用 ssl 证书发出 https 请求【英文标题】:How to make https request with ssl certificate in Retrofit 【发布时间】:2015-08-27 01:47:06 【问题描述】:我有一个 .p12 证书文件,我使用 SSL Converter 将其转换为 .pem 证书文件。然后我在我的 android 代码中使用该 pem 证书文件,如下所示:
OkHttpClient okHttpClient = new OkHttpClient();
try
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream instream = context.getResources().openRawResource(R.raw.pem_certificate);
Certificate ca;
ca = cf.generateCertificate(instream);
KeyStore kStore = KeyStore.getInstance(KeyStore.getDefaultType());
kStore.load(null, null);
kStore.setCertificateEntry("ca", ca);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(kStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
catch (CertificateException
| KeyStoreException
| NoSuchAlgorithmException
| IOException
| KeyManagementException e)
e.printStackTrace();
baseURL = endpoint;
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(baseURL)
.setClient(new OkClient(okHttpClient))
.build();
service = restAdapter.create(DishService.class);
但是这段代码不起作用。它在“ca = cf.generateCertificate(instream);”行失败带有 CertificateException 消息。
【问题讨论】:
请看这个答案:***.com/a/31436459/4261176. gist.github.com/erikcaffrey/10af1cc0b99a54f5b9a8a7614cca6f0a希望对大家有所帮助! 使用这个最简单的解决方案:***.com/a/45855405/3448003 【参考方案1】:public class RetrofitBuilder
private static Retrofit retrofit = null;
private static final String BASE_URL = BuildConfig.BASE_URL;
private static final String API_VERSION = BuildConfig.VERSION;
private static OkHttpClient.Builder httpClientBuilder = null;
public static Retrofit getInstance(Context context)
if (retrofit == null)
httpClientBuilder = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS);
initHttpLogging();
initSSL(context);
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(BASE_URL + API_VERSION)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClientBuilder.build());
retrofit = builder.build();
return retrofit;
private static void initHttpLogging()
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
if (BuildConfig.DEBUG) httpClientBuilder.addInterceptor(logging);
private static void initSSL(Context context)
SSLContext sslContext = null;
try
sslContext = createCertificate(context.getResources().openRawResource(R.raw.cert));
catch (CertificateException | IOException | KeyStoreException | KeyManagementException | NoSuchAlgorithmException e)
e.printStackTrace();
if(sslContext!=null)
httpClientBuilder.sslSocketFactory(sslContext.getSocketFactory(), systemDefaultTrustManager());
private static SSLContext createCertificate(InputStream trustedCertificateIS) throws CertificateException, IOException, KeyStoreException, KeyManagementException, NoSuchAlgorithmException
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate ca;
try
ca = cf.generateCertificate(trustedCertificateIS);
finally
trustedCertificateIS.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);
return sslContext;
private static X509TrustManager systemDefaultTrustManager()
try
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager))
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
return (X509TrustManager) trustManagers[0];
catch (GeneralSecurityException e)
throw new AssertionError(); // The system has no TLS. Just give up.
在阅读了很多帖子、博客和要点后,我终于找到了一种方法。这对我有用。
【讨论】:
在哪里可以找到证书文件? 在哪里可以找到 kotlin 版本?【参考方案2】:也许你的 R.raw.pem_certificate 有问题...
1) 尝试使用 openssl 从服务器获取原始公共证书: openssl s_client -connect HOSTNAME:PORT -showcerts
(详情请看这里:https://superuser.com/questions/97201/how-to-save-a-remote-server-ssl-certificate-locally-as-a-file)
2) 如何使用自定义 SSL 证书设置 Retrofit2 https://adiyatmubarak.wordpress.com/tag/add-ssl-certificate-in-retrofit-2/
或改造1: https://number1.co.za/use-retrofit-self-signed-unknown-ssl-certificate-android/
PS:它适用于我,请不要将 PEM 文件转换为 BKS。
【讨论】:
【参考方案3】:Jay 的回答让我开始了,但我收到了这个错误:Trust anchor for certification path not found
我设法使用KeyTool command 将我拥有的 p12 证书导入到密钥库中。
更具体地说:
步骤 1. 使用默认证书生成新的密钥库,然后将其删除
keytool -genkey -alias mycert -keyalg RSA -keysize 2048 -keystore mykeystore
keytool -delete -alias mycert -keystore mykeystore
keytool -v -list -keystore mykeystore
第 2 步。导入证书
keytool -v -importkeystore -srckeystore <path to certificate> -srcstoretype PKCS12 -destkeystore mykeystore -deststoretype PKCS12
keytool -v -list -keystore mykeystore
第 3 步:然后我使用以下代码加载 SSLContext:
// open the keystore
KeyStore keyStore = KeyStore.getInstance(type);
try
keyStore.load(keystoreInputStream, password.toCharArray());
finally
try
keystoreInputStream.close();
catch (IOException e)
// swallow this
// create and initialise a key manager factory
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
if (password != null && !password.trim().equals(""))
kmf.init(keyStore, password.toCharArray());
else
kmf.init(keyStore, null);
// build an SSL context
KeyManager[] keyManagers = kmf.getKeyManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, null, null);
感谢这里的代码:https://chariotsolutions.com/blog/post/https-with-client-certificates-on/
【讨论】:
【参考方案4】:第 1 步: 将您的安全证书放在 src/main/res/raw/client_certificate.cer 等原始目录中
第二步:用这个证书创建一个SSLContext对象,比如
SSLContext sslContext = createCertificate(getApplicationContext().getResources().openRawResource(R.raw.client_certificate));
第 3 步:将创建的 SSLContext 对象添加到 OkHttpClient 构建器。
addClientCertificate(okHttpClientBuilder);
第四步:设置 OkHttpClient 构建器来改造客户端。
retrofitBuilder.client(okHttpClientBuilder.build());
这是 RetrofitManager.java 文件中上述所有步骤的完整实现。
RetrofitManager.java
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.security.cert.Certificate;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Created by Rajoo Kannaujiya on 02/16/2020.
*/
public class RetrofitManager
private static final String URL = "https://***.com"; // Fetch url from config file.
private static final String TLS = "TLS";
private static final String SSL = "SSL";
private static final String CA = "ca";
private static final String X_509 = "X.509";
private static final String PROD = "prod";
private static X509TrustManager trustManager = null;
private static final String CONTENT_TYPE = "Content-Type";
private static final String CONTENT_TYPE_VALUE = "application/json";
public static final String BUILD_TYPE = "prod"; // Fetch build type from config file (Whether it is a dev, qa, stage or prod build).
public static Retrofit getRetrofitForAPIUrl()
return getRetrofit(URL);
private static Retrofit getRetrofit(String url)
Gson gson = new GsonBuilder().setLenient().create();
Retrofit.Builder retrofitBuilder = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(url);
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.level(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
okHttpClientBuilder.addInterceptor(chain ->
Request origReq = chain.request();
Request.Builder requestBuilder = origReq.newBuilder()
.addHeader(CONTENT_TYPE, CONTENT_TYPE_VALUE);
Request request = requestBuilder.build();
return chain.proceed(request);
);
addClientCertificate(okHttpClientBuilder);
OkHttpClient.Builder okHttpBuilder = okHttpClientBuilder
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(logging);
retrofitBuilder.client(okHttpBuilder.build());
return retrofitBuilder.build();
/**
* Method to create SSLContext object for the client certificate and return it.
*
* @param clientCertificate
* @return SSLContext Object
*/
private static SSLContext getSSLContextForClientCertificate(InputStream clientCertificate)
SSLContext sslContext = null;
// If given client certificate is for production environment only, Then check put check for build type.
if (PROD.contentEquals(BUILD_TYPE))
// Creating X.509 certificate factory instance
CertificateFactory cf = null;
try
cf = CertificateFactory.getInstance(X_509);
catch (CertificateException e)
e.printStackTrace();
// Generating client certificate
Certificate ca = null;
try
try
if (cf != null)
ca = cf.generateCertificate(clientCertificate);
catch (CertificateException e)
e.printStackTrace();
finally
try
clientCertificate.close();
catch (IOException e)
e.printStackTrace();
// Creating a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = null;
try
keyStore = KeyStore.getInstance(keyStoreType);
catch (KeyStoreException e)
e.printStackTrace();
if (keyStore != null)
try
keyStore.load(null, null);
catch (CertificateException | IOException | NoSuchAlgorithmException e)
e.printStackTrace();
try
keyStore.setCertificateEntry(CA, ca);
catch (KeyStoreException e)
e.printStackTrace();
// Creating a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = null;
try
tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
catch (NoSuchAlgorithmException e)
e.printStackTrace();
try
if (tmf != null)
tmf.init(keyStore);
catch (KeyStoreException e)
e.printStackTrace();
// Creating an SSLContext instance that uses our TrustManager
try
sslContext = SSLContext.getInstance(TLS);
catch (NoSuchAlgorithmException e)
e.printStackTrace();
try
if (sslContext != null && tmf != null)
sslContext.init(null, tmf.getTrustManagers(), null);
catch (KeyManagementException e)
e.printStackTrace();
else // For non prod environment build. This section is useful only if you don't have client certificate for lower environment.
try
sslContext = SSLContext.getInstance(SSL);
sslContext.init(null, new TrustManager[]getTrustManager(), new java.security.SecureRandom());
catch (NoSuchAlgorithmException | KeyManagementException e)
e.printStackTrace();
trustManager = getTrustManager();
return sslContext;
/**
* Method to add client certificate to OkHttpClient.Builder object.
*
* @param okHttpClientBuilder
* @return void
*/
private static void addClientCertificate(OkHttpClient.Builder okHttpClientBuilder)
SSLContext sslContext = getSSLContextForClientCertificate(getApplicationContext().getResources().openRawResource(R.raw.myaccount_coxbusiness_com));
if (sslContext != null)
okHttpClientBuilder.sslSocketFactory(sslContext.getSocketFactory(), trustManager);
okHttpClientBuilder.hostnameVerifier((hostname, session) -> true);
private static X509TrustManager getTrustManager()
X509TrustManager trustManager = null;
if (PROD.contentEquals(BUILD_TYPE)) // If client certificate is only available for prod environment.
try
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers[0] instanceof X509TrustManager)
trustManager = (X509TrustManager) trustManagers[0];
catch (GeneralSecurityException e)
throw new AssertionError();
else // For non prod environment build. This section is useful only if you don't have client certificate for lower environment.
trustManager = new X509TrustManager()
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException
@Override
public X509Certificate[] getAcceptedIssuers()
return new X509Certificate[0];
;
return trustManager;
【讨论】:
对不起,没有用的评论,但你的方法 getSSLContextForClientCertificate() 是我一生中见过的最丑陋的代码,我只是想让你知道。【参考方案5】:按照@Jay的answer,这里是kotlin版本:
private var retrofit: Retrofit? = null
private val BASE_URL: String = BuildConfig.BASE_URL
private val API_VERSION: String = BuildConfig.VERSION
private var httpClientBuilder: Builder? = null
fun getInstance(context: Context): Retrofit?
if (retrofit == null)
httpClientBuilder = Builder().readTimeout(5, TimeUnit.SECONDS)
initHttpLogging()
initSSL(context)
val builder = Retrofit.Builder()
.baseUrl(BASE_URL + API_VERSION)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClientBuilder.build())
retrofit = builder.build()
return retrofit
private fun initHttpLogging()
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
if (BuildConfig.DEBUG) httpClientBuilder.addInterceptor(logging)
private fun initSSL(context: Context)
var sslContext: SSLContext? = null
try
sslContext = createCertificate(context.resources.openRawResource(R.raw.cert))
catch (e: CertificateException)
e.printStackTrace()
catch (e: IOException)
e.printStackTrace()
catch (e: KeyStoreException)
e.printStackTrace()
catch (e: KeyManagementException)
e.printStackTrace()
catch (e: NoSuchAlgorithmException)
e.printStackTrace()
if (sslContext != null)
httpClientBuilder.sslSocketFactory(
sslContext.socketFactory,
systemDefaultTrustManager()
)
@Throws(
CertificateException::class,
IOException::class,
KeyStoreException::class,
KeyManagementException::class,
NoSuchAlgorithmException::class
)
private fun createCertificate(trustedCertificateIS: InputStream): SSLContext
val cf = CertificateFactory.getInstance("X.509")
val ca: Certificate
ca = try
cf.generateCertificate(trustedCertificateIS)
finally
trustedCertificateIS.close()
// creating a KeyStore containing our trusted CAs
val keyStoreType = KeyStore.getDefaultType()
val keyStore = KeyStore.getInstance(keyStoreType)
keyStore.load(null, null)
keyStore.setCertificateEntry("ca", ca)
// creating a TrustManager that trusts the CAs in our KeyStore
val tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
val tmf = TrustManagerFactory.getInstance(tmfAlgorithm)
tmf.init(keyStore)
// creating an SSLSocketFactory that uses our TrustManager
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, tmf.trustManagers, null)
return sslContext
private fun systemDefaultTrustManager(): X509TrustManager
return try
val trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val trustManagers = trustManagerFactory.trustManagers
check(!(trustManagers.size != 1 || trustManagers[0] !is X509TrustManager))
"Unexpected default trust managers:" + Arrays.toString(
trustManagers
)
trustManagers[0] as X509TrustManager
catch (e: GeneralSecurityException)
throw AssertionError() // The system has no TLS. Just give up.
【讨论】:
【参考方案6】:伙计们进行了大量研究,终于找到了这个解决方案的核心。
步骤 1. 创建一个新的 java 类,如下所示
public class UnsafeOkHttpClient
public 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, (X509TrustManager) trustAllCerts[0]);
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);
第 2 步。将上述类方法 getUnsafeOkHttpClient()
添加到您的 API 客户端中。
OkHttpClient clients = UnsafeOkHttpClient.getUnsafeOkHttpClient();
if (retrofit == null)
retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
.client(clients)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
PS:它适用于我,请正确添加功能。该死的肯定会工作的。
【讨论】:
以上是关于如何在改造中使用 ssl 证书发出 https 请求的主要内容,如果未能解决你的问题,请参考以下文章