为清楚起见,这些可能被表述为单独的问题,但它们都与同一问题有关.
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)中没有主题备用名称. 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更多
我发现我可以让Java信任实现我自己的HostNameVerifier的证书,我从这里复制了:com.sun.jbi.internal.security.https.DefaultHostnameVerifier只是为了测试(顺便说一句,主机名作为参数传递给HostnameVerifier是正确的,所以我认为应该已经接受了).
我一直使用证书字段CN作为主机名(通常是IP地址).
任何人都可以告诉我,如果我做错了什么,并指出我正确的方向?
如何进行主机名验证在RFC 6125中定义,这是最新的并且将该实践推广到所有协议,并取代了特定于HTTPS的RFC 2818.(我甚至不确定Java 7是否使用RFC 6125,这可能是最新的.)
来自RFC 2818(第3.1节):
如果存在类型为dNSName的subjectAltName扩展名,则必须将其用作标识.否则,必须使用证书的Subject字段中的(最具体的)Common Name字段.尽管使用Common Name是现有做法,但不推荐使用它,并且鼓励证书颁发机构使用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