如何禁用SSL验证?

11

我试图调用REST API,但是我得到了以下异常(完整的堆栈跟踪在结尾处):

"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"

我向API供应商询问了这个问题,并且他们表示,您使用的SSL库存在问题。

您如何解决这个问题?使用以下相同的代码,我可以无问题地调用NIMBLE API REST,但在这种情况下却不行。此外,我没有任何cert(.cer)文件。

我需要停用SSL验证,因为API KEY就是我的安全方法。

有什么建议吗?

非常感谢!

==== 控制台:====

POST https://api.nexalogy.com/project/create?api_key=XXXXXXXXXXXXXXXXXXXX
HTTP Header:"Content-Type" "application/json"
HTTP Body:[{"name":"project1","lang":"en","type":"twitter"}]
api_key:XXXXXXXXXXXXXXXXXXXX
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

我也尝试了以下表格,但是异常情况仍然相同:

POST https://api.nexalogy.com/project/create
HTTP Header:"Content-Type" "application/json"
HTTP Header:"api_key" "XXXXXXXXXXXXXXXXXXXX"
HTTP Body:[{"name":"project1","lang":"en","type":"twitter"}]
api_key:XXXXXXXXXXXXXXXXXXXX
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

==== 代码: ====

package servlet;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/AddProject")
public class AddProject extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public AddProject() {
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String body="[{\"name\":\"project1\",\"lang\":\"en\",\"type\":\"twitter\"}]";
        String api_key="XXXXXXXXXXXXXXXXXXXX";
        String str_response="";
        String line="";
        URL url = new URL("https://api.nexalogy.com/project/create?api_key="+api_key);
        try{
            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type","application/json");
            System.out.println("POST https://api.nexalogy.com/project/create?api_key="+api_key);
            System.out.println("HTTP Header:"+"\"Content-Type\" \"application/json\"");
            System.out.println("HTTP Body:"+body);
            System.out.println("api_key:"+api_key);
            connection.setUseCaches(false);
            connection.setDoInput(true);
            connection.setDoOutput(true);
            DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
            wr.writeBytes(body);
            wr.flush();
            wr.close();
            InputStream is = connection.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader rd = new BufferedReader(isr);
            while ((line = rd.readLine()) != null) str_response+= line + '\r';
            rd.close();
            System.out.println("str_response:"+str_response);
        }catch(Exception e){
            e.printStackTrace(System.out);
            //throw new RuntimeException(e);
        }
    }
}

==== 完整堆栈跟踪: ====

com.sun.jersey.api.client.ClientHandlerException: 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 com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:131)
    at com.sun.jersey.api.client.Client.handle(Client.java:616)
    at com.sun.jersey.api.client.WebResource.handle(WebResource.java:559)
    at com.sun.jersey.api.client.WebResource.post(WebResource.java:230)
    at engine_brandchats.twitter_api_create_project_0_1.Twitter_api_create_project.tREST_2Process(Twitter_api_create_project.java:955)
    at engine_brandchats.twitter_api_create_project_0_1.Twitter_api_create_project.tJava_1Process(Twitter_api_create_project.java:635)
    at engine_brandchats.twitter_api_create_project_0_1.Twitter_api_create_project.runJobInTOS(Twitter_api_create_project.java:1641)
    at engine_brandchats.twitter_api_create_project_0_1.Twitter_api_create_project.main(Twitter_api_create_project.java:1494)
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 com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unknown Source)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at com.sun.net.ssl.internal.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.getOutputStream(Unknown Source)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(Unknown Source)
    at com.sun.jersey.client.urlconnection.URLConnectionClientHandler$1$1.getOutputStream(URLConnectionClientHandler.java:203)
    at com.sun.jersey.api.client.CommittingOutputStream.commitWrite(CommittingOutputStream.java:117)
    at com.sun.jersey.api.client.CommittingOutputStream.write(CommittingOutputStream.java:89)
    at sun.nio.cs.StreamEncoder.writeBytes(Unknown Source)
    at sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source)
    at sun.nio.cs.StreamEncoder.implFlush(Unknown Source)
    at sun.nio.cs.StreamEncoder.flush(Unknown Source)
    at java.io.OutputStreamWriter.flush(Unknown Source)
    at java.io.BufferedWriter.flush(Unknown Source)
    at com.sun.jersey.core.util.ReaderWriter.writeToAsString(ReaderWriter.java:191)
    at com.sun.jersey.core.provider.AbstractMessageReaderWriterProvider.writeToAsString(AbstractMessageReaderWriterProvider.java:128)
    at com.sun.jersey.core.impl.provider.entity.StringProvider.writeTo(StringProvider.java:88)
    at com.sun.jersey.core.impl.provider.entity.StringProvider.writeTo(StringProvider.java:58)
    at com.sun.jersey.api.client.TerminatingClientHandler.writeRequestEntity(TerminatingClientHandler.java:305)
    at com.sun.jersey.client.urlconnection.URLConnectionClientHandler._invoke(URLConnectionClientHandler.java:182)
    at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:129)
    ... 7 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 com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(Unknown Source)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
    ... 35 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)
    ... 41 more

编辑于2013年6月13日的解决方案:实现X509TrustManager

添加以下代码...

HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
if (connection instanceof HttpsURLConnection) {
    try {
        KeyManager[] km = null;
        TrustManager[] tm = {new RelaxedX509TrustManager()};
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, tm, new java.security.SecureRandom());
        SSLSocketFactory sf = sslContext.getSocketFactory();
        ((HttpsURLConnection)connection).setSSLSocketFactory(sf);
        System.out.println("setSSLSocketFactory OK!");
    }catch (java.security.GeneralSecurityException e) {
        System.out.println("GeneralSecurityException: "+e.getMessage());
    }
}

...并添加以下类(实现X509TrustManager)

class RelaxedX509TrustManager implements X509TrustManager {
    public boolean isClientTrusted(java.security.cert.X509Certificate[] chain){ return true; }
    public boolean isServerTrusted(java.security.cert.X509Certificate[] chain){ return true; }
    public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; }
    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String input) {}
    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String input) {}
}

你使用的Java版本是哪个? - Bruno
嗨,谢谢你的回复,我看到了你的评论。我使用的是Java6,现在我安装和配置了Java7,但错误仍然相同:/ 我将不得不尝试你提到的第二个选项。 - Juan Rojas Montserrat
我不得不实现“X509TrustManager”接口(http://docs.oracle.com/javase/6/docs/api/javax/net/ssl/X509TrustManager.html),并升级到Java7,这两个步骤是解决方案,现在一切正常。谢谢! - Juan Rojas Montserrat
你的RelaxedX509TrustManager不会检查任何内容(它的isClientTrusted和isServerTrusted方法也不会被使用):这是不安全的。不要使用这个解决方案。 - Bruno
我同意,你有什么建议吗? - Juan Rojas Montserrat
3个回答

12

试一下这个

public void disableCertificates() {
    TrustManager[] trustAllCerts = new TrustManager[]{
        new X509TrustManager() {

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            @Override
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] certs, String authType) {
            }

            @Override
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] certs, String authType) {
            }
        }
    };

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

4
请勿在生产环境中使用此代码,因为它将禁用所有证书检查。这将导致您的实现容易受到中间人攻击的威胁。请注意保护您的系统安全。 - Uwe Plonus
1
嗨!谢谢你的回复,我尝试了这段代码:import javax.net.ssl.TrustManager和javax.net.ssl.X509TrustManager,并在我的代码之前调用了这个方法,但异常仍然是相同的。 - Juan Rojas Montserrat
1
绝对不要使用这段代码。即使只是为了测试,部署一个不安全的解决方案也没有任何意义。您正在冒着它“泄漏”到生产环境中的风险,这将是一个重大的安全漏洞。此外,该代码忽略了异常并违反了TrustManager的契约。请避免使用。 - user207421
10
你们误解了重点,他想禁用SSL验证并禁用上述代码的验证。如果他关心安全,他首先不会想要禁用SSL验证。唉。 - Akintayo Olusegun
@Aardvocate Akintayo Olusegun,你说得完全正确,但是OP可能想要禁用SSL验证的原因和我一样-它只是无法工作! 然而,在这种特定情况(以及我的情况)下,问题可以通过为Oracle做他们的工作-导入StartSSL的根证书解决。 - Phil

3
我需要禁用SSL验证,因为我已经拥有API密钥,而该密钥是安全方法。
不清楚您如何认为API密钥(以及其提供的安全性)与SSL / TLS提供的安全性有任何关联:它们显然没有解决相同的安全问题。 SSL / TLS是关于保护数据传输免受窃听和篡改的内容。
如果您使用此SSL测试服务检查要访问的服务器,则会发现该服务器需要支持服务器名称指示(SNI)的客户端:这允许服务器托管具有不同证书的多个主机。
如果您的客户端不支持SNI(仅从版本7开始,Java才支持客户端),则请求将显示不同于所需证书的证书:它可能失败证书验证或主机名验证程序。禁用其中任一检查会使连接容易受到中间人攻击。
  • 我认为您遇到的问题最可能的原因是您正在使用不支持SNI的Java版本(例如Java 6)。

  • 还有可能您正在使用的JRE没有将此特定CA包含在其默认信任存储中。您可以尝试从其他来源获取并转换您愿意信任的CA证书列表(例如Mozilla捆绑包,通常建议使用cURL)。

    JSSE参考指南中所述:

    重要提示:JDK在/lib/security/cacerts文件中提供了有限数量的可信根证书。根据keytool的文档,如果您使用此文件作为信任存储,则您有责任维护(即添加/删除)此文件中包含的证书。

    根据您联系的服务器的证书配置,您可能需要添加其他根证书。从适当的供应商获取所需的特定根证书。

无论哪种方式,禁用SSL验证都不是解决您问题的方法。 编辑: 看起来您要使用的服务具有由StartSSL颁发的证书,这显然不是Oracle JRE默认捆绑的CA之一。
您需要从StartSSL下载它(或从您已经拥有的受信任捆绑包中导出它,名为“StartCom ...”),并将其导入您的cacerts存储中的keytool(或另一个信任存储,如果您正在使用不同的信任存储)。
keytool -import -keystore /path/to/jre/lib/security/cacerts -alias startssl -file startssl.crt

(当然,根据需要调整startssl证书文件的路径和名称。)

-2
请使用带有完整证书链的证书。这意味着至少需要根证书。
您只导入了对方的终端证书,实现无法验证证书链。至少需要根证书。

那不对。只需要将任何一个签名者证书导入为“受信任即可”。通常,最高的那个会被导入。节点应当附带其证书和剩余的链条。 - user207421
@EJP 我写道至少需要根证书(在第一段中)。我已经更正了第二段以澄清此事。 - Uwe Plonus

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