这个异常有解决方法吗?看看github上的这个问题,听起来这可能是一个android bug,关于主机名有下划线字符之类的东西。
这是堆栈跟踪:
javax.net.ssl.SSLHandshakeException: java.lang.IllegalArgumentException: Invalid input to toASCII: ip_nbae7bac35.kodrive.xyz
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:219)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:318)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:282)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:167)
at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:257)
at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
at okhttp3.RealCall.execute(RealCall.java:77)
_
的 toASCII 将字符串转换为 ASCII。除了与域名所有者联系并要求他们删除
_
之外,没有其他办法解决此问题。这就是为什么你得到一个
IllegalArgumentException
,该字符串是“非法的”。
Kotlin 代码:
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.io.IOException
import java.security.SecureRandom
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
class ExampleClient {
companion object{
private fun httpClient(accessToken: String): OkHttpClient.Builder? {
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 builder = OkHttpClient.Builder()
builder.addInterceptor(object : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val original = chain.request()
// Request customization: add request headers
val requestBuilder = original.newBuilder()
.addHeader("Authorization", accessToken)
val request = requestBuilder.build()
return chain.proceed(request)
}
})
builder.sslSocketFactory(
sslSocketFactory,
(trustAllCerts[0] as X509TrustManager)
)
builder.hostnameVerifier(HostnameVerifier { hostname, session -> true })
builder
} catch (e: Exception) {
throw RuntimeException(e)
}
}
fun getClient(accessToken: String):Retrofit{
return Retrofit.Builder()
.baseUrl("https://sub_domain.base-url.com")
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient(accessToken)!!.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
}
}
Java代码:
import android.content.ContextWrapper;
import com.pixplicity.easyprefs.library.Prefs;
import java.io.IOException;
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.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class Client {
public static String deviceid;
private static Retrofit retrofit;
public static String BASE_URL = "https://base_url.com/api/";
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.addInterceptor(new Interceptor() {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
String token;
try {
token = Prefs.getString("token","Token not found");
}catch (Exception e){
token = LoginActivity.getToken();
}
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("deviceid", deviceid).addHeader("Authorization","Bearer " + token);
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
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);
}
}
public static Retrofit getClient(String device_id){
deviceid = device_id;
MyLog.log("DEVICE ID : "+deviceid);
if(retrofit == null){
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(getUnsafeOkHttpClient().build())
.build();
return retrofit;
}
return retrofit;
}
}
BouncyCastle。
为此,您需要首先将依赖项添加到应用程序级别的 build.gradle
// Check the actual version at the BouncyCastle website
implementation 'org.bouncycastle:bcpkix-jdk15to18:1.77'
implementation 'org.bouncycastle:bcprov-jdk15to18:1.77'
implementation 'org.bouncycastle:bctls-jdk15to18:1.77'
然后您需要覆盖应用程序的默认SecurityProvider。为此,您可以创建一个新的 App 类,它使用以下代码扩展 Application
import android.app.Application;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
import java.security.Security;
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
Security.removeProvider("BC");
// Confirm that positioning this provider at the end works for your needs!
Security.addProvider(new BouncyCastleProvider());
Security.insertProviderAt(new BouncyCastleProvider(), 1);
Security.insertProviderAt(new BouncyCastleJsseProvider(), 1);
}
}
您需要将您的 App 类添加到 AndroidManifest.xml 应用程序标签中
<application
...
android:name=".App"
...
更改之后,OkHttpClient 应该已经使用 BouncyCastle 并且必须调用子域。但是,您可能会收到未知证书的错误org.bouncycastle.tls.TlsFatalAlert: certificate_unknown
。您可以检查该答案以设置密钥
https://stackoverflow.com/a/72235440
但是出于开发目的,您可能想跳过该步骤,然后您需要稍微采用@Gorkem KARA 答案并使用 BouncyCastleJsseProvider 创建自定义版本的 HttpClient
private static OkHttpClient.Builder getUnsafeOkHttpClient() {
try {
final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] cArrr = new X509Certificate[0];
return cArrr;
}
}};
SSLContext sslContext = SSLContext.getInstance("TLSv1.2", BouncyCastleJsseProvider.PROVIDER_NAME);
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0]);
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
Log.d(TAG, "Trust Host");
return true;
}
};
builder.hostnameVerifier( hostnameVerifier);
return builder;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
之后您可以使用您的 unsafeOkHttpClient 版本
Retrofit retrofit = new retrofit2.Retrofit.Builder()
.client(getUnsafeOkHttpClient().build())
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
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;
/**
* 忽略https证书验证
*/
public class SSLSocketClient {
//获取这个SSLSocketFactory
public static SSLSocketFactory getSSLSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, getTrustManager(), new SecureRandom());
return sslContext.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//获取TrustManager
private static TrustManager[] getTrustManager() {
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}};
return trustAllCerts;
}
//获取HostnameVerifier
public static HostnameVerifier getHostnameVerifier() {
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
};
return hostnameVerifier;
}
}
OkHttpClient.Builder builder = ...
builder.sslSocketFactory(SSLSocketClient.getSSLSocketFactory())
.hostnameVerifier(SSLSocketClient.getHostnameVerifier())