import android.content.Context import android.util.Log import com.wlld.common.utils.LogUtils import okhttp3.OkHttpClient import javax.net.ssl.SSLContext import javax.net.ssl.TrustManager import javax.net.ssl.X509TrustManager import java.security.SecureRandom
/**
OkHttp SSL 配置工具类
提供动态证书校验支持 */ object SSLConfigUtil {
private const val TAG = "SSLConfigUtil" private var dynamicTrustManager: DynamicSSLTrustManager? = null
/**
配置 OkHttp 使用动态 SSL 证书校验 */ fun OkHttpClient.Builder.configureDynamicSSL(context: Context): OkHttpClient.Builder { LogUtils.d(TAG, "🔧 开始配置动态 SSL...") return try { // 创建动态信任管理器 dynamicTrustManager = DynamicSSLTrustManager.create(context) LogUtils.d(TAG, "✅ 动态信任管理器创建成功")
// 创建 SSL 上下文 val sslCOntext= SSLContext.getInstance("TLS") sslContext.init(null, arrayOf<TrustManager>(dynamicTrustManager!!), SecureRandom()) LogUtils.d(TAG, "✅ SSL 上下文初始化完成") // 配置 SSL Socket Factory sslSocketFactory(sslContext.socketFactory, dynamicTrustManager!!) LogUtils.d(TAG, "✅ SSL Socket Factory 配置完成") LogUtils.d(TAG, "🎉 动态 SSL 配置应用成功!") this
} catch (e: Exception) { Log.e(TAG, "❌ 动态 SSL 配置失败", e) // 降级到不安全的配置(仅用于紧急情况) configureUnsafeSSL() } }
/**
配置不安全的 SSL (信任所有证书)
仅在紧急情况下使用 */ private fun OkHttpClient.Builder.configureUnsafeSSL(): OkHttpClient.Builder { Log.w(TAG, "Using unsafe SSL configuration - trusts all certificates") return try { val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager { override fun checkClientTrusted(chain: Array<out java.security.cert.X509Certificate>, authType: String) {} override fun checkServerTrusted(chain: Array<out java.security.cert.X509Certificate>, authType: String) {} override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> = arrayOf() })
val sslCOntext= SSLContext.getInstance("SSL") sslContext.init(null, trustAllCerts, SecureRandom()) sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager) hostnameVerifier { _, _ -> true } this
} catch (e: Exception) { Log.e(TAG, "Failed to configure unsafe SSL", e) this } }
/**
/**
/**
/**
/**
/**
/**
package com.wlld.common.network
import android.content.Context import android.util.Log import com.wlld.common.R import java.io.ByteArrayInputStream import java.io.InputStream import java.security.KeyStore import java.security.KeyStoreException import java.security.NoSuchAlgorithmException import java.security.PrivateKey import java.security.cert.Certificate import java.security.cert.CertificateException import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.util.Date import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager
/**
动态 HTTPS 证书校验工具类
当证书过期时跳过校验,确保应用正常运行 */ class DynamicSSLTrustManager( private val context: Context ) : X509TrustManager {
private val defaultTrustManager: X509TrustManager private val customCertificates: MutableList<X509Certificate> = mutableListOf() private var skipValidation = false
companion object { private const val TAG = "DynamicSSLTrustManager"
// 检查证书是否过期 fun isCertificateExpired(certificate: X509Certificate): Boolean { return try { val expiratiOnDate= certificate.notAfter expirationDate.before(Date()) } catch (e: Exception) { Log.e(TAG, "证书过期检查出错", e) true // 如果无法检查,默认认为过期 } } // 创建动态信任管理器 fun create(context: Context): DynamicSSLTrustManager { return DynamicSSLTrustManager(context) }
}
init { Log.d(TAG, "🚀 DynamicSSLTrustManager 初始化开始")
// 初始化默认信任管理器 defaultTrustManager = createDefaultTrustManager() Log.d(TAG, "✅ 默认信任管理器初始化完成") // 加载自定义证书 loadCustomCertificates() Log.d(TAG, "🎯 DynamicSSLTrustManager 初始化完成 - 跳过校验模式: $skipValidation, 自定义证书数量: ${customCertificates.size}")
}
private fun createDefaultTrustManager(): X509TrustManager { return try { val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) trustManagerFactory.init(null as KeyStore?) val trustManagers = trustManagerFactory.trustManagers trustManagers.firstOrNull { it is X509TrustManager } as? X509TrustManager ?: throw IllegalStateException("Default X509TrustManager not found") } catch (e: Exception) { Log.e(TAG, "Failed to create default trust manager", e) throw RuntimeException("Failed to initialize SSL trust manager", e) } }
private fun loadCustomCertificates() { try { // 加载 assets 中的证书文件 loadCertificateFromAssets("weilaiqiyuan.cer")
// 可以继续加载其他证书 // loadCertificateFromAssets("other_certificate.cer") Log.d(TAG, "已加载 ${customCertificates.size} 个自定义证书") // 如果没有找到任何证书,启用跳过校验模式 if (customCertificates.isEmpty()) { Log.w(TAG, "未找到任何自定义证书,启用跳过校验模式") skipValidation = true } } catch (e: Exception) { Log.e(TAG, "加载自定义证书失败,启用跳过校验模式", e) skipValidation = true }
}
private fun loadCertificateFromAssets(fileName: String) { try { context.assets.open(fileName).use { inputStream -> val certificate = loadCertificateFromStream(inputStream) if (certificate != null) { customCertificates.add(certificate) Log.d(TAG, "已加载证书: $fileName, 过期时间: ${certificate.notAfter}")
// 检查证书是否过期 if (isCertificateExpired(certificate)) { Log.w(TAG, "证书 $fileName 已过期,启用跳过校验模式") skipValidation = true } } } } catch (e: Exception) { Log.e(TAG, "从 assets 加载证书失败: $fileName", e) }
}
private fun loadCertificateFromStream(inputStream: InputStream): X509Certificate? { return try { val certificateFactory = CertificateFactory.getInstance("X.509") certificateFactory.generateCertificate(inputStream) as X509Certificate } catch (e: Exception) { Log.e(TAG, "解析证书失败", e) null } }
/**
动态添加证书 */ fun addCertificate(certificate: X509Certificate) { customCertificates.add(certificate) Log.d(TAG, "已添加新证书, 过期时间: ${certificate.notAfter}")
// 检查新添加的证书是否过期 if (isCertificateExpired(certificate)) { Log.w(TAG, "新添加的证书已过期,启用跳过校验模式") skipValidation = true } }
/**
/**
/**
/**
override fun checkClientTrusted(chain: Array<out X509Certificate>, authType: String) { // if (skipValidation) { // Log.w(TAG, "⚠️ 跳过客户端证书校验(证书过期或未找到)- 这可能导致抓包风险!") // return // } // // // 先检查自定义证书 // if (validateCustomCertificates(chain)) { // Log.d(TAG, "使用有效的自定义证书") // return // } // // // 使用默认校验 // try { // defaultTrustManager.checkClientTrusted(chain, authType) // } catch (e: CertificateException) { // Log.w(TAG, "默认客户端证书校验失败", e) // // // 检查是否因为过期导致的失败 // if (!checkCertificatesValidity(chain as Array<X509Certificate>)) { // Log.w(TAG, "客户端证书已过期,启用跳过校验模式") // skipValidation = true // return // } // // throw e // } }
override fun checkServerTrusted(chain: Array<out X509Certificate>, authType: String) { Log.d(TAG, "🔍 checkServerTrusted 被调用 - 验证服务器证书,authType: $authType") if (skipValidation) { Log.i(TAG, "跳过服务端证书校验(证书过期或未找到)") return }
// 先检查自定义证书 if (validateCustomCertificates(chain)) { return } // 使用默认校验 try { defaultTrustManager.checkServerTrusted(chain, authType) } catch (e: CertificateException) { Log.w(TAG, "默认服务端证书校验失败", e) // 检查是否因为过期导致的失败 if (!checkCertificatesValidity(chain as Array<X509Certificate>)) { Log.w(TAG, "服务端证书已过期,启用跳过校验模式") skipValidation = true return } throw e }
}
override fun getAcceptedIssuers(): Array<X509Certificate> { return if (skipValidation) { // 跳过校验时返回空数组,接受所有证书 emptyArray() } else { // 返回默认接受的证书颁发机构 defaultTrustManager.acceptedIssuers + customCertificates.toTypedArray() } }
/**
// // 如果自定义证书有效,优先使用 // for (customCert in customCertificates) { // if (!isCertificateExpired(customCert)) { // // 检查自定义证书是否覆盖 weilaiqiyuan.com 域名 // val subjectCN = extractCommonName(customCert.subjectDN.toString()) // if (subjectCN.contains("*.weilaiqiyuan.com") || subjectCN.contains("weilaiqiyuan.com")) { // Log.d(TAG, "使用有效的自定义证书验证 weilaiqiyuan.com 域名: $subjectCN") // return true // } // } // }
// 检查链中的证书是否与自定义证书匹配 for (cert in chain) { for (customCert in customCertificates) { if (cert.subjectDN == customCert.subjectDN) { // 找到匹配的自定义证书 if (!isCertificateExpired(customCert)) { // 🔍 严格验证证书:比较公钥指纹,防止证书伪造 if (verifyCertificateFingerprint(cert, customCert)) { Log.d(TAG, "✅ 证书验证通过(指纹匹配): ${cert.subjectDN}") return true } else { Log.w(TAG, "❌ 证书验证失败(指纹不匹配)- 可能是抓包攻击!") // 指纹不匹配,拒绝连接 throw CertificateException("证书指纹验证失败,可能存在中间人攻击") } } else { Log.w(TAG, "自定义证书已过期: ${cert.subjectDN}") return false } } } } return false } /** * 验证证书指纹,防止证书伪造 */ private fun verifyCertificateFingerprint(cert1: X509Certificate, cert2: X509Certificate): Boolean { return try { // 比较公钥的 SHA-256 指纹 val pubkey1 = cert1.publicKey.encoded val pubkey2 = cert2.publicKey.encoded val digest1 = java.security.MessageDigest.getInstance("SHA-256").digest(pubkey1) val digest2 = java.security.MessageDigest.getInstance("SHA-256").digest(pubkey2) val fingerprint1 = digest1.joinToString("") { "%02X".format(it) } val fingerprint2 = digest2.joinToString("") { "%02X".format(it) } val isValid = fingerprint1 == fingerprint2 Log.d(TAG, "证书指纹对比: $fingerprint1 vs $fingerprint2, 匹配: $isValid") isValid } catch (e: Exception) { Log.e(TAG, "证书指纹验证失败", e) false } } /** * 从 SubjectDN 中提取 Common Name */ private fun extractCommonName(subjectDN: String): String { val cnPattern = "CN=([^,]+)".toRegex() val match = cnPattern.find(subjectDN) return match?.groupValues?.get(1) ?: "" } /** * 获取自定义证书信息 */ fun getCertificateInfo(): List<String> { return customCertificates.map { cert -> "Subject: ${cert.subjectDN}, Issuer: ${cert.issuerDN}, Expires: ${cert.notAfter}, Expired: ${isCertificateExpired(cert)}" } }
}
]]>现在找到了国际版的 ROM ,zip 名字写着“售后专用”,应该是官方的包?但我不知道能不能回锁。回锁纯粹是我懒得跟 Google 斗智斗勇,暂时不考虑 root 安装模块,想要达到原生的 Widevine L1 。希望了解这方面的朋友指教🙇♂️
]]>为了避免耽误大家的时间,下面简单介绍下这款日志 App ,如果正好是你需要的,欢迎参与测试,再次强调,此为封闭式测试,无法通过公开链接获取 App ,必须要有 Google Play 账号(一般是 Gmail 邮箱)从 Google play 商城获取。
简单来说,它是一个强调文本记录的日志 App ,它的设计完全贴合我的个人使用习惯,算是比较有个性的一个产品。不同于传统的日志 App ,它主要有以下特性:
是的,一天一篇,最初的设定即如此,首页不再是列表,而是当天。基于这个设定,督促我把每天都点亮。即便哪天忘了,我也会努力回忆,补齐当天发生的事情。
除了常见的图文,日志中支持待办记录、事件(重要时刻)记录,未来计划支持每日消费支出、时间开销等记录形式。
对于数据这块,从一开始我便设计为本地储存,至于为什么这么设计,就无需多说了。同时,整个应用也是支持离线运行的。
数据也完全自主可控的,支持明文导出,不加密,不转码,自己的数据,自己做主,自己负责。
日志按每日拆分了,自然也能合并展示,每日待办可以统一页面管理,事件也可以按照日期或按照事件类别,进行展示及统计。
这个日志 App 的常规功能,不过多介绍了。
数据统计,也算是日志 App 的常规功能,不过多介绍。
再次说明:此次测试为封闭式测试,收到邀请的账号才能够下载 App ,所以,务必提供 Google Play 账号给我,我加到测试用户列表中,才会收到 App 的测试通知。
另外,该应用目前只测试了安卓 15 ,15 以下的系统版本,能够使用,但可能会有一些 UI 视觉上的问题,请见谅。
再次感谢参与测试的小伙伴,务必在评论区留下你的 Google 账号。
]]>这是综合考虑价格,手机性能和 ROM 丰富度的选择。
有其他推荐吗?
]]>看来这种强行附加 DNS 的做法正在扩散,以后说不定还要禁止 DoH 。
]]>谢谢
]]>谷歌是不是应该限制衍生了?生态也铺的差不多了。
]]>Claude ,codex ,cursor 这些 AI 辅助编辑器做 web 端开发调试确实蛮方便的,做安卓客户端原生开发,怎么利用比较方便?
同时开两边的编辑器,感觉不太方便,请教大佬做 Android,iOS 原生客户端怎么配置 AI 工具的?
]]>但是有一些场景下必须要用安卓手机,所以前几年买了一个特别便宜的红米手机来备用.
其中一个必须要用到安卓手机的场景用途就是
用 电子税务局 这个 app
不错 电子税务局 这个 app 也有苹果版
但有的业务提交的时候,苹果版的按钮就总是灰色,无法提交数据
我到税务局柜台去问,他们告诉我是这样的,换成安卓机试试就可以了
然后换成了我的红米,这个业务的提交按钮就能点击了
但是上个月发现他们这个电子税务局 app,又升级了
升级后办理比如租房电子发票这个业务,最后一步需要人脸识别提交
连我的红米手机都无法提交
APP 里面提示 版本太低
我把红米系统升级到最新还是不行
然后我又到税务局的柜台咨询,反映这个问题
他们说要我用他们柜台里面的手机试试看
这是一台华为的手机,然后人脸识别提交成功
我不知道这个电子税务局 app 提示的版本太低到底是什么意思
看来要换一台安卓机了
大家能推荐下,越便宜越好
对于华为我不是很了解, 鸿蒙是华为自己的系统,是不是说不能安装其他的安卓 APP 了
]]>手机是老妈用剩下的 360 N6Pro ,内置 360OS ,版本号 V105 ,其实就是安卓 7.1.1 ,因为刷机需要拆机短接进 9008 ,手头无拆机工具只好作罢(当然这就必须得想办法忍受/搞定广告了),幸而还支持 4G ,而且性能好像还行,单纯作为备用的备用机使用。
问题来了,安卓 7.1.1 似乎是太老了,装微信支付宝 QQ 之类的当然不再指望,但除此之外,想要装个 Bitwarden (包括第三方客户端 Keyguard )也装不上,adb 显示 SDK 版本过低,目前成功尝试装上去的只有 Firefox 142 和 Aegis (还能当离线 TOTP 机使用),别的暂未尝试
对于这种性能还行但安卓版本过低的手机,还能装些什么软件吗?
]]>GrapheneOS 昨天安全补丁更新后有使用中国广电的用户反馈 PixelIMS 崩溃, 无法正常启用 VoLTE. 事后发现是某个补丁封上了覆写运营商 VoLTE 支持情况的方法.
java.lang.SecurityException: overrideConfig cannot be invoked by shell
安全补丁细节由于 Google 新开源政策的关系还未公开, 是 GrapheneOS 通过合作渠道提前拿到且推送的. 在年底前 Google 推送给全体 Pixel 用户后, 极有可能 PixelIMS 这条路就走不通了.
GrapheneOS 开发者称已经准备在系统层面增加开关来让用户强开 VoLTE/VoNR. 但对于 Pixel 原厂系统用户来说, 这个限制本来就是 Google 有意加的(类似运营商白名单), 必不可能给你放开. 现在电信和广电都刚需 VoLTE, 之后怎么办只有天知道.
]]>Java.perform(function () { var Activity = Java.use("android.app.Activity"); Activity.onCreate.overload('android.os.Bundle').implementation = function (savedInstanceState) { ... ... return this.onCreate(savedInstanceState); }; });
]]>目前的手机是 ROOT 过的,安装证书没问题,但是考虑到以后终有一日要换手机,到时候估计没办法再进行 ROOT 处理,不知道最近几个版本的安卓,在不 ROOT 能不能继续手动添加 CA 根证书?(需要 adb 等辅助工具的也算可以)
谢谢
]]>操作方式: 任一文章,长按图片,点击 [ Save ] ,保存成功。
保存目录为: storage/emulated/0/DCIM/
]]>你们最近出现“飞牛 TV”启动开屏广告吗?不是每次点开都有,而是随机概率出现
之前就发现小米电视对“当贝市场”特殊照顾,加了开屏提示,已经非常恶心了
现在又搞在“飞牛 TV”前面,真他么恶心
]]>日常基本上只拿来办工看看消息,不玩游戏不拍照,会多安装一些软件,正常使用不卡就行。
希望价格便宜点,各位有什么推荐的?新机二手都可以。
]]>上网查了一下,小红书上面还有小米 13 淋雨就屏幕不亮的,比我还惨。
说是 IP68 ,拥有完全的防尘能力以及可在 1.5m 的水深下存留不超过 30 分钟。笑死,现在虚标都这么明目张胆了吗?
我再放几天通风看看能不能奇迹发生。不行就换手机了。枉费我支持了快 10 年的小米。
但是看了一圈好像也没什么选择,之前有人推我 oneplus 13t ,不知道实际怎么样,会不会也是坑。实在不行我就转投苹果了。
]]>我觉得国行小米手机对谷歌的出场支持还不错的,至少这台 K50 拿到手只需要在自带的应用商店里安装 Play Store ,之后再通过 Play Store 更新谷歌服务,基本上就能用了。没遇到过什么兼容性问题,Google Wallet 也能正常用 Contactless 。谈起小米的 NFC 又勾起了我高中时期偷偷模拟宿管大爷和班主任的门禁卡并把它分享给所有人的回忆。
手机对我来说能解决简单的上网需求就可以了,不过最近越来越担心被国产手机出卖,虽然我也不参与任何政治讨论,但 HyperOS 收紧 BL 解锁让我更加感到不安。为什么不用苹果?没钱去买那些我可能用不上的功能,加上从来也没有接触过苹果的生态。以后换手机的话大概是一部海外版的安卓。
自己没弄明白,咸鱼花了 139 找人远程帮忙弄的,也不了解具体细节,说是通过 MTK 授权来刷。自己尝试过这个工具但是失败了( error 10000 ),说是可以安装旧版设置来解决,但我也没法安装他给的旧版设置。如果是 HyperOS 1 的话应该能成功。
我不知道该做什么。不升级接着用 MIUI ?解锁 BL 之后升级到 HyperOS 再观察观察?刷 EU 版 ROM ?目前想到的这三个选项虽然都挺安全,不过考虑到我有使用银行 app 的需求( HSBC UK ),后两个都要求我有另一部手机作为备用,而我没有。可如果新买手机的话,我可能选 Pixel 系列的,那我又何必折腾呢。
由于我一直是开启自动更新的,一般就是他给我推我就更了。刚从 MIUI 换到 HyperOS 的时候我特别恼火,手机启动速度巨慢无比。经历好长一段时间后,某次更新终于恢复到了原来的速度。广告的话我没有什么感受,自带的系统应用也就只有一个应用商店是有广告并且我需要的。我也不开启负屏或者桌面搜索啥的,很少用到。通知栏里的广告也就是看到了就顺藤摸瓜给他删掉或者禁用掉,后来也从来没看到过了。HyperOS 给我的唯一感受是一开始的开机时长。
]]>已尝试过调低敏感度但会导致侧滑困难
突然明白水果为何不搞侧滑和缩减敏感度😂
大佬们有什么好的策略应对吗?
]]>