SSL证书服务器名称如何解析/是否可以使用keytool添加备用名称?


78

为了清楚起见,可以将这些问题表述为单独的问题,但是它们都与同一问题相关。

SSL证书服务器名称如何解析?

为什么浏览器似乎使用证书的CN字段,但是Java的机制似乎仅查看“主题备用名称”?

是否可以使用keytool将替代名称添加到SSL证书?如果不是,使用openSSL代替是一个不错的选择吗?

只是一点点背景:我需要获得一个主服务器,以便使用HTTPS与多个服务器进行通信。显然,我们不想为每台服务器购买SSL证书(可能有很多),所以我想使用自签名证书(我一直在使用keytool生成它们)。在操作系统中将证书添加为受信任的证书后,浏览器(IE和Chrome)欣然接受该连接为受信任的证书。但是,即使将证书添加到Java的cacerts中,Java仍然不会将连接接受为受信任的,并引发以下异常:

由以下原因引起:java.security.cert.CertificateException:sun.security.util.HostnameChecker.match(HostnameChecker.java:75)上的sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:142)上没有使用者替代名称。在com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:250)处com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkIdentity(X509T rustManagerImpl.java:264) sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Clien tHandshaker.java:1185)...还有14个

我发现我可以使Java信任实现我自己的HostNameVerifier的证书,该证书是我从此处复制的:com.sun.jbi.internal.security.https.DefaultHostnameVerifier只是为了进行测试(顺便说一下,主机名作为参数传递给了HostnameVerifier是正确的,因此我认为它应该已经被接受)。

我一直使用证书字段CN作为主机名(通常是IP地址)。

有人可以告诉我我做错了什么,并指出正确的方向吗?


Answers:


100

RFC 6125中定义了应该如何完成主机名验证,这是很新的方法,将实践推广到所有协议,并取代了HTTPS专用的RFC 2818。(我什至不确定Java 7是否使用RFC 6125,对此可能太新了。)

RFC 2818(第3.1节)开始

如果存在类型为dNSName的subjectAltName扩展名,则该扩展名必须用作标识。否则,必须使用证书的“主题”字段中的(最特定的)“公共名称”字段。尽管使用通用名是惯例,但不建议使用,建议鼓励证书颁发机构改为使用dNSName。

[...]

在某些情况下,URI被指定为IP地址而不是主机名。在这种情况下,iPAddress subjectAltName必须存在于证书中,并且必须与URI中的IP完全匹配。

从本质上讲,您遇到的特定问题来自您在CN中使用IP地址而不是主机名这一事实。某些浏览器之所以可以工作,是因为并非所有工具都严格遵循此规范,特别是因为RFC 2818中的“最具体”没有明确定义(请参阅RFC 6215中的讨论)。

如果您正在使用keytool从Java 7开始,keytool可以选择包含使用者备用名称(请参见文档中的表-ext):您可以使用-ext san=dns:www.example.com-ext san=ip:10.0.0.1

编辑:

您可以通过更改来请求OpenSSL中的SAN openssl.cnf(据我所知,如果您不想编辑全局配置,它会在当前目录中选择一个副本,或者您可以使用OPENSSL_CONF环境变量选择一个显式位置)。

设置以下选项(首先在方括号内找到相应的部分):

[req]
req_extensions = v3_req

[ v3_req ]
subjectAltName=IP:10.0.0.1
# or subjectAltName=DNS:www.example.com

在此处使用环境变量(而不是将其固定在配置文件中)还有一个不错的技巧:http : //www.crsr.net/Notes/SSL.html


这正是我需要知道的...但是,看来Java 6没有这个-ext选项。我将尝试将VM更改为Java 7并进行测试。
雷纳托

2
请注意,您可以在另一台机器上使用Java 7中的keytool,并在以后复制密钥库(不必运行Java 7)。另外,我已经编辑了使用OpenSSL的答案。话虽这么说,从长远来看,您可能会发现使用主机名代替IP地址更加灵活(无论如何,使用SAN是个好主意)。
布鲁诺

-ext选项在Java6中不起作用!我将切换到Java 7,看看是否可以仅使用keytool就可以做到这一点...非常感谢您的答案..(PS。一旦测试过,将接受答案)
Renato

5
在这种情况下,您在证书中使用IP地址这一事实将变得更加困难。DNS可能用于更改IP地址。通过使用动态DNS服务并使用具有该名称的证书(或可以解析为该动态名称的任何CNAME),您更有可能解决您的问题。
布鲁诺

1
只是增加了一些混乱,许多浏览器会接受SAN,例如DNS:10.0.0.1,但不接受IP:10.0.0.1,但是好消息是您可以同时使用这两种方式
KCD
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.