You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter请求自签名证书服务报CERTIFICATE_VERIFY_FAILED错误如何解决

排查与解决思路

1. 校验自签名证书本身的扩展字段合法性

报错原因为invalid or inconsistent certificate extension,说明TLS握手阶段在证书信任校验之前就因为证书扩展字段错误终止流程,因此不会触发badCertificateCallback回调:

  • 执行openssl命令检查证书内容:openssl x509 -in 证书文件路径.pem -text -noout,重点核查X509v3扩展部分是否存在字段重复、值非法、配置逻辑冲突的问题。
  • 确认服务端证书是否添加了subjectAltName扩展,且扩展值匹配请求使用的域名/IP,高版本Dart的TLS栈对缺少该扩展的证书会直接终止握手,不会进入证书信任校验环节。
  • 如果是多级自签名证书,确认根CA证书的basicConstraints扩展配置为CA:TRUE,否则会被判定为非法CA证书直接拦截。

2. 确认全局配置的生效时机与范围

  • 确保HttpOverrides.global = 自定义HttpOverrides实例的代码在所有网络请求初始化之前执行,建议放在main()函数最顶部,早于runApp()调用,避免部分网络库提前初始化HttpClient实例绕过重写逻辑。
  • 若使用dio 5.0及以上版本,默认IO适配器不再依赖全局HttpClient,全局HttpOverrides对其不生效,需要单独配置适配器:
    import 'package:dio/io.dart';
    
    // 配置badCertificate回调
    (dio.httpClientAdapter as IOHttpClientAdapter).onBadCertificate = (certificate) {
      // 开发阶段可直接返回true放行,生产环境建议校验证书指纹
      return true;
    };
    
    // 或显式配置信任指定证书
    (dio.httpClientAdapter as IOHttpClientAdapter).createHttpClient = () {
      final securityContext = SecurityContext();
      securityContext.addTrustedCertificateBytes(自签名证书的字节数组);
      return HttpClient(context: securityContext);
    };
    

3. 排查网络库配置逻辑

  • 如果使用http库,若业务代码中显式创建了HttpClient实例传入http库,需要单独为该实例配置badCertificateCallback,全局重写不会作用于手动创建的实例。
  • 排查项目中是否存在第三方网络封装、埋点、统计类库,此类库内部可能独立初始化HttpClient实例,未继承项目全局配置,导致请求被拦截。

4. 临时调试方案

开发阶段可通过以下命令生成符合Dart TLS栈要求的标准自签名证书,替换现有证书后重试:

openssl req -x509 -newkey rsa:4096 -keyout server_key.pem -out server_cert.pem \
-days 365 -nodes \
-subj '/CN=你的服务端域名或IP' \
-addext 'subjectAltName=IP:192.168.1.100,DNS:dev.yourdomain.com' \
-addext 'basicConstraints=CA:FALSE' \
-addext 'keyUsage=digitalSignature,keyEncipherment' \
-addext 'extendedKeyUsage=serverAuth'

替换服务端证书后即可正常触发证书校验回调,或直接将生成的证书添加到客户端信任列表完成请求。


内容的提问来源于stack exchange,提问作者Chris Hibler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 03:36:03