Okhttp hostnameVerifier详解
hostnameVerifier
- 方法简介
- 核心原理
- 参考资料
方法简介
本篇博文以Okhttp 4.6.0来解析hostnameVerfier的作用,顾名思义,该方法的主要作用就是鉴定hostnname的合法性。Okhttp在初始化的时候我们可以自己配置hostnameVerfier:
new OkHttpClient.Builder().connectTimeout(20, TimeUnit.SECONDS).readTimeout(20, TimeUnit.SECONDS).writeTimeout(35, TimeUnit.SECONDS) .hostnameVerifier(new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {//注意这里在生产环境中千万不要直接写死true return true;}}).build();
但是网上好多资料将verfiy直接返回true是十分危险的。当然如果vertify返回fasle意味着hostname验证不通过,http请求无法成功,比如我以自己的博客地址发起http请求,错误信息如下:

{http errorCode=-500, mErrorMsg=Hostname yanchen.blog.csdn.net not verified:certificate: sha256/tlnf6pbfeu257hnJ9e6j4A1ZWH3vVMzn3Zn3F9kLHdg=DN: CN=*.blog.csdn.netsubjectAltNames: [*.blog.csdn.net]}
执行vertify的地方是在RealConnection里面,执行之后。

除了自定义hostnameVerfier之外,Okhttp提供了默认实现,现在就分析下起内部原理。
核心原理
在Okhttp内置了OkHostnameVerifier,该方法通过session.peerCertificates[0] as X509Certificate获取证书的对象
override fun verify(host: String, session: SSLSession): Boolean {return try {verify(host, session.peerCertificates[0] as X509Certificate)} catch (_: SSLException) {false}}fun verify(host: String, certificate: X509Certificate): Boolean {return when {host.canParseAsIpAddress() -> verifyIpAddress(host, certificate)else -> verifyHostname(host, certificate)}}
通过X509Certificate对象提供了一系列get方法可以获取到证书的公钥,序列号等一系列信息。见下图:

最终会调用verifyHostname方法,通过certificate获取getSubjectAltNames拿到SubjectAltName之后,将hostname与SubjectAltName进行比对,如果符合就返回true,否则就返回fasle.
private fun verifyHostname(hostname: String, certificate: X509Certificate): Boolean {val hostname = hostname.toLowerCase(Locale.US)return getSubjectAltNames(certificate, ALT_DNS_NAME).any {verifyHostname(hostname, it)}}//hostname和SubjectAltName比对
private fun verifyHostname(hostname: String?, pattern: String?): Boolean {var hostname = hostnamevar pattern = pattern//检验客户端域名的有效性if (hostname.isNullOrEmpty() ||hostname.startsWith(".") ||hostname.endsWith("..")) {// Invalid domain namereturn false}//检验证书中SubjectAltName的有效性if (pattern.isNullOrEmpty() ||pattern.startsWith(".") ||pattern.endsWith("..")) {// Invalid pattern/domain namereturn false}// Normalize hostname and pattern by turning them into absolute domain names if they are not// yet absolute. This is needed because server certificates do not normally contain absolute// names or patterns, but they should be treated as absolute. At the same time, any hostname// presented to this method should also be treated as absolute for the purposes of matching// to the server certificate.// www.android.com matches www.android.com// www.android.com matches www.android.com.// www.android.com. matches www.android.com.// www.android.com. matches www.android.comif (!hostname.endsWith(".")) {hostname += "."}if (!pattern.endsWith(".")) {pattern += "."}// Hostname and pattern are now absolute domain names.pattern = pattern.toLowerCase(Locale.US)// Hostname and pattern are now in lower case -- domain names are case-insensitive.if ("*" !in pattern) {// Not a wildcard pattern -- hostname and pattern must match exactly.return hostname == pattern}// Wildcard pattern// WILDCARD PATTERN RULES:// 1. Asterisk (*) is only permitted in the left-most domain name label and must be the// only character in that label (i.e., must match the whole left-most label).// For example, *.example.com is permitted, while *a.example.com, a*.example.com,// a*b.example.com, a.*.example.com are not permitted.// 2. Asterisk (*) cannot match across domain name labels.// For example, *.example.com matches test.example.com but does not match// sub.test.example.com.// 3. Wildcard patterns for single-label domain names are not permitted.if (!pattern.startsWith("*.") || pattern.indexOf('*', 1) != -1) {// Asterisk (*) is only permitted in the left-most domain name label and must be the only// character in that labelreturn false}// Optimization: check whether hostname is too short to match the pattern. hostName must be at// least as long as the pattern because asterisk must match the whole left-most label and// hostname starts with a non-empty label. Thus, asterisk has to match one or more characters.if (hostname.length < pattern.length) {return false // Hostname too short to match the pattern.}if ("*." == pattern) {return false // Wildcard pattern for single-label domain name -- not permitted.}// Hostname must end with the region of pattern following the asterisk.val suffix = pattern.substring(1)if (!hostname.endsWith(suffix)) {return false // Hostname does not end with the suffix.}// Check that asterisk did not match across domain name labels.val suffixStartIndexInHostname = hostname.length - suffix.lengthif (suffixStartIndexInHostname > 0 &&hostname.lastIndexOf('.', suffixStartIndexInHostname - 1) != -1) {return false // Asterisk is matching across domain name labels -- not permitted.}// Hostname matches pattern.return true}
那么SubjectAltName是什么?我们可以通过如下方法获取:
new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {try {X509Certificate x509Certificate= (X509Certificate) session.getPeerCertificates()[0];Collection<List<?>> subjectAltNames = x509Certificate.getSubjectAlternativeNames();for (List<?> subjectAltName : subjectAltNames) {if (subjectAltName == null || subjectAltName.size() < 2) continue;int type = (int)subjectAltName.get(0);if (type!= 2) continue;String altName = (String)subjectAltName.get(1);LogUtil.logD("hostnameVerifier","x509Certificate altName=="+altName);}} catch (Exception e) {} return true;}}


Okhttp 内置的hostname校验逻辑很简单,大家可以自行查看起源码即可。
参考资料
- Android CertificateSource系统根证书的检索和获取
- Android https TrustManager checkServerTrusted 详解
- Android RootTrustManager 证书校验简单分析
- Android CertificateSource系统根证书的检索和获取
- Android AndroidNSSP的简单说明
- Okhttp之RealConnection建立链接简单分析
相关文章:
Okhttp hostnameVerifier详解
hostnameVerifier 方法简介核心原理参考资料 方法简介 本篇博文以Okhttp 4.6.0来解析hostnameVerfier的作用,顾名思义,该方法的主要作用就是鉴定hostnname的合法性。Okhttp在初始化的时候我们可以自己配置hostnameVerfier: new OkHttpClien…...
TCP的p2p网络模式
TCP的p2p网络模式 1、tcp连接的状态有以下11种 CLOSED:关闭状态LISTEN:服务端状态,等待客户端发起连接请求SYN_SENT:客户端已发送同步连接请求,等待服务端相应SYN_RECEIVED:服务器收到客户端的SYN请请求&…...
力扣-贪心算法4
406.根据身高重建队列 406. 根据身高重建队列 题目 假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或…...
动手学深度学习6.2 图像卷积-笔记练习(PyTorch)
以下内容为结合李沐老师的课程和教材补充的学习笔记,以及对课后练习的一些思考,自留回顾,也供同学之人交流参考。 本节课程地址:卷积层_哔哩哔哩_bilibili 代码_哔哩哔哩_bilibili 本节教材地址:6.2. 图像卷积 — 动…...
展开说说:Android服务之bindService解析
前面两篇文章我们分别总结了Android四种Service的基本使用以及源码层面总结一下startService的执行过程,本篇继续从源码层面总结bindService的执行过程。 本文依然按着是什么?有什么?怎么用?啥原理?的步骤来分析。 b…...
node-sass 老版本4.14.0 安装失败解决办法
旧项目 npm install 发现 node-sass 安装 失败 切换淘宝镜像之后 不能完全解决问题。因为需要编译,本地没有Python环境不能实现 安装node-sass时,在install阶段会从Github上下载一个叫binding.node的文件,而「GitHub Releases」里的文件…...
最近很火的字幕截图生成器
网址 https://disksing.com/fake-screenshot/ 最近很火的字幕截图生成器,对于自媒体来说真的太实用了 另外透露一下,你仔细研究就会发现,这是个纯前端的项目...
使用RabbitMQ实现可靠的消息传递机制
使用RabbitMQ实现可靠的消息传递机制 大家好,我是微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 1. RabbitMQ简介 RabbitMQ是一个开源的消息代理软件,实现了高级消息队列协议(AMQP&…...
Function Call ReACT,Agent应用落地的加速器_qwen的function calling和react有什么不同
探索智能体Agent的未来之路:Function Call与ReACT框架的较量,谁能引领未来? 引言 各大平台出现智能体应用创建,智能体逐渐落地,背后的使用哪种框架? 随着各大平台,例如百度千帆APPbuilder、阿…...
Java的JSONPath(fastjson)使用总结
背景 最近使用json实现复杂业务配置, 因为功能需要解析读取json的中节点数据。如果使用循环或者stream处理,可以实现,但是都过于麻烦。在想能否使用更简单json读取方式,正好发现fastjson支持该功能,本文做一个记录 案例说明 示…...
【大模型】大语言模型:光鲜背后的阴影——事实准确性和推理能力的挑战
大语言模型:光鲜背后的阴影——事实准确性和推理能力的挑战 引言一、概念界定二、事实准确性的局限2.1 训练数据的偏差2.2 知识的时效性问题2.3 复杂概念的理解与表述 三、推理能力的局限3.1 表层理解与深层逻辑的脱节3.2 缺乏常识推理3.3 无法进行长期记忆和连续推…...
Java面向对象练习(1.手机类)(2024.7.4)
手机类 package Phone;public class Phone {private String brand;private int price;private String color;public Phone(){}public Phone(String brand, int price, String color){this.brand brand;this.price price;this.color color;}public void setBrand(String bra…...
智慧生活新篇章,Vatee万腾平台领航前行
在21世纪的科技浪潮中,智慧生活已不再是一个遥远的梦想,而是正逐步成为我们日常生活的现实。从智能家居的温馨便捷,到智慧城市的高效运转,科技的每一次进步都在为我们的生活增添新的色彩。而在这场智慧生活的变革中,Va…...
Spring Cloud Gateway报sun.misc.Unsafe.park(Native Method)
项目引入spring cloud gateway的jar报,启动的时候报: [2024-07-05 10:10:16.162][main][ERROR][org.springframework.boot.web.embedded.tomcat.TomcatStarter][61]:Error starting Tomcat context. Exception: org.springframework.beans.factory.Bean…...
select single , select endselect
select single , select endselect single 根据条件找到一条数据,就出来了。 select endselect是在里面循环,每次找一条,依次放到into table中,或者放到into work area中,下面append table 。 实际开发中不建议这么操…...
后端学习(一)
添加数据库包: 数据库连接时 发生错误: 解决方式: SqlConnection conn new SqlConnection("serverlocalhost;databaseMyBBSDb;uidsa;pwd123456;Encryptfalse;") ;conn.Open();SqlCommand cmd new SqlCommand("SELECT * FROM…...
【活动行】参与上海两场线下活动,教育生态行业赛总决赛活动和WAIC人工智能大会活动 - 上海活动总结
目录 背景决赛最后一公里领域范围 决赛作品AI智教相机辅导老师Copilot辅导老师Copilot雅思写作竞技场 优秀作品总结 背景 决赛 百度发起的千帆杯教育生态行业赛于2024年7月4日进行线下决赛,博主虽然没能进入决赛,但也非常荣幸能够以嘉宾身份到现场给进…...
conda 安装设置
安装anaconda 推荐官网下载和安装,最新版本是anaconda3+python3.11,个人选择。有可能找不到 Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror Tips:小白一定要全部勾选,特别第二项“add anaconda3 to my path environment variable…...
用PlantUML和语雀画UML类图
概述 首先阐述一下几个简单概念: UML:是统一建模语言(Unified Modeling Language)的缩写,它是一种用于软件工程的标准化建模语言,旨在提供一种通用的方式来可视化软件系统的结构、行为和交互。UML由Grady…...
uniapp微信小程序电子签名
先上效果图,不满意可以直接关闭这页签 新建成单独的组件,然后具体功能引入,具体功能点击签名按钮,把当前功能页面用样式隐藏掉,v-show和v-if也行,然后再把这个组件显示出来。 【签名-撤销】原理是之前绘画时…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
