我正在使用SSLEngine
withNIO
进行非阻塞 TLS 通信。
该引擎工作正常,并且能够验证 CA 信任、服务器证书的有效期。
但我缺少的是证书主题名称与我连接的服务器名称匹配的验证。
考虑到主题替代名称等所有 SSL 扩展,实现它的最佳方法是什么?
我尝试简单地检查主题名称,但即使使用 ,它也不起作用google.nl
,证书中的主题是google.com
,并且google.nl
只能在替代名称扩展中找到。
又要回答我自己的问题了……
到目前为止,我能够做到这一点。它仅限于 Sun JSSE 实现。不确定这是否是一个完整的解决方案。
void checkServerName(String serverName, SSLSession session) throws IOException {
String name;
boolean matches = false;
X509CertImpl cert = (X509CertImpl)session.getPeerCertificates()[0];
SubjectAlternativeNameExtension altNames = cert.getSubjectAlternativeNameExtension();
if (altNames != null) {
GeneralNames names = (GeneralNames) altNames.get(SubjectAlternativeNameExtension.SUBJECT_NAME);
for (GeneralName genName : names.names()) {
switch (genName.getType()) {
case GeneralNameInterface.NAME_DNS:
name = ((DNSName)genName.getName()).getName();
break;
case GeneralNameInterface.NAME_IP:
name = ((IPAddressName)genName.getName()).getName();
break;
case GeneralNameInterface.NAME_RFC822:
name = ((RFC822Name)genName.getName()).getName();
break;
case GeneralNameInterface.NAME_ANY:
DerValue derValue = new DerValue(((OtherName)genName.getName()).getNameValue());
name = derValue.getData().getUTF8String();
break;
default:
continue;
}
if (name.startsWith("*"))
matches |= serverName.endsWith(name.substring(1).toLowerCase());
else
matches |= serverName.equals(name.toLowerCase());
if (matches)
break;
}
}
name = ((X500Name)cert.getSubjectDN()).getCommonName();
if (!matches)
matches = serverName.equals(name.toLowerCase());
if (!matches)
throw new SSLPeerUnverifiedException("server name mismatch, certificate issued to: " + name);
}