无法验证证书签名?

22
我使用了这个网站 自签名SSL 的SSL Socket和Trustmanager,但我一直收到以下错误信息:

09-28 19:52:41.942: WARN/System.err(10101): javax.net.ssl.SSLHandshakeException:org.bouncycastle.jce.exception.ExtCertPathValidatorException:无法验证证书签名。

有什么问题呢? 我已经查看了Stack Overflow上的不同帖子,但似乎无法使其正常工作。
我的代码:
SchemeRegistry schemeRegistry = new SchemeRegistry();

// http scheme

schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));

// https scheme

schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(), 443));
params = new BasicHttpParams();
params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 1);
params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(1));
params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, "utf8");
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope("www.example.com", AuthScope.ANY_PORT),
    new UsernamePasswordCredentials("user", "password"));
clientConnectionManager = new ThreadSafeClientConnManager(params, schemeRegistry);
context = new BasicHttpContext();
context.setAttribute("http.auth.credentials-provider", credentialsProvider);

DefaultHttpClient client = new DefaultHttpClient(clientConnectionManager, params);

HttpGet get = new HttpGet("https://www.example.com/web/restricted/form/formelement=512663");
HttpResponse response = client.execute(get, context);

Log.w("Response ","Status line : "+ response.toString());

21
您可能还希望检查手机的日期和时间设置。如果时钟不同步,会导致类似的错误出现。 - Michael Levy
如果您使用Fuel作为REST库,请参阅https://dev59.com/9qfja4cB1Zd3GeqPpAEc#54276837。 - CoolMind
6个回答

36

正如Michael Levy所提到的,我之所以会遇到这个异常,是因为我让我的Android模拟器运行了几天,导致时钟偏离太远。一旦重新启动模拟器,异常就消失了。


2
我在一台没有SIM卡的实体Galaxy Note设备上遇到了类似的问题。有趣的是,内置浏览器可以正常工作。一旦我将时间同步设置为自动,就不再遇到任何错误了。 - Stephen Niedzielski
3
没错,我遇到了证书握手问题,并发现我的设备时间没有正确设置。它被设置成过去的某个时间,导致 SSL 握手失败。 - Haresh Chaudhary

3

更新

我在API 16模拟器上还遇到了另一个错误:

routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version (external/openssl/ssl/s23_clnt.c:741'。

阅读12,我修改了代码如下:

val okHttpClient = getOkHttpBuilder().build()

private fun getOkHttpBuilder(): OkHttpClient.Builder {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        Security.insertProviderAt(Conscrypt.newProvider(), 1)
    }
    return OkHttpClient().newBuilder()
}

// build.gradle:
implementation 'org.conscrypt:conscrypt-android:2.5.1'

但是这个会使apk增加3.4 Mb。

我还从MyApplication中删除了这些行:

try {
    ProviderInstaller.installIfNeeded(applicationContext)
    val sslContext = SSLContext.getInstance("TLSv1.2")
    sslContext.init(null, null, null)
    sslContext.createSSLEngine()
} catch (e: GooglePlayServicesRepairableException) {
    Timber.e(e.stackTraceToString())
    // Prompt the user to install/update/enable Google Play services.
    GoogleApiAvailability.getInstance().showErrorNotification(this, e.connectionStatusCode)
} catch (e: GooglePlayServicesNotAvailableException) {
    Timber.e(e.stackTraceToString())
    // Prompt the user to install/update/enable Google Play services.
    // GoogleApiAvailability.getInstance().showErrorNotification(this, e.errorCode)
} catch (e: NoSuchAlgorithmException) {
    Timber.e(e.stackTraceToString())
} catch (e: KeyManagementException) {
    Timber.e(e.stackTraceToString())
}

=== 旧回答 ===

在我的情况下,在Android 4和5上引发了以下错误:

由于: com.android.org.bouncycastle.jce.exception.ExtCertPathValidatorException: 无法验证证书:证书已于2020年5月30日过期 10:48:38 GMT+00:00(与2020年8月13日11:47:00 GMT+00:00相比)

...

由于:java.security.cert.CertificateExpiredException: 证书已于2020年5月30日过期 10:48:38 GMT+00:00(与2020年8月13日11:47:00 GMT+00:00相比)

服务器存在证书错误(可能已过期)。

对于 Retrofit,请参见https://dev59.com/3Ww15IYBdhLWcg3wGn9c#60507560。如果您使用 Fuel 作为REST库,请参见kotlin library that can do httpS connection without certificate verification (like curl --insecure)

您可以信任所有证书,但这是危险的。

import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.*
import javax.security.cert.CertificateException

companion object {

    private val gson: Gson
    private val retrofit: Retrofit

    init {

        val okHttpClient = getOkHttpBuilder().build()

        gson = GsonBuilder().setLenient().create()

        retrofit = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build()
    }

    private fun getOkHttpBuilder(): OkHttpClient.Builder =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            OkHttpClient().newBuilder()
        } else {
            getUnsafeOkHttpClient()
        }

    private fun getUnsafeOkHttpClient(): OkHttpClient.Builder =
        try {
            // Create a trust manager that does not validate certificate chains
            val trustAllCerts: Array<TrustManager> = arrayOf(
                object : X509TrustManager {
                    @Throws(CertificateException::class)
                    override fun checkClientTrusted(chain: Array<X509Certificate?>?,
                                                    authType: String?) = Unit

                    @Throws(CertificateException::class)
                    override fun checkServerTrusted(chain: Array<X509Certificate?>?,
                                                    authType: String?) = Unit

                    override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
                }
            )
            // Install the all-trusting trust manager
            val sslContext: SSLContext = SSLContext.getInstance("SSL")
            sslContext.init(null, trustAllCerts, SecureRandom())
            // Create an ssl socket factory with our all-trusting manager
            val sslSocketFactory: SSLSocketFactory = sslContext.socketFactory
            val builder = OkHttpClient.Builder()
            builder.sslSocketFactory(sslSocketFactory,
                trustAllCerts[0] as X509TrustManager)
            builder.hostnameVerifier { _, _ -> true }
            builder
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
}

请参考https://dev59.com/3Ww15IYBdhLWcg3wGn9c#60507560来检查Android版本和连接Glide.


我在Crashlytics上的Android 4和5版本上遇到了完全相同的错误,但我仍然很困惑为什么会发生这种情况,而且为什么只在这两个操作系统上出现。 - VGM
@VGM,谢谢!经过数月的出现和消失的证书问题,我理解到在Android 4和5中,默认情况下无法通过新协议建立连接。因此,我们需要启用SSL、TLS 1.2、1.3。你知道,这对我来说很困难,所以我只是删除了信任。我不知道它是否适用于所有旧设备,但在模拟器和一些设备上删除信任是有效的。 - CoolMind

2

嗨Konstantin, 谢谢回复,但我认为我必须以另一种方式登录此网站,这太复杂了。 我想要登录的网站有一个登录表单,可以通过HTTP提交,然后通过SSL进行身份验证并重定向到我想要的页面。 我认为使用保留自HTTPGet的cookie的HTTP Post应该可以做到,对吗? - Lars
你没有走得那么远——你从未获得HTTPS连接。你可以通过提交HTTP表单来成功,但是除非解决证书问题,否则SSH重定向将无法工作——这是你的一方不信任服务器。有趣的是,有些服务器存在缺陷,无法验证客户端的签名(特别是JBoss存在此漏洞)。 - Konstantin Pribluda

1

检查您的设备时间,并进行修正,然后再次检查。


0

顺便提一下,我们可以很容易地重现这个错误——只需将手机日期改为几年后即可。

注意:不同的手机可能会有一些差异。有些手机可能会显示证书已过期。


0

看起来设备的日期/时间有误


你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

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