SSL 证书验证:javax.net.ssl.SSLHandshakeException

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25079751/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 23:35:33  来源:igfitidea点击:

SSL Certificate Verification : javax.net.ssl.SSLHandshakeException

javasecuritysslhttps

提问by Runcorn

I am trying to call a HTTPS REST API through Jersey Client. And on the course of development i stumble upon following error :

我正在尝试通过Jersey Client. 在开发过程中,我偶然发现了以下错误:

Exception in thread "main" com.sun.jersey.api.client.ClientHandlerException: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching mvn.signify.abc.com found
    at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:149)
    at com.sun.jersey.api.client.Client.handle(Client.java:648)
    at com.sun.jersey.api.client.WebResource.handle(WebResource.java:670)
    at com.sun.jersey.api.client.WebResource.access0(WebResource.java:74)
    at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:503)
    at com.lftechnology.sbworkbench.utility.utils.PingFederateUtility.main(PingFederateUtility.java:32)
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching mvn.signify.abc.com found

So I googled it out for a bit and found tons of solution for it , which actually works.

所以我用谷歌搜索了一下,找到了大量的解决方案,这确实有效。

  1. HTTPS using Jersey Client
  2. https://gist.github.com/outbounder/1069465
  3. How to fix the "java.security.cert.CertificateException: No subject alternative names present" error?
  4. http://www.mkyong.com/webservices/jax-ws/java-security-cert-certificateexception-no-name-matching-localhost-found/
  5. http://java.globinch.com/enterprise-java/security/fix-java-security-certificate-exception-no-matching-localhost-found/
  1. 使用 Jersey 客户端的 HTTPS
  2. https://gist.github.com/outbounder/1069465
  3. 如何修复“java.security.cert.CertificateException:没有主题替代名称存在”错误?
  4. http://www.mkyong.com/webservices/jax-ws/java-security-cert-certificateexception-no-name-matching-localhost-found/
  5. http://java.globinch.com/enterprise-java/security/fix-java-security-certificate-exception-no-matching-localhost-found/

They are in different domain but they have a common solution to work it out.

他们在不同的领域,但他们有一个共同的解决方案来解决这个问题。

Scenario

设想

I am currently using a self created self-signed certificate in development environment. And hence it is bound to show up the issue.

我目前在开发环境中使用自创建的自签名证书。因此它必然会出现问题。

Question

The above mentioned solution focus on skipping / allowing all certificates to be verified.

上述解决方案侧重于跳过/允许验证所有证书。

But when i move it to the production environment , then i have access to Valid Signed Certificate from trustworthy source.

但是当我将它移动到生产环境时,我就可以访问来自可信赖来源的有效签名证书。

  1. So are these solution any help when I move to production?
  2. Is it OK to skip SSL verification ?
  3. What are the other alternate way to achieve a common solution for both development and production environment?
  1. 那么当我进入生产环境时,这些解决方案有帮助吗?
  2. 可以跳过 SSL 验证吗?
  3. 为开发和生产环境实现通用解决方案的其他替代方法是什么?

P.S

聚苯乙烯

The solution i used was ,

我使用的解决方案是,

try
{
    // Create a trust manager that does not validate certificate chains
    TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
        public void checkClientTrusted(X509Certificate[] certs, String authType) {
        }
        public void checkServerTrusted(X509Certificate[] certs, String authType) {
        }
    }
    };

    // Install the all-trusting trust manager
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

    // Create all-trusting host name verifier
    HostnameVerifier allHostsValid = new HostnameVerifier() {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };

    // Install the all-trusting host verifier
    HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
} catch (KeyManagementException e) {
    e.printStackTrace();
}

which i then in-cooperate with Jerseyto make it work. And it is working great.

然后我与Jersey它合作以使其工作。而且效果很好。

So , the Questionagain. Is this solution viable to be used in Production environment?However, you don't want to modify the returning entities, it's much better to fetch the entities in read-only mode. This will allows Hibernate to discard the associated detached state which is used by the dirty checking mechanism to detect entity state modifications. More, read-only entities are skipped during flushing.

所以,Question又。此解决方案是否适用于生产环境?但是,您不想修改返回的实体,最好以只读模式获取实体。这将允许 Hibernate 丢弃关联的分离状态,脏检查机制使用该分离状态来检测实体状态修改。此外,在刷新期间会跳过只读实体。

采纳答案by jww

I am currently using a self created self-signed certificate in development environment. ... javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching dev.ppc.lftechnology.com found

我目前在开发环境中使用自创建的自签名证书。... javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: 找不到与 dev.ppc.lftechnology.com 匹配的名称

It appears the self signed certificate is incorrect.

看来自签名证书不正确。

Below is the OpenSSL CONFfile I use to create self signed certificates and certificate requests to use during testing. Save it as example-com.conf. Change the DNS names under [ alternate_names ]to suit your tastes. You can even put localhost, localhost.localdomainand 127.0.0.1in there for testing.

下面是CONF我用来创建自签名证书和在测试期间使用的证书请求的 OpenSSL文件。将其另存为example-com.conf. 更改下面的 DNS 名称[ alternate_names ]以适合您的口味。你甚至可以把localhost, localhost.localdomainand127.0.0.1放在那里进行测试。

If you want to create a self signed certificate, then use:

如果要创建自签名证书,请使用:

openssl req -config example-com.conf -new -x509 -newkey rsa:2048 \
    -nodes -keyout example-com.key.pem -days 365 -out example-com.cert.pem

If you want to create a signing request (CSR) that will be signed by a trusted authority, then use:

如果要创建将由受信任机构签名的签名请求 (CSR),请使用:

openssl req -config example-com.conf -new -newkey rsa:2048 \
    -nodes -keyout example-com.key.pem -days 365 -out example-com.req.pem

The difference between a self signed certificate and a signing request is the -x509option. With -x509present, a self signed certificate is created. The absence of -x509means a request is created.

自签名证书和签名请求之间的区别在于-x509选项。随着-x509目前,创建一个自签名证书。不存在-x509意味着创建请求。

If you want to print your self signed certificate or request to see what's actually in it, then use:

如果您想打印您的自签名证书或请求查看其中的实际内容,请使用:

openssl x509 -in example-com.cert.pem -text -noout
openssl req -in example-com.req.pem -text -noout

If you want to test the server, then use s_client:

如果要测试服务器,请使用s_client

openssl s_client -connect <server>:<port> -CAfile <trust-anchor.pem>

The above command should finish with a message similar to Verify OK (0). If you don't receive Verify OK (0), then fix your test rig. Once OpenSSL completes successfully, then that becomes your baseline.

上面的命令应该以类似于 的消息结束Verify OK (0)。如果您没有收到Verify OK (0),请修理您的测试台。一旦 OpenSSL 成功完成,这将成为您的基准。



[ req ]
default_bits        = 2048
default_keyfile     = server-key.pem
distinguished_name  = subject
req_extensions      = req_extensions
x509_extensions     = cert_extensions
string_mask         = utf8only

[ subject ]
countryName         = Country Name (2 letter code)
countryName_default     = US

stateOrProvinceName     = State or Province Name (full name)
stateOrProvinceName_default = NY

localityName            = Locality Name (eg, city)
localityName_default        = New York

organizationName         = Organization Name (eg, company)
organizationName_default    = Example, LLC

# Use a friendly name here. Its presented to the user.
#   The server's DNS name show up in Subject Alternate Names. Plus, 
#   DNS names here is deprecated by both IETF and CA/Browser Forums.
commonName          = Common Name (e.g. server FQDN or YOUR name)
commonName_default      = Example Company

emailAddress            = Email Address
emailAddress_default        = [email protected]

[ cert_extensions ]

subjectKeyIdentifier        = hash
authorityKeyIdentifier  = keyid,issuer

basicConstraints        = CA:FALSE
keyUsage            = digitalSignature, keyEncipherment
# extendedKeyUsage  = serverAuth
subjectAltName          = @alternate_names
nsComment           = "OpenSSL Generated Certificate"

[ req_extensions ]

subjectKeyIdentifier        = hash

basicConstraints        = CA:FALSE
keyUsage            = digitalSignature, keyEncipherment
# extendedKeyUsage  = serverAuth
subjectAltName          = @alternate_names
nsComment           = "OpenSSL Generated Certificate"

[ alternate_names ]

DNS.1       = example.com
DNS.2       = www.example.com
DNS.3       = mail.example.com
DNS.4       = ftp.example.com

# Add these if you need them. But usually you don't want them or
#   need them in production. You may need them for development.
# DNS.5       = localhost
# DNS.6       = localhost.localdomain
# DNS.7       = 127.0.0.1


Is it OK to skip SSL verification ?

可以跳过 SSL 验证吗?

No. That's very irresponsible. If you are not going to use PKIX correctly, then why use it at all?

不,这是非常不负责任的。如果您不打算正确使用 PKIX,那为什么还要使用它呢?

This comes to mind: The Most Dangerous Code in the World: Validating SSL Certificates in Non-Browser Software.

这让我想到:世界上最危险的代码:验证非浏览器软件中的 SSL 证书



HostnameVerifier allHostsValid = new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
};

Its better to load your self signed certificate in a Keystore (or load your private CA), and then pass it to SSLContext.init. Then everything works as intended, and there's no need to trust everything or return truefrom verify.

最好在密钥库中加载您的自签名证书(或加载您的私有 CA),然后将其传递给SSLContext.init. 然后一切都按预期工作,无需信任一切或trueverify.

Bruno and EJP have plenty of answers covering that subject.

Bruno 和 EJP 有很多关于该主题的答案。



What are the other alternate way to achieve a common solution for both development and production environment?

为开发和生产环境实现通用解决方案的其他替代方法是什么?

Use a well formed certificate that chains back to a trusted root.

使用格式良好的证书链接回受信任的根。

For testing, you can create a self signed certificate. Or, create a certificate request and have it signed by your internal CA in a private PKI. In this case, you need to trust your self signed certificate or trust your internal CA.

为了测试,您可以创建一个自签名证书。或者,创建证书请求并让您的内部 CA 在私有 PKI 中对其进行签名。在这种情况下,您需要信任您的自签名证书或信任您的内部 CA。

For production, you can use a certificate signed by one of the members of the CA Zoo so others outside the organization trusts it too. StartComand CACertoffer free Class 1 certificates.

对于生产,您可以使用由 CA Zoo 成员之一签名的证书,以便组织外的其他人也信任它。StartComCACert提供免费的 Class 1 证书。

Class 1 certificates are usually domain validated and don't allow wild cards. While the Class 1 is issued for free, they charge for revocation because that's where the cost lies.

1 类证书通常经过域验证,不允许使用通配符。虽然第 1 类是免费发行的,但它们会收取撤销费用,因为这就是成本所在。

If you need a wild card, then you will usually to purchase a Class 2 or higher.

如果您需要通配符,那么您通常会购买 2 级或更高级别的卡。

回答by Steve Jones

@jww rightly answers the question

@jww 正确回答了这个问题

Is it OK to skip SSL verification? No. That's very irresponsible.

可以跳过 SSL 验证吗?不,这是非常不负责任的。

However, in some cases you may not control the server in question to be able to install a valid certificate. If the server belongs to someone else, and you trust that server, a better solution is to use a "white list" to validate certificates only for trusted servers, otherwise use normal validation.

但是,在某些情况下,您可能无法控制相关服务器以安装有效证书。如果服务器属于其他人,并且您信任该服务器,则更好的解决方案是使用“白名单”仅验证受信任服务器的证书,否则使用常规验证。

public static class WhitelistHostnameVerifier implements HostnameVerifier {
    private static final HostnameVerifier defaultHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
    private Set<String> trustedHosts;

    public WhitelistHostnameVerifier(Set<String> trustedHosts) {
        this.trustedHosts = trustedHosts;
    }

    @Override
    public boolean verify(String hostname, SSLSession session) {
        if (trustedHosts.contains(hostname)) {
            return true;
        } else {
            return defaultHostnameVerifier.verify(hostname, session);
        }
    }
}

And install it once:

并安装一次:

HttpsURLConnection.setDefaultHostnameVerifier(
    new WhitelistHostnameVerifier(Sets.newHashSet("trustedhost.mydomain.com")));

If you're going to disable a security check, don't do it globally...

如果您要禁用安全检查,请不要全局执行...

回答by MacGyver

There is a label software bug with the Java Keytool for Java version 1.7.0_60-b19, when creating a self signed certificate. See these instructions for a reference.

创建自签名证书时,Java Keytool for Java 版本 1.7.0_60-b19 存在标签软件错误。请参阅这些说明以获取参考。

https://www.sslshopper.com/article-how-to-create-a-self-signed-certificate-using-java-keytool.html

https://www.sslshopper.com/article-how-to-create-a-self-signed-certificate-using-java-keytool.html

When it prompts you for "What is your first and last name?", instead of typing your name, you should enter the common name, or (fully qualified domain name of your server).

当它提示您“您的名字和姓氏是什么?”时,您应该输入通用名称或(服务器的完全限定域名),而不是输入您的姓名。

[root@localhost ~]# keytool -genkey -keyalg RSA -alias myalias -keystore keystore.jks -storepass XXXXXX -validity 360 -keysize 2048
What is your first and last name?
  [Unknown]:  Angus MacGyver
What is the name of your organizational unit?
  [Unknown]:  My Department
What is the name of your organization?
  [Unknown]:  My Company
What is the name of your City or Locality?
  [Unknown]:  My City
What is the name of your State or Province?
  [Unknown]:  My State
What is the two-letter country code for this unit?
  [Unknown]:  US
Is CN=Angus MacGyver, OU=My Department, O=My Company, L=My City, ST=My State, C=US correct?
  [no]:  yes
Enter key password for <selfsigned>
        (RETURN if same as keystore password):XXXXXX
Re-enter new password:XXXXXX

You can verify if the "CN" (common name) attribute gets set correctly by calling this:

您可以通过调用以下代码来验证“CN”(通用名称)属性是否设置正确:

[root@localhost ~]# keytool -v -list -keystore keystore.jks

Find Java version:

查找 Java 版本:

[root@localhost ~]# java -version
java version "1.7.0_60"
Java(TM) SE Runtime Environment (build 1.7.0_60-b19)
Java HotSpot(TM) Client VM (build 24.60-b09, mixed mode, sharing)