如何解决 javax.net.ssl.SSLHandshakeException 错误?

Posted

技术标签:

【中文标题】如何解决 javax.net.ssl.SSLHandshakeException 错误?【英文标题】:How to solve javax.net.ssl.SSLHandshakeException Error? 【发布时间】:2011-10-03 07:32:24 【问题描述】:

我连接了 *** 以设置库存 API 以获取产品列表,它工作正常。一旦我从 Web 服务获得结果并绑定到 UI。此外,当我拨打付款电话时,我将 PayPal 与我的应用程序进行了快速结帐,我正面临此错误。我将 servlet 用于后端进程。谁能说说如何解决这个问题?

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: 
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target

【问题讨论】:

我想知道的是,在那种情况下,确切的目标是什么......你得到了一个例外,但你没有得到关于目标的信息,这可能与你的期望不同......我有这种情况下,我确定我的链接有证书,但我仍然收到此异常 【参考方案1】:

首先,您需要从您尝试连接的服务器获取公共证书。这可以通过多种方式完成,例如联系服务器管理员并询问它,using OpenSSL to download it,或者,因为这似乎是一个 HTTP 服务器,所以使用任何浏览器连接到它,查看页面的安全信息,以及保存证书的副本。 (Google 应该能够准确地告诉您如何针对您的特定浏览器执行操作。)

现在您已将证书保存在文件中,您需要将其添加到 JVM 的信任库中。在 JRE 的 $JAVA_HOME/jre/lib/security/ 或 JDK 的 $JAVA_HOME/lib/security 处,有一个名为 cacerts 的文件,它随 Java 一起提供并包含知名认证机构的公共证书。要导入新证书,请以有权写入 cacerts 的用户身份运行 keytool:

keytool -import -file <the cert file> -alias <some meaningful name> -keystore <path to cacerts file>

它很可能会要求您输入密码。 Java 附带的默认密码是changeit。几乎没有人改变它。完成这些相对简单的步骤后,您将可以安全地进行通信,并确保您正在与正确的服务器进行通信,并且只与正确的服务器通信(只要它们不会丢失他们的私钥)。

【讨论】:

我需要比“不工作”更多的细节。尝试使用您尝试过的内容和一些错误输出来更新您的问题。不幸的是,现在已经过了我的就寝时间,所以也许其他人能够回答你的问题。这也是很常见的情况。你可以在网上找到很多关于它的信息,包括keytool docs。 基本上我需要包括 PayPal 证书。我做了你的步骤,证书也添加到了适当的位置。然后我运行应用程序它说同样的错误? 实际上它运行良好,浏览器没有问题,我连接了 *** 以获取库存清单,当时我使用 PayPal 付款,它说是 SSL 错误,但我使用离线数据或热编码数据意味着它可以工作。 @selladurai:您不需要为贝宝导入证书。除非您的 cacerts 文件已损坏或修改,否则它应该包含所有受信任的根证书,并且 paypal 的证书应该能够追溯到其中之一。您可以尝试获取一份您知道很好的 ​​cacerts 副本,然后尝试使用该副本。如果问题仍然存在,那么您实际上可能没有连接到贝宝。 @RyanStewart 我需要以某种方式将其导入 glassfish 吗?因为我确实将 .cer 文件添加到我的 java 主目录中的 cacert 但 glassfish 似乎没有使用它所以我仍然收到错误。【参考方案2】:

现在我用这种方式解决了这个问题,

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.OutputStream; 

// Create a trust manager that does not validate certificate chains like the default 

TrustManager[] trustAllCerts = new TrustManager[]
        new X509TrustManager() 

            public java.security.cert.X509Certificate[] getAcceptedIssuers()
            
                return null;
            
            public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
            
                //No need to implement.
            
            public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
            
                //No need to implement.
            
        
;

// Install the all-trusting trust manager
try 

    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
 
catch (Exception e) 

    System.out.println(e);

当然,此解决方案仅应在无法使用 keytool 安装所需证书的情况下使用,例如使用临时证书进行本地测试。

【讨论】:

哇,我会毫不犹豫地称其为“修复”。您基本上只是关闭了安全性。是的,数据仍将被加密,但您无法保证您正在与您希望与之交谈的人交谈。正确的解决方案是获取目标服务器的公钥并将其导入建立连接的 JVM 的信任库。 查看我的answer 以获得合适的解决方案 什么例子?我的回答应该有你需要的所有步骤。 nakov.com/blog/2009/07/16/… 如果您正在针对开发服务器编写一个非常简单的测试,此处显示的代码看起来很有帮助,但我不会依赖它来进行生产。【参考方案3】:

每当我们尝试连接到 URL 时,

如果其他站点的服务器在 https 协议上运行,并且要求我们应该通过证书中提供的信息进行通信,那么 我们有以下选择:

1) 索取证书(下载证书),将此证书导入trustore。 Java默认的trustore使用可以在\Java\jdk1.6.0_29\jre\lib\security\cacerts中找到,然后如果我们重试连接到URL连接将被接受。

2) 在正常的业务案例中,我们可能会连接到组织中的内部 URL,并且我们知道它们是正确的。 在这种情况下,您相信它是正确的 URL,在上述这种情况下,可以使用不会强制存储证书以连接到特定 URL 的代码。

对于第 2 点,我们必须遵循以下步骤:

1) 编写下面的方法,为 HttpsURLConnection 设置 HostnameVerifier,该方法在所有情况下都返回 true,这意味着我们信任 trustStore。

  // trusting all certificate 
 public void doTrustToCertificates() throws Exception 
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        TrustManager[] trustAllCerts = new TrustManager[]
                new X509TrustManager() 
                    public X509Certificate[] getAcceptedIssuers() 
                        return null;
                    

                    public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException 
                        return;
                    

                    public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException 
                        return;
                    
                
        ;

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HostnameVerifier hv = new HostnameVerifier() 
            public boolean verify(String urlHostName, SSLSession session) 
                if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) 
                    System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
                
                return true;
            
        ;
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
    

2) 编写下面的方法,在尝试连接到 URL 之前调用 doTrustToCertificates

    // connecting to URL
    public void connectToUrl()
     doTrustToCertificates();//  
     URL url = new URL("https://www.example.com");
     HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 
     System.out.println("ResponseCode ="+conn.getResponseCode());
   

此调用将返回响应码 = 200 表示连接成功。

更多细节和示例可以参考URL。

【讨论】:

Google Play 商店会拒绝这个。他们会看到您在 APK 扫描期间忽略了 X509Certificate。【参考方案4】:

SSLHandshakeException 可以通过两种方式解决。

    结合 SSL

    获取 SSL(通过询问源系统管理员,也可以 通过 openssl 命令下载,或者任何浏览器下载 证书)

    将证书添加到位于以下位置的信任库 (cacerts) JRE/lib/安全

    在 vm 参数中提供信任库位置为 "-Djavax.net.ssl.trustStore="

    忽略 SSL

    对于这个 #2,请访问我在另一个 *** 网站上的其他答案: How to ingore SSL verification Ignore SSL Certificate Errors with Java

【讨论】:

【参考方案5】:

我相信您正在尝试使用 SSL 连接到某个东西,但某些东西提供的证书未经根证书颁发机构(例如 verisign)验证。本质上,默认情况下,只有尝试的人才能建立安全连接连接知道交易对手密钥或其他一些验证者(例如威瑞信)可以介入并说所提供的公钥确实是正确的..

所有操作系统都信任少数证书颁发机构和较小的证书颁发者,如果您明白我的意思,则需要由大型认证机构之一进行认证,从而形成一系列认证机构......

回到正题。我在编写 java 小程序和 java 服务器时遇到了类似的问题(希望有一天我会写一篇完整的博文,讲述我是如何让所有安全性工作的:))

本质上,我要做的是从服务器中提取公钥并将其存储在我的小程序内的密钥库中,当我连接到服务器时,我使用该密钥库创建了一个信任工厂,该信任工厂创建 ssl 连接。还有一些替代过程,例如将密钥添加到 JVM 的受信任主机并在启动时修改默认信任存储..

我大约两个月前做了这个,现在没有源代码。使用谷歌,你应该能够解决这个问题。如果你不能给我发消息,我可以为你提供项目的相关源代码。不知道这是否解决了你的问题,因为你没有提供导致这些异常的代码。此外,我正在使用小程序,我认为我不明白为什么它不能在 Serverlets 上工作......

P.S 由于我的办公室禁用了外部 SSH,因此我无法在周末之前获得源代码 :(

【讨论】:

P.S 我在网上找到了这个 java 代码来提取和存储我的服务器的公钥,所以请继续谷歌搜索或等到周日【参考方案6】:

现在我用这种方式解决了这个问题,

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.OutputStream;
// Create a trust manager that does not validate certificate chains like the 
default TrustManager[] trustAllCerts = new TrustManager[] 
    new X509TrustManager() 
        public java.security.cert.X509Certificate[] getAcceptedIssuers() 
            return null;
        
        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) 
            //No need to implement. 
        
        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) 
            //No need to implement. 
        
    
;
// Install the all-trusting trust manager
try 
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
 catch (Exception e) 
    System.out.println(e);

【讨论】:

“无需实施”是完全不正确的。您刚刚使您的 SSL 连接不安全。不要这样做。 @user207421 你是对的。但这取决于用例是代码正在实现。考虑一个应该尽可能多地获取所有页面的网络爬虫。在这种情况下,不会因 SSL 问题而失败。因此,在这些情况下,trust all 策略是一个很好的策略。

以上是关于如何解决 javax.net.ssl.SSLHandshakeException 错误?的主要内容,如果未能解决你的问题,请参考以下文章

云原生景观:可观察性和分析解决了什么问题?如何解决的?

如何解决 Ajax 跨域请求不到的问题

如何解决包冲突问题

如何解决包冲突问题

如何解决ajax跨域问题

MySQL 的 10048问题,如何解决?