"PKIX路径构建失败" 和 "无法找到请求目标的有效认证路径"

892

我正在尝试在我的Java项目中使用twitter4j库获取推文,在底层使用的是java.net.HttpURLConnection(如堆栈跟踪所示)。在第一次运行时,我遇到了关于证书sun.security.validator.ValidatorExceptionsun.security.provider.certpath.SunCertPathBuilderException的错误。然后我通过以下方式添加了Twitter证书:

C:\Program Files\Java\jdk1.7.0_45\jre\lib\security>keytool -importcert -trustcacerts -file PathToCert -alias ca_alias -keystore "C:\Program Files\Java\jdk1.7.0_45\jre\lib\security\cacerts"

但是没有成功。这里是获取推文的过程:

public static void main(String[] args) throws TwitterException {
    ConfigurationBuilder cb = new ConfigurationBuilder();
    cb.setDebugEnabled(true)
        .setOAuthConsumerKey("myConsumerKey")
        .setOAuthConsumerSecret("myConsumerSecret")
        .setOAuthAccessToken("myAccessToken")
        .setOAuthAccessTokenSecret("myAccessTokenSecret");
    
    TwitterFactory tf = new TwitterFactory(cb.build());
    Twitter twitter = tf.getInstance();
    
    try {
        Query query = new Query("iphone");
        QueryResult result;
        result = twitter.search(query);
        System.out.println("Total amount of tweets: " + result.getTweets().size());
        List<Status> tweets = result.getTweets();
        
        for (Status tweet : tweets) {
            System.out.println("@" + tweet.getUser().getScreenName() + " : " + tweet.getText());
        }
    } catch (TwitterException te) {
        te.printStackTrace();
        System.out.println("Failed to search tweets: " + te.getMessage());
    }

这里是错误:

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Relevant discussions can be found on the Internet at:
    http://www.google.co.jp/search?q=d35baff5 or
    http://www.google.co.jp/search?q=1446302e
TwitterException{exceptionCode=[d35baff5-1446302e 43208640-747fd158 43208640-747fd158 43208640-747fd158], statusCode=-1, message=null, code=-1, retryAfter=-1, rateLimitStatus=null, version=3.0.5}
    at twitter4j.internal.http.HttpClientImpl.request(HttpClientImpl.java:177)
    at twitter4j.internal.http.HttpClientWrapper.request(HttpClientWrapper.java:61)
    at twitter4j.internal.http.HttpClientWrapper.get(HttpClientWrapper.java:81)
    at twitter4j.TwitterImpl.get(TwitterImpl.java:1929)
    at twitter4j.TwitterImpl.search(TwitterImpl.java:306)
    at jku.cc.servlets.TweetsAnalyzer.main(TweetsAnalyzer.java:38)
Caused by: 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
    at sun.security.ssl.Alerts.getSSLException(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
    at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
    at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
    at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
    at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
    at sun.security.ssl.Handshaker.processLoop(Unknown Source)
    at sun.security.ssl.Handshaker.process_record(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
    at java.net.HttpURLConnection.getResponseCode(Unknown Source)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(Unknown Source)
    at twitter4j.internal.http.HttpResponseImpl.<init>(HttpResponseImpl.java:34)
    at twitter4j.internal.http.HttpClientImpl.request(HttpClientImpl.java:141)
    ... 5 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
    at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
    at sun.security.validator.Validator.validate(Unknown Source)
    at sun.security.ssl.X509TrustManagerImpl.validate(Unknown Source)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(Unknown Source)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
    ... 20 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
    at java.security.cert.CertPathBuilder.build(Unknown Source)
    ... 26 more
Failed to search tweets: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

11
你好,请检查下面的网址。我相信这些将对你有所帮助。http://www.java-samples.com/showtutorial.php?tutorialid=210 https://confluence.atlassian.com/display/JIRAKB/Unable+to+Connect+to+SSL+Services+due+to+PKIX+Path+Building+Failed+sun.security.provider.certpath.SunCertPathBuilderException。你需要将你的SSL证书添加到Java信任库证书中(路径:jre/lib/security/cacerts)。 - sus007
4
我刚刚使用了这段 Java 代码,对于 https,请不要忘记将端口指定为 443。 Java 代码位于 https://github.com/escline/InstallCert/blob/master/InstallCert.java它将使用您的 CACERTS 文件,并将添加所有那些以及您输入的 URL 的当前证书。 在我的情况下,我将值硬编码为 host="mywebservice.uat.xyz.com"; port=443; passphrase="changeit".toCharArray();然后,程序会创建一个名为“jssecacerts”的新文件,其中将包含所有内容。将其重命名为“cacerts”并使用此文件。您就可以开始了。 - Reddymails
我只是将HTTPS替换为HTTP,它就能正常工作了。也许我的评论对某人有用。 - Oleksandr
当运行mvn时,添加此参数对我有帮助,因为它由于某种原因未使用JAVA_HOME中的默认Java信任存储:-"Djavax.net.ssl.trustStore"="C:\path\to\your\cacerts" - ADJenks
显示剩余2条评论
60个回答

1102
  1. 在浏览器中输入URL:
  • Firefox - 点击HTTPS证书链(URL地址旁边的锁形图标),点击“更多信息”>“安全”>“显示证书”>“详细信息”>“导出..”。选择文件类型和文件名,例如example.cer。
  • Chrome - 点击地址栏左侧的网站图标,选择“证书”->“详细信息”->“导出”,并以“Der编码的二进制格式,单个证书”的格式保存。
  1. 现在你有了keystore文件,需要将其添加到JVM中。确定cacerts文件的位置,例如: C:\Program Files (x86)\Java\jre1.6.0_22\lib\security\cacerts。

  2. 接下来,在命令行中将example.cer文件导入cacerts(可能需要管理员权限):

keytool -import -alias example -keystore "C:\Program Files (x86)\Java\jre1.6.0_22\lib\security\cacerts" -file example.cer

您将被要求输入密码,默认为changeit

重启JVM/PC。

来源: http://magicmonster.com/kb/prg/java/ssl/pkix_path_building_failed.html


50
我必须将路径用引号括起来,并且将其保存为Base64而不是DER格式。 - Theodore K.
5
哇...它就像魔术一样奏效了。顺便说一下,在Chrome中,您只需单击左侧地址栏上的锁图标,然后单击“详细信息”。另外,无需重新启动。 - aswzen
18
请注意,需要为证书链中的所有证书重复这些指令。此外,命令行中证书的“别名”名称应该是唯一的。 - Ilya Serbis
10
如果你的电脑上有安装JDK,请确保指定JRE的位置:keytool -import -alias example -keystore "C:\Program Files\Java\jdk1.8.0_73\jre\lib\security\cacerts" -file example.cer。注意,这里的路径是示例路径,需要根据你自己的实际情况进行修改。 - Jack BeNimble
30
如果你遇到“访问被拒绝”的错误提示,请确保你以管理员身份运行命令提示符。 - Walker Christie
显示剩余39条评论

124

在尝试构建证书文件以使我的 Java 6 版本能够与新的 Twitter 证书配合使用的许多小时后,我最终在留言板的评论中发现了一个非常简单的解决方案。只需从 Java 7 安装中复制 cacerts 文件并覆盖 Java 6 安装中的文件即可。最好先备份 cacerts 文件,然后只需复制新的文件,BOOM!就可以正常工作了。

请注意,我实际上将 Windows cacerts 文件复制到 Linux 安装中,并且它完美地工作。

该文件位于旧和新的 Java jdk 安装中的 jre/lib/security/cacerts 中。

希望这可以为其他人节省几个小时的麻烦。


5
我想访问一个使用自签名证书的 SSL 服务器,尝试使用 keytool 添加它的证书,但没有成功,你有什么建议吗? - Oleg Belousov
1
很高兴它起作用了。但是你知道根本原因吗?是什么导致它在Java 6证书上失败?Java 7证书又是如何解决这个问题的? - Vishwanath gowda k
这样做就可以了,尽管我从icedtea 1.6升级到oracle 1.7。 :) - mike3996
还要考虑这个解决方案:https://dev59.com/51wX5IYBdhLWcg3wvRkf - Piohen
23
对于像我一样有时会忽略显而易见事实的人,请确保您进入 $JAVA_HOME/jre/lib 而不是 $JAVA_HOME/lib -- 我花了一些时间才意识到这个细节。 - Ryan Heathcote
@Vishwanathgowdak 可能的根本原因就是较新的JRE内置了更新的证书,而这些证书在旧版本发布时并不存在。 - Amit Naidu

107

我的UI方案:

  1. 这里下载密钥库浏览器
  2. 打开$JAVA_HOME/jre/lib/security/cacerts
  3. 输入密码: changeit (在Mac上可以是changeme)
  4. 导入你的.crt文件

命令行:

  1. keytool -importcert -file jetty.crt -alias jetty -keystore $JAVA_HOME/jre/lib/security/cacerts
  2. 输入密码:changeit(在Mac上可以是changeme)

3
在Mac上,使用终端命令行时,应该使用sudo来运行该命令。# sudo keytool -importcert -file jetty.crt -alias jetty -keystore $JAVA_HOME/jre/lib/security/cacerts - malajisi
2
在Windows上,以下命令可行:keytool -importcert -file dinardap_cert.cer –alias dinardap –keystore “%JAVA_HOME%/jre/lib/security/cacerts” - Gabriel Patricio Bonilla
从哪里获取证书文件?我不知道要访问哪个URL。https://www.jetty.com? - kraftydevil
1
我通过在网页浏览器上导出它并在锁形图标上执行上下文操作来获取我的证书。 - rtbf
1
输入密钥库密码...? - FlipNovid
显示剩余4条评论

70

1. 检查证书

尝试在浏览器中加载目标URL并查看网站的证书(通常通过锁形图标访问,位于浏览器地址栏的左侧或右侧),检查其是否过期或因其他原因不受信任。

2. 安装最新版本的JRE和JDK

新版本通常带有更新的可信证书集合。

此外,如果可能的话,卸载旧版本。这将使配置错误明显。

3. 检查您的配置:

  • 检查JAVA_HOME环境变量的指向。
  • 检查您用来运行程序的Java版本。在IntelliJ中,请检查:
    • 文件 -> 项目结构... -> 项目设置 -> 项目 -> 项目SDK:
    • 文件 -> 项目结构... -> 平台设置 -> SDKs

4. 从新的Java版本复制整个密钥库

如果您使用的JDK不是最新可用版本,请尝试用最新安装的JRE中的新文件替换%JAVA_HOME%/jre/lib/security/cacerts文件(首先备份副本),如@jeremy-goodell在他的回答中建议的那样。

5. 将证书添加到密钥库

如果以上方法都无法解决问题,请使用keytool将证书保存到Java的密钥库中:

keytool -trustcacerts -keystore "%JAVA_HOME%jre\lib\security\cacerts" -storepass changeit -importcert -alias <alias_name> -file <path_to_crt_file>

按照 @MagGGG 在他的 答案 中所建议的,在浏览器中可获取证书文件。

注意1:您可能需要为链中每个证书重复此过程,以获取站点证书。从根证书开始。

注意2:<alias_name> 应在存储器中的密钥中唯一,否则会出现 keytool 错误。

若要获取存储器中所有证书的列表,可以运行以下命令:

keytool -list -trustcacerts -keystore "%JAVA_HOME%jre\lib\security\cacerts" -storepass changeit

如果出现问题,这将帮助您从存储中删除证书:

keytool -delete -alias <alias_name> -keystore "%JAVA_HOME%jre\lib\security\cacerts" -storepass changeit

6
这篇回答非常详尽,应该被接受作为答案。 - Egor Hans
在 Mac 上应该是 keytool -list -trustcacerts -keystore "$JAVA_HOME/jre/lib/security/cacerts" -storepass changeit - Francis Bacon
我试图访问一个使用LetsEncrypt生成证书的网站,并升级到Java版本> = 8u101后问题得到解决 - https://letsencrypt.org/docs/certificate-compatibility/ - paulcm

56
-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true

它用于跳过证书验证。

警告!

仅在开发目的下使用,因为此方法不安全!


2
@gforums 如何在Eclipse中设置它? - malajisi
18
除非你想冒着在你的电脑上运行任意恶意代码的风险,否则请不要这样做。由于存在危险,在运行此程序时可能会损害任何计算机,因此被贴上了反对的标记。 - Bryan
2
仅仅因为代码“可用”并不意味着它是安全的,遵循这个建议的每个人都会面临风险。我会点个踩。 - Paradoxis
14
关键是如果您不验证证书,它就不是可信代码。尽管如此,我认为这仍然是一个有用的答案。只要理解风险,人们就可以这样做。 - Michael Mior
4
这相当危险,如果你采取这种方法,你会使自己容易受到中间人攻击。证书验证的主要目的之一是,如果有人劫持了URL,你知道这种情况发生时,并不会造成灾难性后果。 - Michael Mior
显示剩余6条评论

33

Mac

由于Mac(Chrome或Firefox)中没有可用的导出按钮,因此接受的答案无法在Mac上使用。请查看此答案以下载证书并按照下面提到的步骤进行操作:

  1. 列出密钥库中安装的所有证书
cd $JAVA_HOME/lib/security
keytool -list -keystore cacerts

注意:

  • 密钥库的默认密码是:changeit
  • 对于Java-8或更低版本,请使用命令cd $JAVA_HOME/jre/lib/security
  1. 在将证书导入密钥库之前,请备份密钥库
sudo cp cacerts cacerts.bak
  1. 将下载的证书导入密钥库中
sudo keytool -importcert -alias youralias -file /path/to/the/downloaded/certificate -keystore cacerts
  • 检查证书是否已存储在密钥库中:
  • sudo keytool -list -keystore cacerts -alias youralias
    

    如果您想查看更详细的信息,请加上-v标志:

    sudo keytool -v -list -keystore cacerts -alias youralias
    

    如果我需要证书才能从Mac访问本地主机,如何生成证书? - Navin
    1
    这个答案对我有用。关于证书,当我们在公司代理后面运行时,我遇到了这个问题。在我的情况下,是zscaler。 打开钥匙串访问,导出CA证书。(选择CA证书,文件->导出项目并使用所需的名称保存) - Sahib Yar

    31

    我遇到了这个问题,为了解决它花费了很多时间,特别是对于自动生成的证书来说。与官方证书不同,它们相当棘手,而且Java并不太喜欢它们。

    请参考以下链接:解决Java中的证书问题

    基本上,您需要将服务器的证书添加到Java Home证书中。

    1. 生成或获取您的证书,并在Servers.xml中配置Tomcat以使用它
    2. 下载类InstallCert的Java源代码,并在服务器运行时执行它,提供以下参数server[:port]。不需要密码,因为原始密码适用于Java证书("changeit")。
    3. 该程序将连接到服务器,Java会抛出一个异常,它会分析服务器提供的证书并允许您在执行程序的目录中创建一个jssecerts文件(如果从Eclipse中执行,则确保您在Run -> Configurations中配置了工作目录)。
    4. 手动将该文件复制到$JAVA_HOME/jre/lib/security

    按照这些步骤后,使用证书的连接在Java中将不会再生成异常。

    以下源代码很重要,它已从(Sun)Oracle博客中消失了,我在提供的链接上找到了它,因此我将其附加在答案中供参考。

    /*
     * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     *   - Redistributions of source code must retain the above copyright
     *     notice, this list of conditions and the following disclaimer.
     *
     *   - Redistributions in binary form must reproduce the above copyright
     *     notice, this list of conditions and the following disclaimer in the
     *     documentation and/or other materials provided with the distribution.
     *
     *   - Neither the name of Sun Microsystems nor the names of its
     *     contributors may be used to endorse or promote products derived
     *     from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
     * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    /**
     * Originally from:
     * http://blogs.sun.com/andreas/resource/InstallCert.java
     * Use:
     * java InstallCert hostname
     * Example:
     *% java InstallCert ecc.fedora.redhat.com
     */
    
    import javax.net.ssl.*;
    import java.io.*;
    import java.security.KeyStore;
    import java.security.MessageDigest;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    
    /**
     * Class used to add the server's certificate to the KeyStore
     * with your trusted certificates.
     */
    public class InstallCert {
    
        public static void main(String[] args) throws Exception {
            String host;
            int port;
            char[] passphrase;
            if ((args.length == 1) || (args.length == 2)) {
                String[] c = args[0].split(":");
                host = c[0];
                port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
                String p = (args.length == 1) ? "changeit" : args[1];
                passphrase = p.toCharArray();
            } else {
                System.out.println("Usage: java InstallCert [:port] [passphrase]");
                return;
            }
    
            File file = new File("jssecacerts");
            if (file.isFile() == false) {
                char SEP = File.separatorChar;
                File dir = new File(System.getProperty("java.home") + SEP
                        + "lib" + SEP + "security");
                file = new File(dir, "jssecacerts");
                if (file.isFile() == false) {
                    file = new File(dir, "cacerts");
                }
            }
            System.out.println("Loading KeyStore " + file + "...");
            InputStream in = new FileInputStream(file);
            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
            ks.load(in, passphrase);
            in.close();
    
            SSLContext context = SSLContext.getInstance("TLS");
            TrustManagerFactory tmf =
                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(ks);
            X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
            SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
            context.init(null, new TrustManager[]{tm}, null);
            SSLSocketFactory factory = context.getSocketFactory();
    
            System.out.println("Opening connection to " + host + ":" + port + "...");
            SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
            socket.setSoTimeout(10000);
            try {
                System.out.println("Starting SSL handshake...");
                socket.startHandshake();
                socket.close();
                System.out.println();
                System.out.println("No errors, certificate is already trusted");
            } catch (SSLException e) {
                System.out.println();
                e.printStackTrace(System.out);
            }
    
            X509Certificate[] chain = tm.chain;
            if (chain == null) {
                System.out.println("Could not obtain server certificate chain");
                return;
            }
    
            BufferedReader reader =
                    new BufferedReader(new InputStreamReader(System.in));
    
            System.out.println();
            System.out.println("Server sent " + chain.length + " certificate(s):");
            System.out.println();
            MessageDigest sha1 = MessageDigest.getInstance("SHA1");
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            for (int i = 0; i < chain.length; i++) {
                X509Certificate cert = chain[i];
                System.out.println
                        (" " + (i + 1) + " Subject " + cert.getSubjectDN());
                System.out.println("   Issuer  " + cert.getIssuerDN());
                sha1.update(cert.getEncoded());
                System.out.println("   sha1    " + toHexString(sha1.digest()));
                md5.update(cert.getEncoded());
                System.out.println("   md5     " + toHexString(md5.digest()));
                System.out.println();
            }
    
            System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
            String line = reader.readLine().trim();
            int k;
            try {
                k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
            } catch (NumberFormatException e) {
                System.out.println("KeyStore not changed");
                return;
            }
    
            X509Certificate cert = chain[k];
            String alias = host + "-" + (k + 1);
            ks.setCertificateEntry(alias, cert);
    
            OutputStream out = new FileOutputStream("jssecacerts");
            ks.store(out, passphrase);
            out.close();
    
            System.out.println();
            System.out.println(cert);
            System.out.println();
            System.out.println
                    ("Added certificate to keystore 'jssecacerts' using alias '"
                            + alias + "'");
        }
    
        private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
    
        private static String toHexString(byte[] bytes) {
            StringBuilder sb = new StringBuilder(bytes.length * 3);
            for (int b : bytes) {
                b &= 0xff;
                sb.append(HEXDIGITS[b >> 4]);
                sb.append(HEXDIGITS[b & 15]);
                sb.append(' ');
            }
            return sb.toString();
        }
    
        private static class SavingTrustManager implements X509TrustManager {
    
            private final X509TrustManager tm;
            private X509Certificate[] chain;
    
            SavingTrustManager(X509TrustManager tm) {
                this.tm = tm;
            }
    
            public X509Certificate[] getAcceptedIssuers() {
                throw new UnsupportedOperationException();
            }
    
            public void checkClientTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
                throw new UnsupportedOperationException();
            }
    
            public void checkServerTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
                this.chain = chain;
                tm.checkServerTrusted(chain, authType);
            }
        }
    }
    

    2
    请注意,创建的文件名是jssecacerts! - DeanDP
    我尝试在Java 7和8中使用此解决方案。在Java 7上运行时,我尝试的所有远程存储库URL都会出现protocol_version或handshake_failure错误。 `SandeepanNath:test sandeepan.nath $ java InstallCert repo1.maven.org:443 加载KeyStore /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/jre/lib/security/cacerts... 打开到repo1.maven.org的连接:443 ... 开始SSL握手...javax.net.ssl.SSLException:接收到致命警报:protocol_version..无法获取服务器证书链` - Sandeepan Nath

    23

    我想为smtp.gmail.com导入证书。 对我有效的唯一解决方案是:

    1. 输入命令以查看此证书

    2. D:\openssl\bin\openssl.exe s_client -connect smtp.gmail.com:465
      
    3. 复制并保存在-----BEGIN CERTIFICATE----------END CERTIFICATE-----之间的行到一个文件中,命名为gmail.cer

    4. 运行

      keytool -import -alias smtp.gmail.com -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -file C:\Users\Admin\Desktop\gmail.cer
      
    5. 输入密码:changeit

    6. 单击“是”以导入证书

    7. 重新启动Java

    现在运行命令,你就可以开始了。


    2
    如果在证书导入过程中出现“输入不是X.509证书”的错误消息,请确保您也复制了BEGIN CERTIFICATE和END CERTIFICATE文本。 - metatron
    1
    密码是changeit,其中n是小写(或在Mac上为changeme)。 - Christophe

    21

    这并不是一个针对 Twitter 的答案,但当你搜索这个错误时,会出现这个问题。如果您的系统连接到一个在 Web 浏览器中看起来具有有效证书的网站时收到此错误,则可能意味着该网站存在不完整的证书链。

    简单概括一下问题:证书颁发机构不会使用其根证书来签署任何旧的证书。相反,它们通常会签署 中间证书,这些证书也具有证书颁发机构标志集(即允许签署证书)。然后,当您从 CA 购买证书时,他们会使用其中一个中间证书签署您的 CSR。

    您的 Java 信任存储很可能只有根证书,而没有中间证书。

    配置不正确的站点可能会仅返回已签名证书。问题在于:它是使用未在您的信任存储中的中间证书签署的。浏览器通过下载或使用缓存中的中间证书来处理此问题;这最大化了网站的兼容性。然而,Java 和 OpenSSL 等工具就不会这样做。这将导致问题。

    您可以使用Qualys SSL Test 来验证这个怀疑。如果您运行该测试来测试一个站点,并且它说

    This server's certificate chain is incomplete.

    那么就可以确认了。您还可以通过查看证书路径并查看文本 Extra Download 来查看此内容。

    如何解决:服务器管理员需要配置 Web 服务器,以便返回中间证书。例如,对于 Comodo,就可以使用 .ca-bundle 文件。例如,在带有 mod_ssl 的 Apache 配置中,您可以使用 SSLCertificateChainFile 配置设置。对于 Nginx,您需要连接中间证书和签名证书,然后在 SSL 证书配置中使用它。您可以通过在线搜索“不完整的证书链”来获取更多信息。


    2
    你很棒。谢谢!我在新的Java服务器构建中遇到了问题,因为我忘记在SSLCertificateChainFile前面删除#号!!!解释得非常清楚。 - KisnardOnline

    17

    我遇到了一个稍微不同的情况,在我的系统上同时存在JDK和JRE 1.8.0_112。

    我使用已知的命令将新的CA证书导入到[JDK_FOLDER]\jre\lib\security\cacerts中:

    keytool -import -trustcacerts -keystore cacerts -alias <new_ca_alias> -file <path_to_ca_cert_file>
    

    尽管如此,我继续遇到相同的PKIX路径构建失败错误。

    我通过使用java -Djavax.net.debug=all ... > debug.log向Java CLI添加了调试信息。在debug.log文件中,以trustStore是:开头的那行实际上指向了在[JRE_FOLDER]\lib\security\cacerts中找到的cacerts存储。

    在我的情况下,解决方案是将JDK使用的cacerts文件(其中已添加了新的CA)复制到JRE使用的文件中,从而解决了该问题。


    我直接使用keytool将所需的证书导入到JRE/lib/security/cacerts中,但对我来说没有任何改变:(仍然出现相同的错误,我还通过IDE添加了它们,甚至添加到了类路径并添加了一个Bean来指定密钥库位置!!这太疯狂了! - Alex

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