找不到Android SSL连接的信任锚


185

我正在尝试连接到运行Godaddy 256位SSL证书的IIS6框,但出现错误:

java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

一直试图确定是什么原因导致的,但现在还是空白。

这是我的联系方式:

HttpsURLConnection conn;              
conn = (HttpsURLConnection) (new URL(mURL)).openConnection();
conn.setConnectTimeout(20000);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.connect();
String tempString = toString(conn.getInputStream()); 

Answers:


78

@Chrispix的解决方案很危险!信任所有证书,任何人都可以在中间攻击中做人!只需将任何证书发送给客户端,它将接受它!

将您的证书添加到自定义信任管理器中,如本文中所述:通过HTTPS使用HttpClient信任所有证书

尽管使用自定义证书建立安全连接会有些复杂,但是它将为您带来想要的ssl加密安全性,而不会造成中间攻击的危险!


1
这对于使用自行生成的证书是可以的,但是对于具有有效链回到根CA的证书(如OP)来说,这只是配置错误的服务器的一种解决方法-请参阅我的答案。
Stevie 2013年

4
@Stevie接受每个证书只是概念验证测试的一种选择,其中SSL连接不是您要测试的部分。否则,如果您接受所有证书,则不必使用SSL,因为连接不安全!
Matthias B

1
啊,很抱歉,我认为这是一个误会-我完全同意没有人可以接受每份证书!:)我的评论是关于您的第二段的-使用自定义信任管理器的建议-IMHO应该是最后的解决方法,而不是推荐的解决方案。
Stevie 2013年

2
抬起头来。.我删除了作为解决方法粘贴的“解决方案”,以免引起更多问题,因为人们并不认为这是“临时解决方法”。
Chrispix

7
@Chrispix,你不应该删除的部分修复,这是很好的只是测试目的
雨果Allexis卡多纳

224

违背了公认的答案,你并不需要一个定制信任管理器,你需要修复服务器配置!

使用错误安装的dynadot / alphassl证书连接到Apache服务器时,我遇到了同样的问题。我正在使用HttpsUrlConnection(Java / Android)进行连接,该连接会抛出-

javax.net.ssl.SSLHandshakeException: 
  java.security.cert.CertPathValidatorException: 
    Trust anchor for certification path not found.

实际的问题是服务器配置错误-使用http://www.digicert.com/help/对其进行测试或类似方法进行,它甚至会告诉您解决方案:

“证书不是由受信任的权威机构签名的(通过Mozilla的根存储进行检查)。如果您是从受信任的权威机构购买的证书,则可能只需要安装一个或多个中间证书。请与您的证书提供商联系,以获取帮助。服务器平台。”

您还可以使用openssl检查证书:

openssl s_client -debug -connect www.thedomaintocheck.com:443

您可能会看到:

Verify return code: 21 (unable to verify the first certificate)

并且,在输出的前面:

depth=0 OU = Domain Control Validated, CN = www.thedomaintocheck.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 OU = Domain Control Validated, CN = www.thedomaintocheck.com
verify error:num=27:certificate not trusted
verify return:1
depth=0 OU = Domain Control Validated, CN = www.thedomaintocheck.com
verify error:num=21:unable to verify the first certificate`

证书链仅包含1个元素(您的证书):

Certificate chain
 0 s:/OU=Domain Control Validated/CN=www.thedomaintocheck.com
  i:/O=AlphaSSL/CN=AlphaSSL CA - G2

...但应将链中的签名授权机构引用回Android信任的签名机构(Verisign,GlobalSign等):

Certificate chain
 0 s:/OU=Domain Control Validated/CN=www.thedomaintocheck.com
   i:/O=AlphaSSL/CN=AlphaSSL CA - G2
 1 s:/O=AlphaSSL/CN=AlphaSSL CA - G2
   i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
 2 s:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
   i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA

用于配置服务器的说明(和中间证书)通常由颁发证书的机构提供,例如:http : //www.alphassl.com/support/install-root-certificate.html

安装了由证书颁发者提供的中间证书之后,使用HttpsUrlConnection进行连接时,我现在没有任何错误。


3
如果OP可以访问他要连接的服务器SSL配置,则可能是一种解决方案。但是,如果他不是托管要连接的服务的人,则必须自己解决问题,这意味着要实现自定义信任管理器。
Matthias B

9
毫无疑问,您的解决方案有效,但是您是否同意这是一种解决方法,而不是根本原因的解决方法?如果必须从3个客户端(Android,iOS,Windows Mobile)连接到服务器,则必须在所有3个客户端上应用解决方法,而我只能修复该服务器一次,它们都将“正常工作”。
Stevie 2013年

2
感谢Stevie,我的服务器配置错误了3个月,直到现在我才检测到这一点!现在,我的android应用正在正常运行100%
jpros 2013年

2
@Stevie我对共享同一域的不同API进行了多次调用,但是只有其中一个失败,并显示(javax.net.ssl.SSLHandshakeException)...知道为什么会发生这种情况吗?顺便说一下,SSL证书不受信任。因此,我认为所有调用均应失败,并具有相同的异常。
一个公平的参与者

1
@dvaey与拥有服务器的任何人联系,并告诉他们其配置已损坏并且https无法正常工作-给他们提供此链接以证明它digicert.com/help ...我想他们会很感激并且快速地解决。否则,您将不得不采取其他答案中的解决方法步骤之一,但是当证书过期且自定义信任库不再与新证书匹配时,您将不接受任何安全性(忽略证书)或重新部署应用程序证书
Stevie 2015年

17

您可以在运行时信任特定证书。
只需从服务器下载它,放入资产并使用ssl-utils-android这样加载:

OkHttpClient client = new OkHttpClient();
SSLContext sslContext = SslUtils.getSslContextForCertificateFile(context, "BPClass2RootCA-sha2.cer");
client.setSslSocketFactory(sslContext.getSocketFactory());

在上面的例子中我用OkHttpClientSSLContext了Java,可以将其与任何客户端一起使用。

如果您有任何问题随时问。我是这个小图书馆的作者。


.pfx呢?
Choletski '18

2
这是不安全的,因为一旦有apk,任何用户都可以访问应用程序的资产文件夹?
帕特里斯·安达拉

1
@PatriceAndala,根CA公开可用,所以没关系
BekaBot

17

根据最新的Android文档更新(2017年3月):

当您收到此类错误时:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
        at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:374)
        at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209)
        at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478)
        at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433)
        at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
        at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
        at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282)
        at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
        at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)

问题可能是以下之一:

  1. 颁发服务器证书的CA未知
  2. 服务器证书不是由CA签名的,而是自签名的
  3. 服务器配置缺少中间CA

解决方案是教导HttpsURLConnection信任一组特定的CA。怎么样?请检查 https://developer.android.com/training/articles/security-ssl.html#CommonProblems

其他正在AsyncHTTPClientcom.loopj.android:android-async-http库中使用的人,请检查Setup AsyncHttpClient以使用HTTPS


5
如果使用的是okhttp客户端怎么办?
TheLearner 2017年

对于okhttp,请检查@mklimek的答案。
user1506104

14

如果使用改造,则需要自定义OkHttpClient。

retrofit = new Retrofit.Builder()
                        .baseUrl(ApplicationData.FINAL_URL)
                        .client(getUnsafeOkHttpClient().build())
                        .addConverterFactory(GsonConverterFactory.create())
                        .build();

完整代码如下。

    public class RestAdapter {

    private static Retrofit retrofit = null;
    private static ApiInterface apiInterface;

    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.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 ApiInterface getApiClient() {
        if (apiInterface == null) {

            try {
                retrofit = new Retrofit.Builder()
                        .baseUrl(ApplicationData.FINAL_URL)
                        .client(getUnsafeOkHttpClient().build())
                        .addConverterFactory(GsonConverterFactory.create())
                        .build();

            } catch (Exception e) {

                e.printStackTrace();
            }


            apiInterface = retrofit.create(ApiInterface.class);
        }
        return apiInterface;
    }

}

1
我尝试使用代码,但再次出现以下错误。“ javax.net.ssl.SSLHandshakeException:java.security.cert.CertPathValidatorException:找不到证书路径的信任锚。” u能帮助这个
Vishwa普拉塔普

这是完全不安全的。不使用。
罗恩侯爵,

谢谢!getUnsafeOkHttpClient()在Kotlin中重写了该方法:stackoverflow.com/a/60507560/2914140
CoolMind

11

回复很旧的帖子。但这可能会帮助一些新手,如果以上方法都不可行。

说明:我知道没人要废话。而不是解决方案。但是,在一台服务器中,您试图将服务从本地计算机访问到不信任您计算机的远程计算机。您请求需要获得远程服务器的信任。

解决方案:以下解决方案假定您满足以下条件

  1. 尝试从本地计算机访问远程api。
  2. 您正在构建Android应用程序
  3. 远程服务器受到代理过滤(您在浏览器设置中使用代理访问远程api服务,通常是登台服务器或开发服务器)
  4. 您正在真实设备上进行测试

脚步:

您需要一个.keystore扩展文件来注册您的应用。如果您不知道如何创建.keystore文件,请执行以下操作:然后按照以下部分创建.keystore文件,否则请跳至下一部分对Apk文件进行签名

创建.keystore文件

打开Android Studio。单击顶部菜单生成>生成签名的APK。在下一个窗口中,单击创建新...按钮。在新窗口中,请在所有字段中输入数据。记住我建议的两个密码字段应该具有相同的密码;请勿使用其他密码;并记住最上方字段“ 密钥存储路径”中的保存路径。输入所有字段后,单击确定按钮。

签名apk文件

现在,您需要使用刚刚创建的.keystore文件构建一个已签名的应用程序。跟着这些步骤

  1. Build> Clean Project,等到完成清理
  2. 生成>生成签名的APK
  3. 点击Choose existing...按钮
  4. 创建.keystore文件部分中选择我们刚刚创建的.keystore文件
  5. 输入在“ 创建.keystore文件”部分中创建时创建的相同密码。为Key store password和使用相同的密码Key password字段。同时输入别名
  6. 单击下一步按钮
  7. 在下一个屏幕中;根据build.gradle文件中的设置可能有所不同,您需要选择Build TypesFlavors
  8. 对于Build Types选择release从下拉菜单中
  9. 对于Flavors然而,它会在依赖于你的设置build.gradle文件。staging从此字段中选择。我在中使用了以下设置build.gradle,您可以使用与我相同的设置,但请确保将更applicationId改为包名

    productFlavors {
        staging {
            applicationId "com.yourapplication.package"
            manifestPlaceholders = [icon: "@drawable/ic_launcher"]
            buildConfigField "boolean", "CATALYST_DEBUG", "true"
            buildConfigField "boolean", "ALLOW_INVALID_CERTIFICATE", "true"
        }
        production {
            buildConfigField "boolean", "CATALYST_DEBUG", "false"
            buildConfigField "boolean", "ALLOW_INVALID_CERTIFICATE", "false"
        }
    }
  10. 单击底部的两个Signature Versions复选框,然后单击Finish按钮。

差不多了:

所有的努力都完成了,现在是真理的运动。为了访问由代理备份的Staging服务器,您需要在实际测试的Android设备中进行一些设置。

Android设备中的代理设置:

  1. 点击Android手机中的设置,然后点击wi-fi
  2. 长按连接的wifi并选择 Modify network
  3. Advanced options如果看不到该Proxy Hostname字段,请单击
  4. Proxy Hostname输入中,您要连接的主机IP或名称。典型的登台服务器将被命名为stg.api.mygoodcompany.com
  5. 对于端口,请输入四位数的端口号,例如 9502
  6. 点击Save按钮

最后一站:

请记住,我们在“签署APK文件”部分生成了签署的apk文件。现在是时候安装该APK文件了。

  1. 打开终端并更改为签名的apk文件夹
  2. 将您的Android设备连接到计算机
  3. 从Android设备中删除任何以前安装的apk文件
  4. adb install name of the apk file
  5. 如果由于某种原因上述命令返回adb command not found。输入完整路径为C:\Users\shah\AppData\Local\Android\sdk\platform-tools\adb.exe install name of the apk file

我希望这个问题可以解决。如果没有,请给我留言。

萨拉姆!


4

我收到的错误消息类似,但是原因是自签名证书已过期。当尝试使用openssl客户端时,它给了我从firefox检查证书对话框时被忽略的原因。

因此,通常,如果证书位于密钥库中,并且证书的证书为“ VALID”,则此错误将消失。


1
如果证书已过期,则根据许多标准,该证书无效。使用自定义TrustManager并使用不同的条件集也可以。但是,开箱即用,这就是您需要使用的。
jww

4

从Android客户端连接到Kurento服务器时,我遇到了同样的问题。Kurento服务器使用jks证书,因此我不得不将pem转换为它。作为转换的输入,我使用了cert.pem文件,它导致了此类错误。但是,如果使用fullchain.pem而不是cert.pem-一切正常。


3

使用https://www.ssllabs.com/ssltest/测试域。

Shihab Uddin在Kotlin中的解决方案。

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 = getUnsafeOkHttpClient() // OkHttpClient().newBuilder()
            .build()

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

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

    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)
        }
}

2

我遇到了同样的问题,我发现的是我提供的certificate .crt文件缺少中间证书。因此,我从服务器管理员那里询问了所有.crt文件,然后以相反的顺序对其进行了隐化处理。

例如 1. Root.crt 2. Inter.crt 3. myCrt.crt

在Windows中,我执行了复制Inter.crt + Root.crt newCertificate.crt

(这里我忽略了myCrt.crt)

然后,我通过inputstream将newCertificate.crt文件提供给代码。完成工作。


我们也有同样的问题。中级证书丢失
Nidhin Chandran

1

信任锚错误可能由于多种原因而发生。对我来说,只是我尝试访问https://example.com/而不是https://www.example.com/

因此,您可能想在开始构建自己的信任管理器之前仔细检查您的URL(就像我一样)。


0

在姜饼手机中,Trust Anchor not found for Android SSL Connection即使我设置为依靠我的证书,也总是会收到此错误:。

这是我使用的代码(使用Scala语言):

object Security {
    private def createCtxSsl(ctx: Context) = {
        val cer = {
            val is = ctx.getAssets.open("mycertificate.crt")
            try
                CertificateFactory.getInstance("X.509").generateCertificate(is)
            finally
                is.close()
        }
        val key = KeyStore.getInstance(KeyStore.getDefaultType)
        key.load(null, null)
        key.setCertificateEntry("ca", cer)

        val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
    tmf.init(key)

        val c = SSLContext.getInstance("TLS")
        c.init(null, tmf.getTrustManagers, null)
        c
    }

    def prepare(url: HttpURLConnection)(implicit ctx: Context) {
        url match {
            case https: HttpsURLConnection 
                val cSsl = ctxSsl match {
                    case None 
                        val res = createCtxSsl(ctx)
                        ctxSsl = Some(res)
                        res
                    case Some(c)  c
                }
                https.setSSLSocketFactory(cSsl.getSocketFactory)
            case _ 
        }
    }

    def noSecurity(url: HttpURLConnection) {
        url match {
            case https: HttpsURLConnection 
                https.setHostnameVerifier(new HostnameVerifier {
                    override def verify(hostname: String, session: SSLSession) = true
                })
            case _ 
        }
    }
}

这是连接代码:

def connect(securize: HttpURLConnection  Unit) {
    val conn = url.openConnection().asInstanceOf[HttpURLConnection]
    securize(conn)
    conn.connect();
    ....
}

try {
    connect(Security.prepare)
} catch {
    case ex: SSLHandshakeException /*if ex.getMessage != null && ex.getMessage.contains("Trust anchor for certification path not found")*/ 
        connect(Security.noSecurity)
}

基本上,我设置为信任我的自定义证书。如果失败,则禁用安全性。这不是最好的选择,但我知道使用过时的越野车的唯一选择。

此示例代码可以轻松转换为Java。


Android 2.3.x的解决方案,以防万一有人需要它,stackoverflow.com
a/46465722/7125370

0

就我而言,这是在更新至Android 8.0之后发生的。Android设置为信任的自签名证书使用的是签名算法SHA1withRSA。使用签名算法SHA256withRSA切换到新证书可以解决此问题。


0

我知道您不需要信任所有证书,但就我而言,在某些调试环境中我遇到了问题,在这些调试环境中我们拥有自签名证书,因此我需要一个肮脏的解决方案。

我要做的就是更改 sslContext

mySSLContext.init(null, trustAllCerts, null); 

这样trustAllCerts创建的位置:

private final TrustManager[] trustAllCerts= new TrustManager[] { new X509TrustManager() {
    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return new java.security.cert.X509Certificate[]{};
    }

    public void checkClientTrusted(X509Certificate[] chain,
                                   String authType) throws CertificateException {
    }

    public void checkServerTrusted(X509Certificate[] chain,
                                   String authType) throws CertificateException {
    }
} };

希望这会派上用场。


那就是我想要找到的!谢谢
oxied

我出错了Hostname '192.168.0.16' was not verified。我正在通过调试器(IIS Express)在本地测试我的webapi。任何想法如何解决这个问题?谢谢:)
山姆

1
这是完全不安全的。不使用。
罗恩侯爵,

0

我遇到了类似的问题,并且完全排除了信任所有来源的策略。

我在这里分享我的解决方案应用于Kotlin中实现的应用程序

我首先建议您使用以下网站来获取有关证书及其有效性的信息

如果未在Android默认信任存储中显示为“接受的发行者”颁发者”,我们必须获取该证书并将其合并到应用程序中以创建自定义信任库

就我而言,理想的解决方案是创建一个高级信任管理器结合了自定义和Android默认信任存储

在这里,他展示了用于配置与Retrofit一起使用的OkHttpClient的高级代码。

override fun onBuildHttpClient(httpClientBuild: OkHttpClient.Builder) {

        val trustManagerWrapper = createX509TrustManagerWrapper(
            arrayOf(
                getCustomX509TrustManager(),
                getDefaultX509TrustManager()
            )
        )

        printX509TrustManagerAcceptedIssuers(trustManagerWrapper)

        val sslSocketFactory = createSocketFactory(trustManagerWrapper)
        httpClientBuild.sslSocketFactory(sslSocketFactory, trustManagerWrapper)

    }

通过这种方式,我可以通过自签名证书与服务器通信,并通过受信任的认证实体发布的证书与其他服务器通信

就是这样,希望它能对某人有所帮助。


0

我知道这是一篇非常古老的文章,但是当我尝试解决我的信任锚问题时遇到了这篇文章。我已经发布了解决方法。如果您已经预安装了根CA,则需要向清单添加配置。

https://stackoverflow.com/a/60102517/114265


仅适用于API 24+
BekaBot

@BekaBot-可能是正确的,但是根据文档23及以下版本,默认情况下信任用户证书默认情况下,所有应用程序的安全连接(使用TLS和HTTPS等协议)信任预安装的系统CA和针对Android 6.0的应用程序(API级23),并且还降低默认信任用户添加的CA存储
GR特使

-1
**Set proper alias name**
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509","BC");
            X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(derInputStream);
            String alias = cert.getSubjectX500Principal().getName();
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(null);
trustStore.setCertificateEntry(alias, cert);

-2

我也面临同样的问题。我只是将hhtps删除到http,例如

final public static String ROOT_URL = "https://example.com";final public static String ROOT_URL = "http://example.com";

最后,我解决了这个问题。


当然,这只是从等式中删除了SSL安全性,对于生产环境而言,这可能不是一个好主意。
Dragonthoughts

删除SSL安全性确实不是解决方案。
omega1
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.