javax.net.ssl.SSL握手异常:java.lang.IllegalArgumentException:toASCII的输入无效:ip_nbae7bac35.kodrive.xyz

10

有没有解决这个异常的方法?看起来像是一种 Android 的 bug,可能与主机名中含有下划线字符有关。详细信息可参考Github 上的问题

以下是堆栈跟踪:

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)
3个回答

12

您的问题是无法使用toASCII将包含_的字符串转换为ASCII。除了联系域名所有者并请求删除_之外,没有其他解决方法。这就是您收到IllegalArgumentException的原因,该字符串是“非法的”。


由于主机名中有下划线,导致出现相同的问题。是否有任何解决方法?在Postman请求中URL有效,但在Retrofit请求中却无效。 - Pragya Singla
1
URL中允许使用下划线。https://datatracker.ietf.org/doc/html/rfc3986#section-2.3 很遗憾toASCII()不能处理它。 - The incredible Jan

1
我解决了这个问题,将编写一个代码,可以接受所有SSL证书,希望您不像我一样花费数小时来处理它。当我看到批准的评论时,我认为它是无法解决的,但我解决了它 :)
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;
    }

}

但是在Play商店上允许吗?我认为信任所有证书的信任管理器不被允许在Play商店上。 - casolorz

0
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())

2
你能否在你的回答中添加更多细节?它与被接受的答案有何不同之处? - razdi
我没有发布这个答案,但基本上它接受所有的主机名,我想。我还没有实现这个,因为我不认为谷歌允许在Play商店上这样做,至少我听说过。 - casolorz
这只是一个例子。您应该添加自己的证书。如果添加ssh支持,则不会出现"_ "错误。因此,就不会有IllegalArgumentException。 - ikakaxi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接