Netty服务器:如何添加SAN白名单绕过主机名验证及重写验证方法?
Netty 自定义SAN白名单绕过主机名验证方案
你可以通过两种方式重写Netty的主机名验证逻辑,实现SAN条目白名单功能:
方法一:自定义HostnameVerifier并配置到SslContext
直接实现HostnameVerifier接口,在逻辑中加入SAN白名单校验,替代默认的HTTPS端点验证:
// 定义SAN白名单集合 private static final Set<String> SAN_WHITELIST = new HashSet<>(Arrays.asList( "allowed-host-1.com", "allowed-host-2.com", "192.168.1.100" // 支持IP类型的SAN条目 )); // 自定义主机名验证器 HostnameVerifier customVerifier = (hostname, session) -> { try { // 获取对方证书 X509Certificate cert = (X509Certificate) session.getPeerCertificates()[0]; Collection<List<?>> sanEntries = cert.getSubjectAlternativeNames(); if (sanEntries != null) { for (List<?> entry : sanEntries) { Integer type = (Integer) entry.get(0); String sanValue = (String) entry.get(1); // 2=DNS类型SAN,7=IP类型SAN if ((type == 2 || type == 7) && SAN_WHITELIST.contains(sanValue.toLowerCase())) { return true; } } } // 可选: fallback 验证原始请求hostname return SAN_WHITELIST.contains(hostname.toLowerCase()); } catch (CertificateException e) { return false; } }; // 构建SslContext时绑定自定义验证器 SslContext sslContext = SslContextBuilder.forServer(new File("server.crt"), new File("server.key")) .hostnameVerifier(customVerifier) .build();
配置后,Netty会用这个自定义验证器接管主机名校验,无需再手动设置setEndpointIdentificationAlgorithm。
方法二:重写SslHandler的verifyPeerCertificate方法
如果需要更细粒度的控制,可以继承SslHandler,重写证书验证逻辑:
public class WhitelistedSslHandler extends SslHandler { private static final Set<String> SAN_WHITELIST = new HashSet<>(Arrays.asList( "allowed-host-1.com", "192.168.1.100" )); public WhitelistedSslHandler(SSLEngine engine) { super(engine); } @Override protected void verifyPeerCertificate(X509Certificate[] peerCerts, String hostname) throws SSLException { boolean isWhitelisted = false; try { X509Certificate cert = peerCerts[0]; Collection<List<?>> sanEntries = cert.getSubjectAlternativeNames(); if (sanEntries != null) { for (List<?> entry : sanEntries) { Integer type = (Integer) entry.get(0); String sanValue = ((String) entry.get(1)).toLowerCase(); if ((type == 2 || type == 7) && SAN_WHITELIST.contains(sanValue)) { isWhitelisted = true; break; } } } // 白名单匹配则跳过默认验证,否则执行原有逻辑 if (!isWhitelisted) { super.verifyPeerCertificate(peerCerts, hostname); } } catch (CertificateException e) { throw new SSLException("证书SAN校验失败", e); } } }
使用时替换原有的SslHandler:
SSLEngine sslEngine = getSSLEngine(); SslHandler sslHandler = new WhitelistedSslHandler(sslEngine); channelPipeline.addLast(sslHandler);
注意事项
- 生产环境避免完全禁用验证,严格控制白名单范围
- 建议统一将SAN条目和请求hostname转成小写后再比较,避免大小写不匹配问题
内容的提问来源于stack exchange,提问作者user1845029
相关产品推荐
相关产品推荐

