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也行,然后再把这个组件显示出来。 【签名-撤销】原理是之前绘画时…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...