如何修复"java.security.cert.CertificateException:没有主题替代名称出现"错误?

DP_*_*DP_ 98 java ssl https certificate ssl-certificate

我有一个Java Web服务客户端,它通过HTTPS使用Web服务.

import javax.xml.ws.Service;

@WebServiceClient(name = "ISomeService", targetNamespace = "http://tempuri.org/", wsdlLocation = "...")
public class ISomeService
    extends Service
{

    public ISomeService() {
        super(__getWsdlLocation(), ISOMESERVICE_QNAME);
    }
Run Code Online (Sandbox Code Playgroud)

当我连接到服务URL(https://AAA.BBB.CCC.DDD:9443/ISomeService)时,我得到了异常java.security.cert.CertificateException: No subject alternative names present.

为了解决这个问题,我首先运行openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt并在文件中获得以下内容certs.txt:

CONNECTED(00000003)
---
Certificate chain
 0 s:/CN=someSubdomain.someorganisation.com
   i:/CN=someSubdomain.someorganisation.com
-----BEGIN CERTIFICATE-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=someSubdomain.someorganisation.com
issuer=/CN=someSubdomain.someorganisation.com
---
No client certificate CA names sent
---
SSL handshake has read 489 bytes and written 236 bytes
---
New, TLSv1/SSLv3, Cipher is RC4-MD5
Server public key is 512 bit
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : RC4-MD5            
    Session-ID: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    Session-ID-ctx:                 
    Master-Key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    Key-Arg   : None
    Start Time: 1382521838
    Timeout   : 300 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---
Run Code Online (Sandbox Code Playgroud)

AFAIK,现在我需要

  1. 提取的部分certs.txt之间-----BEGIN CERTIFICATE----------END CERTIFICATE-----,
  2. 修改它,使证书名称等于AAA.BBB.CCC.DDD
  3. 然后使用keytool -importcert -file fileWithModifiedCertificate(其中fileWithModifiedCertificate是操作1和2的结果)导入结果.

它是否正确?

如果是这样,我如何才能使步骤1中的证书与基于IP的adddress(AAA.BBB.CCC.DDD)一起使用?

更新1( 2013年10月23日15:37 MSK):在回答类似问题时,我读了以下内容:

如果您无法控制该服务器,请使用其主机名(前提是在现有证书中至少存在与该主机名匹配的CN).

"使用"究竟是什么意思?

DP_*_*DP_ 146

我通过使用此处介绍的方法禁用HTTPS检查来解决问题:

我将以下代码放入ISomeService该类:

static {
    disableSslVerification();
}

private static void disableSslVerification() {
    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();
    }
}
Run Code Online (Sandbox Code Playgroud)

由于我https://AAA.BBB.CCC.DDD:9443/ISomeService仅用于测试目的,因此它是一个足够好的解决方案.

  • 禁用HTTPS检查不是"解决方案".你应该说我找到了一个"补丁". (54认同)
  • 这将导致 Pre_prod 和 Production 上的漏洞。1. 在 windows 主机文件上创建主机条目 2. 或者在证书中的主题替代名称字段上添加 IP 或 FQDN 名称 (3认同)
  • 证书检查和主机名检查是有充分理由的。虽然忽略所有警告可能是一种解决方法,但这绝对不是解决方案。它违背了 SSL 旨在提供的所有安全性。 (2认同)

jua*_*lgo 30

我有同样的问题,并用这段代码解决了.我在第一次调用webservices之前放了这段代码.

javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier(){

    public boolean verify(String hostname,
            javax.net.ssl.SSLSession sslSession) {
        return hostname.equals("localhost");
    }
});
Run Code Online (Sandbox Code Playgroud)

它很简单,工作正常.

是原始来源.

  • 或者你可以用`return hostname.equals("localhost");`替换整个verify()函数体,如果这是你想要做的.`if`完全是多余的. (3认同)

Bru*_*uno 22

根据客户端请求执行证书身份验证.

当您的客户端使用https://xxx.xxx.xxx.xxx/something(在哪里xxx.xxx.xxx.xxx是IP地址)时,将根据此IP地址检查证书身份(理论上,仅使用IP SAN扩展).

如果您的证书没有IP SAN,但没有DNS SAN(或者如果没有DNS SAN,主题DN中的公共名称),您可以通过使客户端使用具有该主机名的URL(或主机名)来使其工作.证书有效,如果有多个可能的值).例如,如果您的证书有名称www.example.com,请使用https://www.example.com/something.

当然,您需要该主机名才能解析为该IP地址.

此外,如果存在任何DNS SAN,则将忽略主题DN中的CN,因此在这种情况下使用与其中一个DNS SAN匹配的名称.

  • 在我的代码中,我将请求发送到公共 IP,但证书 CN 是主机名。所以在我的代码中,我用主机名替换了 IP,并配置了我的 /etc/hosts 以将此主机名与 IP 相关联。解决了 ! (2认同)

小智 21

这是一个老问题,但从JDK 1.8.0_144转到jdk 1.8.0_191时遇到了同样的问题

我们在更改日志中找到了一个提示:

更新日志

我们添加了以下附加系统属性,这有助于我们解决此问题:

-Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true
Run Code Online (Sandbox Code Playgroud)

  • 这很疯狂,但没有人知道他们所做的检查实际上会阻止什么。只是工程师添加一些东西,因为他们在thoughtworks博客上读到了它 (2认同)
  • @bharal 该检查可防止对您的安全连接进行中间人攻击。最好不要禁用所有证书验证,除非您 100% 确定它只会在开发环境中使用。 (2认同)

小智 13

要导入证书:

  1. 从服务器中openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt提取证书,例如,这将以PEM格式提取证书.
  2. 将证书转换为DER格式,因为这是keytool期望的,例如 openssl x509 -in certs.txt -out certs.der -outform DER
  3. 现在,您要将此证书导入系统默认的"cacert"文件.找到Java安装的系统默认"cacerts"文件.看看如何获取默认java安装的cacerts的位置?
  4. 将证书导入该cacerts文件:sudo keytool -importcert -file certs.der -keystore <path-to-cacerts>默认cacerts密码为'changeit'.

如果为FQDN颁发了证书,并且您正在尝试通过Java代码中的IP地址进行连接,那么这应该在您的代码中修复,而不是弄乱证书本身.将您的代码更改为通过FQDN连接.如果您的开发计算机上无法解析FQDN,只需将其添加到您的hosts文件,或使用可以解析此FQDN的DNS服务器配置您的计算机.


lif*_*ary 7

您可能不想禁用所有 ssl 验证,因此您可以通过以下方式禁用主机名验证,这比替代方案要简单一些:

HttpsURLConnection.setDefaultHostnameVerifier(
    SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Run Code Online (Sandbox Code Playgroud)

[编辑]

正如 conapart3 所提到的SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER,现在已弃用,因此它可能会在以后的版本中被删除,因此您将来可能会被迫推出自己的解决方案,尽管我仍然会说我会避开任何关闭所有验证的解决方案。

  • `SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER` 现已弃用。 (3认同)

Abh*_*oda 5

我以正确的方式解决了此问题,方法是在证书中添加主题alt名称,而不是进行任何代码更改或禁用SSL(与其他答案不同)。如果您清楚地看到异常,则说明“缺少主题名称”,因此正确的方法是添加它们

请查看此链接以逐步了解

上面的错误表示您的JKS文件缺少尝试访问该应用程序所需的域。您将需要使用Open SSL和密钥工具来添加多个域

  1. 将openssl.cnf复制到当前目录
  2. echo '[ subject_alt_name ]' >> openssl.cnf
  3. echo 'subjectAltName = DNS:example.mydomain1.com, DNS:example.mydomain2.com, DNS:example.mydomain3.com, DNS: localhost'>> openssl.cnf
  4. openssl req -x509 -nodes -newkey rsa:2048 -config openssl.cnf -extensions subject_alt_name -keyout private.key -out self-signed.pem -subj '/C=gb/ST=edinburgh/L=edinburgh/O=mygroup/OU=servicing/CN=www.example.com/emailAddress=postmaster@example.com' -days 365
  5. 将公钥(.pem)文件导出为PKS12格式。这将提示您输入密码

    openssl pkcs12 -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -export -in
    self-signed.pem -inkey private.key -name myalias -out keystore.p12
    
    Run Code Online (Sandbox Code Playgroud)
  6. 从自签名PEM(密钥库)创建a.JKS

    keytool -importkeystore -destkeystore keystore.jks -deststoretype PKCS12 -srcstoretype PKCS12 -srckeystore keystore.p12
    
    Run Code Online (Sandbox Code Playgroud)
  7. 从上面的密钥库或JKS文件生成证书

    keytool -export -keystore keystore.jks -alias myalias -file selfsigned.crt
    
    Run Code Online (Sandbox Code Playgroud)
  8. 由于上述证书是经过自签名的,并且未经CA验证,因此需要将其添加到Truststore中(对于Windows,对于MAC,在以下位置的CAcerts文件中,请找到您的JDK的安装位置。)

    sudo keytool -importcert -file selfsigned.crt -alias myalias -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
    
    Run Code Online (Sandbox Code Playgroud)

原始答案发布在此链接上