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


422

我正在尝试为我的Java项目使用twitter4j库获取推文。在我的第一次跑步中,我得到了关于证书sun.security.validator.ValidatorException和的错误sun.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

6
您好,请检查以下网址。我相信这些会帮助您。java-samples.com/showtutorial.php?tutorialid=210 confluence.atlassian.com/display/JIRAKB/...。您必须将ssl证书添加到java truststore证书中(路径:jre / lib / security / cacerts)。
sus007 2014年

请参考答案以获取解决方案,并确定您将软件打包为捆绑的jre吗?如果是,则复制您的cacerts文件格式系统并替换该打包的jre cacerts文件。
积极

试试这个stackoverflow.com/a/9210661/4741746也许这就是您的答案
令人兴奋的gosavi

2
我只是使用了此Java代码,对于https,请不要忘记将端口指定为443。github.com/escline/InstallCert/blob/master/InstallCert.java上的 Java代码 将会带走您的CACERTS文件并添加所有这些加号。输入的URL的当前证书。就我而言,我将值硬编码为host =“ mywebservice.uat.xyz.com”; 端口= 443; passphrase =“ changeit” .toCharArray(); 然后,程序将创建一个名为“ jssecacerts”的新文件,其中将包含所有内容。将此名称重命名为“ cacerts”并使用它。一切都准备好了。
Reddymails

Answers:


572
  1. 在浏览器中转到URL:
    • firefox-单击HTTPS证书链(URL地址旁边的锁定图标)。点击"more info" > "security" > "show certificate" > "details" > "export.."。提取名称并选择文件类型example.cer
    • chrome-单击左侧的站点图标以在地址栏中输入地址,选择“证书”->“详细信息”->“导出”,然后以“ Der编码的二进制单证书”格式保存。
  2. 现在,您有了带有密钥库的文件,并且必须将其添加到JVM中。确定cacerts文件的位置,例如。 C:\Program Files (x86)\Java\jre1.6.0_22\lib\security\cacerts.

  3. 接下来,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


27
我必须将路径加引号并将其另存为Base64而不是DER
Theodore K.

3
哇,它在chrome中就像魔术一样工作,您只需单击左侧地址栏上的锁定图标,然后单击详细信息,而且 无需重新启动
aswzen

8
请注意,应该对链中的所有证书重复说明。alias命令行中的证书名称也应该是唯一的。
Lu55

5
如果您的家是JDK,请确保指定jre位于其中:keytool -import -alias示例-keystore“ C:\ Program Files \ Java \ jdk1.8.0_73 \ jre \ lib \ security \ cacerts” -file example.cer
Jack BeNimble

12
对于遇到“拒绝访问”错误的任何人,请确保您以管理员身份运行命令提示符。
沃克·克里斯蒂

81

在尝试构建证书文件以使我的Java 6安装与新的twitter cert一起工作之后,我花了许多时间才终于偶然发现了一个非常简单的解决方案,该解决方案掩埋在其中一个留言板上的注释中。只需从Java 7安装中复制cacerts文件,然后覆盖Java 6安装中的cacerts文件。最好是先备份cacerts文件,然后再将新文件复制到BOOM中!它只是工作。

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

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

希望这可以节省别人的时间。


4
我正在尝试使用自签名证书访问ssl服务器,尝试使用keytool添加它的证书,但是没有运气,有什么建议吗?
Oleg Belousov 2014年

1
很高兴。.但是您知道根本原因是什么。是什么导致了Java 6证书失败。以及Java 7证书如何解决了该问题
Vishwanath gowda k 2014年

做到了,尽管我从icedtea 1.6转到了oracle 1.7。:)
nperson325681

还要考虑以下解决方案:stackoverflow.com/questions/33439905/…–
Piohen

10
对于那些像我这样的人,有时会错过明显的东西-确保您正在进入$JAVA_HOME/jre/lib,而不是$JAVA_HOME/lib-我花了一些时间来错过这个细节。
Ryan Heathcote

35

我的UI方法:

  1. 此处下载密钥库资源管理器
  2. 打开$ JAVA_HOME / jre / lib / security / cacerts
  3. 输入密码:changeit(在Mac上可以是changeme)
  4. 导入您的.crt文件

CMD行:

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

1
在Mac上,使用CMD-Line,应使用sudo运行命令。#sudo keytool -importcert-文件jetty.crt -alias jetty -keystore $ JAVA_HOME / jre / lib / security / cacerts
malajisi

1
在Windows上,该命令可正常工作:keytool -importcert -file dinardap_cert.cer –alias dinardap –keystore “%JAVA_HOME%/jre/lib/security/cacerts”
Patricio Bonilla

从哪里获取证书文件?我不知道要转到哪个URL。jetty.com
kraftydevil

我已经从网络浏览器中导出了证书,并在挂锁上执行了上下文操作
rtbf

28

我偶然发现了一个需要花费大量时间进行研究的问题,特别是使用自动生成的证书进行修复,与官方证书不同,该证书非常棘手,Java不太喜欢它们。

请检查以下链接:解决Java证书问题

基本上,您必须将证书从服务器添加到Java Home证书。

  1. 生成或获取您的证书,并将Tomcat配置为在Servers.xml中使用它
  2. 下载该类的Java源代码InstallCert并在服务器运行时执行它,并提供以下参数server[:port]。不需要密码,因为原始密码可用于Java证书(“ changeit”)。
  3. 本程序将连接到服务器,而Java将引发异常,它将分析服务器提供的证书,并允许您jssecerts在执行本程序的目录内创建一个文件(如果从Eclipse执行,则请确保配置了Work )中的目录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 '16

我尝试针对Java 7和8使用此解决方案。在Java 7上运行时,我尝试使用的所有远程存储库URL都具有protocol_version或handshake_failure。SandeepanNath:test sandeepan.nath$ java InstallCert repo1.maven.org:443 Loading KeyStore /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/jre/lib/security/cacerts... Opening connection to repo1.maven.org:443 ... Starting SSL handshake... javax.net.ssl.SSLException: Received fatal alert: protocol_version .. Could not obtain server certificate chain
Sandeepan Nath

20

1.检查证书

请尝试将目标URL加载到浏览器中并查看站点的证书(通常可以通过带有锁定符号的图标访问该证书。该证书位于浏览器地址栏的左侧或右侧),该证书是否已过期或由于其他原因不受信任。

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

新版本通常带有一组更新的受信任证书。

另外,如果可能,请卸载旧版本。这将使配置错误明确。

3.检查您的配置:

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

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

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

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

精心设计,应该是公认的答案。
Egor Hans

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

它用于跳转证书验证。

警告 仅用于开发目的是不安全的!


4
它用于跳转证书验证
gorums's

2
@gorums如何在Eclipse中进行设置?
malajisi

6
除非您要冒在机器上运行任意恶意代码的风险,否则请不要这样做。投票不足,因为它有可能损害任何运行它的计算机。
布赖恩

1
仅仅因为它“有效”并不意味着它是安全的,您就使遵循此建议的每个人都面临风险。让我失望吧
Paradoxis

1
@AceKing关键是,如果您不验证证书,则它不是受信任的代码。也就是说,我认为这仍然是一个有用的答案。人们这样做只需要了解风险。
Michael Mior

12

当我的系统上同时存在JDK和JRE 1.8.0_112时,我的情况略有不同。

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

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

仍然,我不断收到相同的PKIX路径构建失败错误。

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

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


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

8

问题背景:

当我尝试在我的项目中并通过Netbeans IDE clean and build选项运行mvn clean install时,出现以下错误。此问题是由于当我们通过NET Bean IDE /通过命令提示符下载但无法通过浏览器下载文件时,证书不可用。

错误

Caused by: org.eclipse.aether.transfer.ArtifactTransferException: Could not transfer artifact com.java.project:product:jar:1.0.32 from/to repo-local (https://url/local-repo): sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  

解析度:

1.下载相关网址的证书:

  • 通过“以管理员身份运行”启动IE(否则,我们将无法下载证书)
  • 在IE-> https:// url / local-repo中输入网址 (在我的情况下,该网址具有不受信任的证书在此处输入图片说明。)
  • 通过单击证书错误->查看证书下载证书
  • 选择详细信息选项卡->复制到文件->下一步->选择“ DER编码的二进制X.509(.CER)
  • 将证书保存在某个位置,例如:c:/user/sheldon/desktop/product.cer
  • 恭喜!您已经成功下载了该站点的证书

2.现在安装密钥库以解决此问题。

  • 运行keytool命令将下载的密钥库附加到现有证书文件中。
  • 命令:在jdk(JAVA_HOME)的bin文件夹中的命令下方

C:\ Program Files \ Java \ jdk1.8.0_141 \ jre \ bin> keytool -importcert-文件“ C:/user/sheldon/desktop/product.cer”-别名产品-keystore“ C:/ Program Files / Java / jdk1.8.0_141 / jre / lib / security / cacerts”。

  • 系统将提示您输入密码。输入密钥库密码:再次输入“ changeit”作为“信任此证书?[否]:”,输入“是”

样例命令行命令/输出:

keytool -importcert -file "C:/Users/sheldon/Desktop/product.cer" -alias product -keystore "C:/Program iles/Java/jdk1.8.0_141/jre/lib/security/cacerts"
Enter keystore password:
Trust this certificate? [no]:  yes
Certificate was added to keystore
  • 恭喜!现在您应该摆脱了Netbeans IDE中的“ PKIX路径构建失败:sun.security.provider.certpath.SunCertPathBuilderException”错误。

嗨,我遵循了所有步骤,但没有走运:(
ManishNegi

除cacerts之外,您在路径中是否还有其他证书?如果你有一些其他的,尝试将其添加到证书
BARANI [R

谢谢@Barani r,这是我的错误,我从jre执行命令并在日食中使用JDK
ManishNegi,

无法下载证书,但是在“以管理员身份运行”后运行良好。
另一个编码器,

8

我想为smtp.gmail.com导入证书

对于我唯一有效的解决方案是1.输入命令以查看此证书

D:\ openssl \ bin \ openssl.exe s_client -connect smtp.gmail.com:465

  1. 将“ ----- BEGIN CERTIFICATE -----”和“ ----- END CERTIFICATE -----”之间的行复制并保存到文件gmail.cer中

  2. keytool-导入-alias smtp.gmail.com -keystore“%JAVA_HOME%/ jre / lib / security / cacerts”-文件C:\ Users \ Admin \ Desktop \ gmail.cer

  3. 输入密码

  4. 单击是导入证书

  5. 重启java

现在运行命令,您一切顺利


谢谢你,兄弟!那挽救了我的一天。对于我而言,这是唯一合适的解决方案。
安德鲁·甘斯

也拯救了我的一天。谢谢。
克里斯(Kris)

6

这不是特定于Twitter的答案,但这是您搜索此错误时出现的问题。如果系统连接到在Web浏览器中查看具有有效证书的网站时收到此错误,则可能意味着该网站的证书链不完整

对于问题的简要概述:证书颁发机构不使用其根证书仅对任何旧证书进行签名。取而代之的是,它们(通常)签署同时设置了证书颁发机构标记(即允许签署证书)的中间证书。然后,当您从CA购买证书时,他们将使用这些中间证书之一签署您的CSR。

您的Java信任库很可能仅具有根证书,而没有中间证书。

配置错误的站点可能返回其签名证书。问题:它是使用不在您的信任库中的中间证书签名的。浏览器将通过下载或使用缓存的中间证书来解决此问题;这可以最大程度地提高网站的兼容性。但是,Java和OpenSSL之类的工具不会。这将导致问题中的错误。

您可以使用Qualys SSL Test来验证这种怀疑。如果您针对某个网站运行它,并说

该服务器的证书链不完整。

然后证实这一点。您也可以通过查看认证路径并看到文字Extra Download来查看

修复方法:服务器管理员需要将Web服务器配置为也返回中间证书。例如,对于Comodo,这是.ca-bundle文件可用的地方。例如,在具有mod_ssl的Apache配置中,您将使用SSLCertificateChainFile配置设置。对于nginx,您需要将中间证书和签名证书连接起来,并在SSL证书配置中使用它们。您可以通过在线搜索“不完整的证书链”找到更多信息。


你太聪明了。谢谢!我的Java服务器的新版本出现问题,因为我忘记删除SSLCertificateChainFile前面的#号!真的很好解释。
KisnardOnline

6

之所以出现上述错误,是因为JDK与许多受信任的证书颁发机构(CA)证书捆绑在一起,称为“ cacerts”文件,但该文件不包含我们的自签名证书。换句话说,cacerts文件没有导入我们的自签名证书,因此不会将其视为受信任的实体,因此会出现上述错误。

如何解决以上错误

要解决以上错误,我们需要将自签名证书导入cacerts文件。

首先,找到cacerts文件。我们将需要找出JDK的位置。如果通过Eclipse或IntelliJ Idea等IDE之一运行应用程序,请转到项目设置并找出JDK的位置。例如,在Mac OS上,cacerts文件的典型位置将位于Windows计算机上的/ Library / Java / JavaVirtualMachines / {{JDK_version}} / Contents / Home / jre / lib / security处,该位置位于{{Installation_directory} } / {{JDK_version}} / jre / lib / security

找到cacerts文件后,现在我们需要将自签名证书导入此cacerts文件。如果您不知道如何正确生成自签名证书,请查看上一篇文章。

如果您没有证书文件(.crt),而只有.jks文件,则可以使用以下命令生成.crt文件。如果您已经有一个.crt / .pem文件,则可以忽略以下命令

##从密钥库(.jks文件)生成证书####

keytool -export -keystore keystore.jks -alias selfsigned -file selfsigned.crt

上面的步骤将生成一个名为selfsigned.crt的文件。现在将证书导入到cacerts

现在将证书添加到JRE / lib / security / cacerts(trustore)
keytool -importcert -file selfsigned.crt -alias selfsigned -keystore {{cacerts path}}

例如

keytool -importcert -file selfsigned.nextgen.crt -alias selfsigned.nextgen -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts

就是这样,重新启动您的应用程序,它应该可以正常工作。如果仍然无法正常工作,请获取SSL握手异常。这可能意味着您使用的是不同于证书中注册的域。

详细说明和逐步解决方法的链接已在此处结束。


4

添加cacerts对我不起作用。启用带标志的日志后-Djavax.net.debug=all,便知道从读取Java jssecacerts

导入jssecacerts终于起作用了。


2
此处缺少的解释是Java将使用Java(jssecacerts如果存在),否则使用cacerts
罗恩侯爵,

3

这是一个解决方案,但是以我遇到的这个问题的形式:

我尝试了上面给出的所有解决方案(3天)都快要死了,对我没有任何帮助。

我失去了所有希望。

我就此事联系了我的安全团队,因为我在代理后面,他们告诉他们最近更新了他们的安全策略。

我严厉责骂他们没有通知开发人员。

后来他们发布了一个新的“证书”文件,其中包含所有证书。

我删除了%JAVA_HOME%/ jre / lib / security中存在的cacerts文件,它解决了我的问题。

因此,如果您遇到此问题,可能也来自您的网络团队。


2

我在尝试通过其更新站点在Eclipse中安装Cucumber-Eclipse插件时遇到了这个问题。我收到了相同的SunCertPathBuilderException错误:

Unable to read repository at http://cucumber.io/cucumber-eclipse/update-site/content.xml.
    Unable to read repository at http://cucumber.io/cucumber-eclipse/update-site/content.xml.
    sun.security.validator.ValidatorException: PKIX path building failed: 
   sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

尽管某些其他答案对于该问题的给定情况是适当且有用的,但对于我的问题而言,它们却无济于事且产生误导。

就我而言,问题在于为其更新站点提供的URL是:

https://cucumber.io/cucumber-eclipse/update-site

但是,通过浏览器导航到它时,它会重定向到(请注意添加的“ .github ”):

http://cucumber.github.io/cucumber-eclipse/update-site/

因此,解决方案是在Eclipse中添加更新站点时,仅使用更新站点URL的重定向版本。


3
确定要回答正确的问题吗?在原始问题中没有提到黄瓜...
Done Data Solutions

我觉得我是。我收到了同样的错误SunCertPathBuilderException并同时寻找一个解决,发现这个问题,以及这一个。但是,他们都不适合我的情况。我不是要回答如何将Eclipse的Cucumber-Eclipse插件站点添加到特定问题。我想提供我所遇到的问题(URL重定向)的上下文并解释如何解决。
royfripple

我已经更新了答案,以明确说明我收到了相同的错误,并指出根本问题和解决方案有所不同。我认为这是正确的,因为其他人在搜索相同错误时可能会发现此问题。如果您认为有道理,请考虑更改投票。
royfripple

谢谢。颠覆了我的观点,让事情变得更加清晰
完成数据解决方案

别客气!并感谢您抽出宝贵时间再次进行审查。
royfripple

2

我遇到了同样的问题,并使用以下简单步骤解决了该问题:

1)从Google下载InstallCert.java

2)使用javac InstallCert.java进行编译

3)使用带有主机名和https端口的java InstallCert.java运行InstallCert.java ,并在询问输入时按“ 1”。它将添加“ localhost”作为受信任的密钥库,并生成一个名为“ jssecacerts”的文件,如下所示:

Java InstallCert本地主机:443

4)将jssecacerts复制到$ JAVA_HOME / jre / lib / security文件夹中

解决此问题的主要来源是:

https://ankurjain26.blogspot.in/2017/11/javaxnetsslsslhandshakeexception.html


1

我在Windows Server 2016上使用Java 8解决了此问题,方法是将证书从pkcs12存储区导入cacerts密钥存储区。

路径PKCS12店:
C:\Apps\pkcs12.pfx

路径到Java cacerts中:
C:\Program Files\Java\jre1.8.0_151\lib\security\cacerts

路径密钥工具:
C:\Program Files\Java\jre1.8.0_151\bin

在命令提示符(管理员),命令导入证书从possitioning到文件夹密钥工具后pkcs12,以cacerts如下:
keytool -v -importkeystore -srckeystore C:\Apps\pkcs12.pfx -srcstoretype PKCS12 -destkeystore "C:\Program Files\Java\jre1.8.0_151\lib\security\cacerts" -deststoretype JKS

您会被提示:
1,输入目的地库密码(证书密码,默认为“ changeit”)
2.输入源密钥库密码(pkcs12密码)


为了使更改生效,请重新启动服务器计算机(或仅重新启动JVM)。


1
我在Mac上使用了这种方法,但它确实有效。我的证书当然是自签名的,并生成了我导入的在Windows adfs服务器上生成的.pfx文件。对我的应用程序使用Java 10谢谢
Shashikant Soni

0

在这种情况下,当可信证书的路径不匹配时,通常会发生这种异常。检查安全通信需要此服务器证书的配置或路径。


您能告诉我我可以在其中安装证书的确切配置吗?实际上,我对此有疑问,我已经使用keytool安装了有关此问题的证书,该证书的工作时间约为20-30分钟,然后服务器再次发出相同的错误。请帮我。我已经在这个问题上停留了2天。
Deepak Gangore 2014年

我认为证书每20-30分钟更改一次
Vishwanath gowda k 2014年

我面临类似的问题,但问题是:尽管我提供了-Djavax.net.ssl.trustStore = / myapp / app.jks,我的Java为Java应用程序的$ JAVA_HOME / lib / security / cacerts选择了默认位置。在此: stackoverflow.com/questions/33821785/...
拉克斯曼摹

0

对我来说,弹出证书错误是因为我的提琴手在后台运行,并且弄乱了证书。它充当代理,因此请关闭并重新启动eclipse。


即使关闭所有应用程序后,eclipse中也会发生同样的问题
arvindwill

0

目标:

  1. 使用https连接
  2. 验证SSL链
  3. 不处理证书
  4. 在运行时添加证书
  5. 不要丢失证书的证书

怎么做:

  1. 定义自己的密钥库
  2. 将证书放入密钥库
  3. 使用我们的自定义类重新定义SSL默认上下文
  4. ???
  5. 利润

我的密钥库包装文件:

public class CertificateManager {

    private final static Logger logger = Logger.getLogger(CertificateManager.class);

    private String keyStoreLocation;
    private String keyStorePassword;
    private X509TrustManager myTrustManager;
    private static KeyStore myTrustStore;

    public CertificateManager(String keyStoreLocation, String keyStorePassword) throws Exception {
        this.keyStoreLocation = keyStoreLocation;
        this.keyStorePassword = keyStorePassword;
        myTrustStore = createKeyStore(keyStoreLocation, keyStorePassword);
    }

    public void addCustomCertificate(String certFileName, String certificateAlias)
            throws Exception {
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init((KeyStore) null);
        Certificate certificate = myTrustStore.getCertificate(certificateAlias);
        if (certificate == null) {
            logger.info("Certificate not exists");
            addCertificate(certFileName, certificateAlias);
        } else {
            logger.info("Certificate exists");
        }
        tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(myTrustStore);
        for (TrustManager tm : tmf.getTrustManagers()) {
            if (tm instanceof X509TrustManager) {
                setMytrustManager((X509TrustManager) tm);
                logger.info("Trust manager found");
                break;
            }
        }
    }

    private InputStream fullStream(String fname) throws IOException {
        ClassLoader classLoader = getClass().getClassLoader();
        InputStream resource = classLoader.getResourceAsStream(fname);
        try {
            if (resource != null) {
                DataInputStream dis = new DataInputStream(resource);
                byte[] bytes = new byte[dis.available()];
                dis.readFully(bytes);
                return new ByteArrayInputStream(bytes);
            } else {
                logger.info("resource not found");
            }
        } catch (Exception e) {
            logger.error("exception in certificate fetching as resource", e);
        }
        return null;
    }

    public static KeyStore createKeyStore(String keystore, String pass) throws Exception {
        try {
            InputStream in = CertificateManager.class.getClass().getResourceAsStream(keystore);
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(in, pass.toCharArray());
            logger.info("Keystore was created from resource file");
            return keyStore;
        } catch (Exception e) {
            logger.info("Fail to create keystore from resource file");
        }

        File file = new File(keystore);
        KeyStore keyStore = KeyStore.getInstance("JKS");
        if (file.exists()) {
            keyStore.load(new FileInputStream(file), pass.toCharArray());
            logger.info("Default keystore loaded");
        } else {
            keyStore.load(null, null);
            keyStore.store(new FileOutputStream(file), pass.toCharArray());
            logger.info("New keystore created");
        }
        return keyStore;
    }

    private void addCertificate(String certFileName, String certificateAlias) throws CertificateException,
            IOException, KeyStoreException, NoSuchAlgorithmException {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream certStream = fullStream(certFileName);
        Certificate certs = cf.generateCertificate(certStream);
        myTrustStore.setCertificateEntry(certificateAlias, certs);
        FileOutputStream out = new FileOutputStream(getKeyStoreLocation());
        myTrustStore.store(out, getKeyStorePassword().toCharArray());
        out.close();
        logger.info("Certificate pushed");
    }

    public String getKeyStoreLocation() {
        return keyStoreLocation;
    }

    public String getKeyStorePassword() {
        return keyStorePassword;
    }
    public X509TrustManager getMytrustManager() {
        return myTrustManager;
    }
    public void setMytrustManager(X509TrustManager myTrustManager) {
        this.myTrustManager = myTrustManager;
    }
}

此类将在必要时创建密钥库,并将能够管理其中的证书。现在为SSL上下文分类:

public class CustomTrustManager implements X509TrustManager {

    private final static Logger logger = Logger.getLogger(CertificateManager.class);

    private static SSLSocketFactory socketFactory;
    private static CustomTrustManager instance = new CustomTrustManager();
    private static List<CertificateManager> register = new ArrayList<>();

    public static CustomTrustManager getInstance() {
        return instance;
    }

    private X509TrustManager defaultTm;

    public void register(CertificateManager certificateManager) {
        for(CertificateManager manager : register) {
            if(manager == certificateManager) {
                logger.info("Certificate manager already registered");
                return;
            }
        }
        register.add(certificateManager);
        logger.info("New Certificate manager registered");
    }

    private CustomTrustManager() {
        try {
            String algorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);

            tmf.init((KeyStore) null);
            boolean found = false;
            for (TrustManager tm : tmf.getTrustManagers()) {
                if (tm instanceof X509TrustManager) {
                    defaultTm = (X509TrustManager) tm;
                    found = true;
                    break;
                }
            }
            if(found) {
                logger.info("Default trust manager found");
            } else {
                logger.warn("Default trust manager was not found");
            }

            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{this}, null);
            SSLContext.setDefault(sslContext);
            socketFactory = sslContext.getSocketFactory();
            HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);


            logger.info("Custom trust manager was set");
        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
            logger.warn("Custom trust manager can't be set");
            e.printStackTrace();
        }
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        List<X509Certificate> out = new ArrayList<>();
        if (defaultTm != null) {
            out.addAll(Arrays.asList(defaultTm.getAcceptedIssuers()));
        }
        int defaultCount = out.size();
        logger.info("Default trust manager contain " + defaultCount + " certficates");
        for(CertificateManager manager : register) {
            X509TrustManager customTrustManager = manager.getMytrustManager();
            X509Certificate[] issuers = customTrustManager.getAcceptedIssuers();
            out.addAll(Arrays.asList(issuers));
        }
        logger.info("Custom trust managers contain " + (out.size() - defaultCount) + " certficates");
        X509Certificate[] arrayOut = new X509Certificate[out.size()];
        return out.toArray(arrayOut);
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain,
                                   String authType) throws CertificateException {
        for(CertificateManager certificateManager : register) {
            X509TrustManager customTrustManager = certificateManager.getMytrustManager();
            try {
                customTrustManager.checkServerTrusted(chain, authType);
                logger.info("Certificate chain (server) was aproved by custom trust manager");
                return;
            } catch (Exception e) {
            }
        }
        if (defaultTm != null) {
            defaultTm.checkServerTrusted(chain, authType);
            logger.info("Certificate chain (server) was aproved by default trust manager");
        } else {
            logger.info("Certificate chain (server) was rejected");
            throw new CertificateException("Can't check server trusted certificate.");
        }
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain,
                                   String authType) throws CertificateException {
        try {
            if (defaultTm != null) {
                defaultTm.checkClientTrusted(chain, authType);
                logger.info("Certificate chain (client) was aproved by default trust manager");
            } else {
                throw new NullPointerException();
            }
        } catch (Exception e) {
            for(CertificateManager certificateManager : register) {
                X509TrustManager customTrustManager = certificateManager.getMytrustManager();
                try {
                    customTrustManager.checkClientTrusted(chain, authType);
                    logger.info("Certificate chain (client) was aproved by custom trust manager");
                    return;
                } catch (Exception e1) {
                }
            }
            logger.info("Certificate chain (client) was rejected");
            throw new CertificateException("Can't check client trusted certificate.");
        }
    }

    public SSLSocketFactory getSocketFactory() {
        return socketFactory;
    }
}

该类作为单例,因为只允许一个defaultSSL上下文。所以,现在用法:

            CertificateManager certificateManager = new CertificateManager("C:\\myapplication\\mykeystore.jks", "changeit");
            String certificatePath = "C:\\myapplication\\public_key_for_your_ssl_service.crt";
            try {
                certificateManager.addCustomCertificate(certificatePath, "alias_for_public_key_for_your_ssl_service");
            } catch (Exception e) {
                log.error("Can't add custom certificate");
                e.printStackTrace();
            }
            CustomTrustManager.getInstance().register(certificateManager);

可能无法使用此设置,因为我将证书文件保存在资源文件夹中,因此我的路径不是绝对的。但通常来说,它运行良好。


1
available()在自己的Javadoc中特别警告了这种双重滥用。
罗恩侯爵,


0

我使用以下方法修复了此问题-

  1. 复制存在连接问题的网址
  2. 转到Android Studio->设置-> Http设置
  3. 在“测试连接”中,粘贴该网址,然后按确定
  4. 单击确定后,Android Studio将要求导入该URL的证书,并将其导入
  5. 而已。没有别的事情可做,我的问题不见了。无需重新启动Studio。

0

当您在Atlassian软件ex上遇到上述错误时。吉拉

2018-08-18 11:35:00,312 Caesium-1-4 WARN anonymous    Default Mail Handler [c.a.mail.incoming.mailfetcherservice] Default Mail Handler[10001]: javax.mail.MessagingException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target while connecting to host 'imap.xyz.pl' as user 'jira@xyz.pl' via protocol 'imaps, 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

您可以将证书添加到其受信任的密钥库中(将missing_ca更改为正确的证书名称):

keytool -importcert -file missing_ca.crt -alias missing_ca -keystore /opt/atlassian/jira/jre/lib/security/cacerts

如果要求输入密码changeit并确认y

之后,只需重新启动jira。


0

如果您的存储库URL也可以在HTTP上使用并且不考虑安全性,则可以转到settings.xml(通常(但并非总是)位于中%USERPROFILE%/.m2),并将HTTPS替换为HTTP for <repository><pluginRepository>URL。

例如,这:

<repository>
    <snapshots>
        <enabled>false</enabled>
    </snapshots>
    <id>central</id>
    <name>libs-release</name>
    <url>https://<artifactory>/libs-release</url>
</repository>

应替换为:

<repository>
    <snapshots>
        <enabled>false</enabled>
    </snapshots>
    <id>central</id>
    <name>libs-release</name>
    <url>https://<artifactory>/libs-release</url>
</repository>

0

我通过传递arg而不是JRE来使用自己的信任库 -Djavax.net.ssl.trustStore=

无论truststore中的证书如何,我都会收到此错误。对我来说,问题是在arg行上传递的属性的顺序。当我&args 和args 之前加上-Djavax.net.ssl.trustStore=&时-Djavax.net.ssl.trustStorePassword= 我能够通过https成功调用我的rest调用。-Dspring.config.location=-jar


0

如果您使用的是CloudFoundry并遇到证书问题,则必须确保再次使用带有证书的keystore-service推动jar。简单地解除绑定,绑定和重新启动将不起作用。


0

如果您的主机位于防火墙/代理之后,请在cmd中使用以下命令:

keytool -J-Dhttps.proxyHost=<proxy_hostname> -J-Dhttps.proxyPort=<proxy_port> -printcert -rfc -sslserver <remote_host_name:remote_ssl_port>

替换<proxy_hostname><proxy_port>使用已配置的HTTP代理服务器。替换<remote_host_name:remote_ssl_port>为存在证书问题的远程主机(基本上是URL)和端口之一。

取出最后打印的证书内容并进行复制(还复制开始证书和结束证书)。将其粘贴到文本文件中,并以.crt 扩展名。现在,使用java keytool命令将此证书导入cacerts,它应该可以工作。

keytool -importcert -file <filename>.crt -alias randomaliasname -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit


0

如果当Java应用程序试图与另一个应用程序/站点进行通信时,您在Linux容器中看到此问题,那是因为证书已被错误地导入到负载均衡器中。导入证书需要遵循一系列步骤,如果操作不正确,您将看到类似以下的问题

Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid 
certification path to requested target

一旦证书正确导入,就应该完成。无需修补任何JDK证书。


0

我为Intellij Idea解决了它, 我遇到了这个问题Althouh,我换了很多地方来解决问题。我找到了解决方案。右键单击您的项目,您将看到Maven,之后按Generate Sources and Update Folders and ReImport 在此处输入图片说明

完成了


0

我遇到了同样的问题,我使用的是8.1.0-3,但后来我使用的是9.2.1-0,此问题已得到解决,无需任何手动步骤。自签名证书运行良好。

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.