SSL证书服务器名称是如何解析的?我可以使用keytool添加替代名称吗?

83

这些问题可能用分开的方式更加清晰,但它们都涉及同一个问题。

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

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

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

背景:我需要让一个主服务器使用HTTPS与多个服务器通信。很明显,我们不想为每台服务器(可能有很多)购买SSL证书,所以我想使用自签名证书(我一直在使用keytool生成它们)。在将证书添加到操作系统中作为受信任的证书之后,浏览器(IE和Chrome)高兴地接受连接并将其视为可信。然而,即使在将证书添加到Java的cacerts之后,Java仍然不会接受连接并抛出以下异常:

Caused by: java.security.cert.CertificateException: No subject alternative names present at sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:142) at sun.security.util.HostnameChecker.match(HostnameChecker.java:75) at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkIdentity(X509T rustManagerImpl.java:264) at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted( X509TrustManagerImpl.java:250) at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Clien tHandshaker.java:1185) ... 14 more

我发现,使用自己实现的HostNameVerifier可以让Java信任证书,我从这里复制了HostNameVerifier:com.sun.jbi.internal.security.https.DefaultHostnameVerifier 只是为了测试(顺便说一句,传递给HostNameVerifier的主机名是正确的,所以我认为它应该被接受)。

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

请问我是否做错了什么,并指出正确的方向?


1
请选择您的替代方案:http://grepcode.com/search?query=DefaultHostnameVerifier&n= - Renato
1个回答

111

如何进行主机名验证是在RFC 6125中定义的,这是一个相当新的标准,并将实践推广到所有协议,取代了专门针对HTTPS的RFC 2818。(我甚至不确定Java 7是否使用了RFC 6125,因为它可能太新了。)

RFC 2818(第3.1节)中获得以下信息:

如果存在类型为dNSName的subjectAltName扩展名,则必须将其用作标识。否则,必须使用证书Subject字段中最具体的通用名称字段。虽然使用通用名称是现有的做法,但已经被弃用,并鼓励证书颁发机构使用dNSName。

[...]

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

基本上,你遇到的具体问题源于在CN中使用IP地址而不是主机名。某些浏览器可能会工作,因为并非所有工具都严格遵循此规范,特别是因为RFC 2818中的“最具体”没有明确定义(请参见RFC 6215中的讨论)。

如果你正在使用keytool,自Java 7起,keytool具有包含Subject Alternative Name的选项(请参考-ext文档中的表格):你可以使用-ext san=dns:www.example.com-ext san=ip:10.0.0.1

编辑:

你可以通过更改openssl.cnf来请求SAN(如果不想编辑全局配置文件,则它将选择当前目录中的副本,只需记住您可以使用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


2
请注意,您可以在另一台计算机上使用Java 7中的keytool,并稍后复制密钥库(您不必运行Java 7)。或者,我已经编辑了使用OpenSSL进行操作的答案。话虽如此,从长远来看,您可能会发现使用主机名而不是IP地址更加灵活(使用SANs也是一个好主意)。 - Bruno
6
您在证书中使用IP地址将成为问题的更大隐患。可能会更改IP地址,这就是DNS存在的原因。您可以通过使用动态DNS服务,并使用具有该名称的证书(或任何解析为该动态名称的CNAME)来更好地解决问题。 - Bruno
查看诸如dyndns.org(及其自动更新工具)之类的服务。确保更新的地址对希望连接的客户有意义(取决于接口和/或是否具有反向NAT)。 - Bruno
1
只是为了增加一些混淆,许多浏览器将接受类似DNS:10.0.0.1的SAN,但不接受IP:10.0.0.1,但好消息是你可以同时拥有两者。 - KCD
非常感谢您。这解决了我在尝试从Jersey客户端拨打无法通过DNS解析的服务器(我使用服务器的IP进行通话)时遇到的问题。 - Mouhammed Soueidane
显示剩余5条评论

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