hostnameVerifier
方法概述基本原理参考资料
方法简介
在这篇博文中,我们将使用Okhttp 4.6.0 来分析hostnameVerfier 的作用。顾名思义,该方法的主要功能是识别主机名的合法性。 Okhttp初始化后,您可以自己设置hostnameVerfier。
新的OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(35, TimeUnit.SECONDS)
.hostnameVerifier(new HostnameVerifier() {
@覆盖
公共布尔验证(字符串主机名,SSLSession会话){
//注意,生产中不应该直接写true。
返回真。
}
})
。建造();
不过网上有很多资料是把verfiy直接变回true,这是非常危险的。当然,如果verify返回fasle,则说明主机名验证失败,http请求不会成功。例如,当您使用博客地址发起http 请求时,您会收到类似于以下内容的错误消息:
{http errorCode=-500, mErrorMsg=主机名yanchen.blog.csdn.net未验证:
证书: sha256/tlnf6pbfeu257hnJ9e6j4A1ZWH3vVMzn3Zn3F9kLHdg=
DN: CN=*.blog.csdn.net
subjectAltNames: [*.blog.csdn.net]}
执行后在RealConnection内执行Verify。
除了自定义的hostnameVerfier之外,Okhttp还提供了默认的实现。接下来我们来分析一下内部原理。
核心原理
OkHostnameVerifier 内置于Okhttp 中,该方法通过session.peerCertificates[0] 获取X509Certificate 形式的证书对象。
覆盖fun verify(host: String, session: SSLSession): Boolean {
请返回并尝试{
验证(主机,session.peerCertificates [0]作为X509Certificate)
捕获(_: SSLException){
错误
}
}
有趣的验证(主机:字符串,证书: X509Certificate):布尔值{
返回如果{
host.canParseAsIpAddress() – verifyIpAddress(主机,证书)
else – verifyHostname(主机,证书)
}
}
X509Certificate 对象提供一组get 方法,用于检索一组信息,例如证书的公钥和序列号。请参考以下内容:
最后,调用verifyHostname 方法,在获取SubjectAltName 后,将主机名与SubjectAltName 进行比较,如果匹配则返回true,如果不匹配则返回fasle。
私人乐趣verifyHostname(主机名:字符串,证书: X509Certificate):布尔值{
val 主机名=主机名.toLowerCase(Locale.US)
返回getSubjectAltNames(证书, ALT_DNS_NAME).any {
验证主机名(主机名,它)
}
}
//比较主机名和SubjectAltName
private fun verifyHostname(hostname: String?pattern: String?): Boolean {
var 主机名=主机名
var 模式=模式
//检查客户端域名的有效性
if (主机名.isNullOrEmpty() ||
主机名.startsWith(\’.\’) ||
主机名.endsWith(\’.\’)) {
//无效域名
返回错误
}
//检查证书中SubjectAltName的有效性
if (pattern.isNullOrEmpty() ||
模式.startsWith(\’.\’) ||
模式.endsWith(\’.\’)) {
//无效模式/域名
返回错误
}
//如果主机名和模式不是绝对域名,则将其转换为绝对域名并规范化。
//但是,这是必要的,因为服务器证书不包含绝对值。
//名称或模式,但也必须被视为任意主机名。
//出于整理目的,提供给此方法的任何内容都必须被视为绝对值
//到服务器证书。
//www.android.com 匹配www.android.com
//www.android.com 匹配www.android.com。
//www.android.com 匹配www.android.com。
//www.android.com 匹配www.android.com
if (!hostname.endsWith(\’.\’)) {
主机名+=\’.\’
}
if (!pattern.endsWith(\’.\’)) {
模式+=\’.\’
}
//主机名和模式现在是绝对域名。
模式=pattern.toLowerCase(Locale.US)
//主机名和模式现在为小写——域名不区分大小写。
if (\’*\’ !内部模式) {
//不是通配符模式——主机名和模式必须完全匹配。
主机名==返回模式
}
//通配符模式
//通配符模式规则:
//1. 星号(*)只允许出现在最左边的域名标签中,
//仅该标签内的字符(即,它必须匹配整个最左边的标签)。
//例如,允许使用*.example.com,但是*a.example.com、a*.example.com、
//不允许a*b.example.com、a.*.example.com。
//2. 域名标签之间不能匹配星号(*)。
//例如,*.example.com 匹配test.example.com,但不匹配
//sub.test.example.com。
//3. 单标签域名不允许使用通配符模式。
if (!pattern.startsWith(\’*.\’) || pattern.indexOf(\’*\’, 1) !=-1) {
//星号(*)只能出现在最左边的域名标签中,并且必须是唯一的
//该标签中的字符
返回错误
}
//Optimization: 检查主机名是否太短而无法匹配模式。
//星号必须匹配整个最左边的标签,因此它必须与模式长度相同。
//主机名以非空标签开头。因此,星号必须匹配一个或多个字符。
if (主机名.lengthpattern.length) {
return false //主机名太短,无法匹配模式。
}
if (\’*.\’==模式) {
return false //单标签域名通配符模式——不允许。
}
//主机名必须以星号后面的模式中的字段结尾。
val 后缀=pattern.substring(1)
if (!hostname.endsWith(suffix)) {
return false //主机名不以后缀结尾。
}
//确保域名标签之间的星号不匹配。
val suffixStartIndexInHostname=主机名.length – 后缀.length
if (suffixStartIndexInHostname 0
主机名.lastIndexOf(\’.\’, suffixStartIndexInHostname – 1) !=-1) {
return false //域名标签之间的星号匹配——不允许。
}
//主机名与模式匹配。
返回真
}
那么SubjectAltName是什么?您可以通过以下方式获取它。
新的主机名验证器(){
@覆盖
公共布尔验证(字符串主机名,SSLSession会话){
尝试{
X509Certificate x509Certificate=(X509Certificate) session.getPeerCertificates()[0];
集合列表?
for (列表? subjectAltName : subjectAltNames) {
if (subjectAltName==null || subjectAltName.size() 2) 继续;
int 类型=(int)subjectAltName.get(0);
if (type!=2) 继续;
字符串altName=(String)subjectAltName.get(1);
LogUtil.logD(\’hostnameVerifier\’,\’x509Certificate altName==\’+altName);
}
} catch (异常e) {
}
返回真。
}
}
Okhttp内置的主机名验证逻辑非常简单,您可以自己检查源代码。
参考资料
Android CertificateSource 获取和获取系统根证书Android https TrustManager checkServerTrusted Android RootTrustManager 详解证书验证简单分析Android CertificateSource 获取和获取系统根证书Android NSSP 简单解释Okhttp 简单分析RealConnection 建立的链接
以上相关内容来源网络对#Okhttp hostnameVerifier的详细描述,仅供参考。相关信息请参见官方公告。
原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/93037.html