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

使用个人证书对接SOAP服务器遇SSL握手失败及连接重置问题求助

问题分析与修复方案

首先明确:SEFAZ的NFe服务仅支持HTTPS,Connection reset by peer是因为服务器直接拒绝HTTP请求,无需再尝试HTTP方案,专注解决HTTPS握手失败问题即可。

核心问题定位

SSLV3_ALERT_HANDSHAKE_FAILURE错误的常见诱因:

  • 客户端未正确向服务器提供要求的个人PFX证书
  • 证书加载方式错误,导致服务器无法验证客户端身份
  • TLS版本不兼容(SEFAZ服务通常强制要求TLS 1.2及以上)
  • 代码中混用多个SecurityContext,导致证书配置冲突

修复步骤

1. 统一证书加载逻辑,避免配置混乱

你的代码在main函数和请求代码中分别处理证书,导致配置冲突。修改为在请求时一次性正确加载PFX证书:

// 在请求方法内加载证书(可全局缓存避免重复加载)
ByteData pfxData = await rootBundle.load('assets/cert/imperiocert.pfx');
Uint8List pfxBytes = pfxData.buffer.asUint8List();

2. 正确使用PFX证书完成客户端认证

PFX文件包含完整的证书链和私钥,推荐使用setClientIdentityBytes方法直接加载,比分开设置证书链和私钥更可靠:

final context = SecurityContext(withTrustedRoots: true)
  // 加载客户端PFX证书,用于服务器验证客户端身份
  ..setClientIdentityBytes(pfxBytes, password: 'mypassword');

3. 移除不安全的badCertificateCallback(仅调试时可临时保留)

你自定义的HttpOverrides中强制返回true会忽略所有证书验证错误,可能破坏正常的握手逻辑。SEFAZ官方证书均为可信CA签发,生产环境必须删除该回调:

// 注释或删除这行代码
// HttpOverrides.global = MyHttpOverrides();

4. 强制使用兼容的TLS版本

SEFAZ服务通常要求TLS 1.2,可在创建HttpClient时指定:

final httpClient = HttpClient(context: context)
  ..setTLSProtocols(['TLSv1.2'], [])
  ..connectionTimeout = const Duration(seconds: 30);

5. 检查SOAP请求头是否合规

确保请求头包含正确的Content-Type和SOAPAction(部分SEFAZ服务强制要求):

Map<String, String> headers = {
  'Content-Type': 'text/xml; charset=utf-8',
  'SOAPAction': 'http://www.portalfiscal.inf.br/nfe/wsdl/NFeStatusServico4/nfeStatusServicoNF',
};

修改后的完整请求代码示例

ElevatedButton(
  onPressed: () async {
    try {
      // 加载PFX证书
      ByteData pfxData = await rootBundle.load('assets/cert/imperiocert.pfx');
      Uint8List pfxBytes = pfxData.buffer.asUint8List();

      // 构建请求参数
      final requestUrl = Uri.parse('https://homolog.sefaz.go.gov.br/nfe/services/NFeStatusServico4');
      final headers = {
        'Content-Type': 'text/xml; charset=utf-8',
        'SOAPAction': 'http://www.portalfiscal.inf.br/nfe/wsdl/NFeStatusServico4/nfeStatusServicoNF',
      };
      final xmlBody = arquivoxml.toXmlString();

      // 创建配置好的SecurityContext
      final context = SecurityContext(withTrustedRoots: true)
        ..setClientIdentityBytes(pfxBytes, password: 'mypassword');

      // 初始化HttpClient
      final httpClient = HttpClient(context: context)
        ..setTLSProtocols(['TLSv1.2'], [])
        ..connectionTimeout = const Duration(seconds: 30);

      final ioClient = ioh.IOClient(httpClient);

      // 发起请求
      final response = await ioClient.post(
        requestUrl,
        headers: headers,
        body: xmlBody,
      );

      print('响应状态码: ${response.statusCode}');
      print('响应内容: ${response.body}');
      setState(() {
        retonochamada = response.body;
      });
    } catch (e) {
      setState(() {
        retonochamada = e.toString();
      });
      print('请求错误: $e');
    }
  },
  child: const Text('调用服务'),
)

额外排查点

  • 验证PFX文件完整性:使用OpenSSL命令openssl pkcs12 -info -in imperiocert.pfx确认证书可正常解析
  • 再次确认设备系统时间精确到分钟(时间偏差可能导致证书验证失败)
  • 测试网络连通性:用浏览器访问https://homolog.sefaz.go.gov.br/nfe/services/NFeStatusServico4确认可正常打开服务页面

内容的提问来源于stack exchange,提问作者Julio Cesar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:12:55