配置 https 证书,如果证书过期,希望不影响访问,这个怎么做。

9 小时 43 分钟前
 zhixiz1007

帮公司的安卓小哥来提个问题:之前项目是没有配置证书的,可以被抓包,现在为了防止抓包,就需要配置 https 证书。首先静态配置证书是属于写到 network_security_config_app.xml 这个证书,直接配置到网络认证文件里,这个种情况有个不好就是如果哪天证书过期了,那就无法通过网络认证了,但是不希望如此,希望如果过期了那就忽略认证,所以需要动态配置 ssl 不直接配置到文件中,通过代码去认证,先去判断证书有效,如果无效或者不能找到证书直接走信任证书,如果有效那就走证书认证,目前没配置的表现就是可以被抓包。请问大家,有过这方面的经验吗? GPT 代码帮忙解决了,但是因为是知识盲区想请教一下诸位。 package com.wlld.common.network

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

/**

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

/**

// // 如果自定义证书有效,优先使用 // 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)}"
    }
}

}

1453 次点击
所在节点    Android
14 条回复
samIIsun
9 小时 38 分钟前
看得出目的主要是:"为了防止抓包"
buuuut ,用 https 也可以中间人抓包欸。。。我抓安卓的 http 包,不 care 服务端有没有 https 的。。。
unused
9 小时 36 分钟前
自签
gesse
9 小时 28 分钟前
https/tls 库都有 insecure 忽略证书有效性、不验证证书日期有效性等选项吧。
Bananana
9 小时 23 分钟前
客户端验证公钥?这样证书过期了,只要重签时候公钥不变就不会报错
sujin190
9 小时 20 分钟前
不需要这么费劲吧,常用的库比如 okhttp 都支持设置自定义证书验证回调吧,在回调里处理下直接忽略证书过期然后改成直接验证服务器证书指纹就好了啊,也是安全的

import okhttp3.*;
import javax.net.ssl.*;
import java.security.cert.X509Certificate;

public class CustomVerifyOkHttp {

public static OkHttpClient buildClient() throws Exception {
TrustManager[] trustManagers = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
// 不处理客户端
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
// 示例:只信任特定公钥指纹
X509Certificate cert = chain[0];
String sha256 = sha256(cert.getPublicKey().getEncoded());
if (!"your_expected_sha256_fingerprint".equalsIgnoreCase(sha256)) {
throw new CertificateException("Untrusted server certificate");
}
}

@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, new java.security.SecureRandom());

return new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0])
.hostnameVerifier((hostname, session) -> true) // 可选:略过主机名验证
.build();
}

private static String sha256(byte[] data) throws Exception {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(data);
StringBuilder sb = new StringBuilder();
for (byte b : digest) sb.append(String.format("%02x", b));
return sb.toString();
}
}


基本就是这样,这样之后楼上说的什么中间人抓包也没可能了
miyuki
9 小时 18 分钟前
请求库一般可以设置忽略“不安全”证书

提高抓包难度还能验证服务端证书,防止被中间人( ssl pinning )
iyaozhen
8 小时 41 分钟前
Android 很好解决吧。现在非 root 几乎不能抓包了
你可以设置不信任用户导入的证书,只信任系统的。大概是这个意思。

我是 QA ,每次要抓自己 app 的包,都需要研发单独打 dev 的开发包,去掉这个限制
Esec
7 小时 6 分钟前
然后就开始头疼证书注入了..
busier
5 小时 39 分钟前
1 、服务器不要求双向证书验证

2 、客户端忽略证书错误
czyt
3 小时 59 分钟前
caddy 貌似自动申请的
mmdsun
3 小时 56 分钟前
自签一个 9999 年到期的证书。
edsion1107
2 小时 32 分钟前
留个后门,把 https 降级到 http 。
只要是 https 都走你自己的证书信任链,过期、第三方证书都按非法证书处理。

感觉你的思路是在把问题复杂化,而且好像也有点违背 https 设计的初衷。
imlonghao
2 小时 30 分钟前
及时更新证书别让他过期
clarkethan
31 分钟前
我们是通过限制签发 CA 来做的,不允许未知 CA 签发的证书,因为会通过固定的 CA 签发证书,所以这个策略比较有效

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/1164208

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX