我在android中使用retrofit连接服务器。
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:找不到证书路径的信任锚。
答案 0 :(得分:29)
使用此类获取不安全的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();
}
});
}
答案 1 :(得分:5)
我强烈反对这样做
简短回答 - 子类HostNameVerifier,覆盖验证()以始终返回true。
这有更好的选择
答案很长 - 在这里查看我的(变得很旧)博客:Making Android and SSL Work Together
可能是您的方案的最佳选择
将https删除到测试服务器的http,然后逻辑不必更改。
HTH
答案 2 :(得分: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();
}
...
答案 3 :(得分:3)
自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);
}
}
答案 4 :(得分:0)
在代码中实现此类变通办法,即使是出于测试目的也是一种不好的做法。
你可以:
一些可能有用的链接:
答案 5 :(得分:0)
根据@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 :(得分:0)
在我的情况下,我用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)
}
}
}
答案 7 :(得分:0)
我在此页面上尝试了@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);
}
}
这对我来说是可以想象的。快乐的时光!不过,在使用时要小心。