Flutter TlsException:无法信任内置根
Flutter TlsException: Failure trusting builtin roots
我正在尝试使用 HttpClient
在 Flutter 应用程序中执行 SSL 证书固定。我之前已经成功地在本机 Android 应用程序中执行了固定。这是我收到的错误消息:
E/flutter (28810): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Exception: TlsException: Failure trusting builtin roots (OS Error:
E/flutter (28810): BAD_PKCS12_DATA(pkcs8_x509.c:645), errno = 0)
E/flutter (28810): #0 _SecurityContext.setTrustedCertificatesBytes (dart:io-patch/secure_socket_patch.dart:233:59)
E/flutter (28810): #1 ApiProvider._clientWithPinnedCertificate (package:app/api/api.provider.dart:68:13)
E/flutter (28810): #2 ApiProvider.makePostRequest (package:app/api/api.provider.dart:45:11)
E/flutter (28810): #3 _fireAuthenticationRequestIsolate (package:app/api/repositories/auth.repository.dart:14:41)
E/flutter (28810): #4 _IsolateConfiguration.apply (package:flutter/src/foundation/_isolates_io.dart:84:34)
E/flutter (28810): #5 _spawn.<anonymous closure> (package:flutter/src/foundation/_isolates_io.dart:91:65)
E/flutter (28810): #6 _spawn.<anonymous closure> (package:flutter/src/foundation/_isolates_io.dart:90:5)
E/flutter (28810): #7 Timeline.timeSync (dart:developer/timeline.dart:163:22)
E/flutter (28810): #8 _spawn (package:flutter/src/foundation/_isolates_io.dart:88:35)
E/flutter (28810): #9 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:286:17)
E/flutter (28810): #10 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
下载 .cer
文件后,我使用以下命令将其转换为 .pem
:
openssl x509 -inform pem -in test.ca -outform der -out test.pem
我从 assets
文件夹加载证书,如下所示:
const certificatePath = 'assets/test.pem';
final certificateBytes = await rootBundle.load(certificatePath);
final certificate = certificateBytes.buffer.asUint8List();
这是触发 HTTP POST 请求的 API 提供商的代码
class ApiProvider {
final String _baseURL;
ApiProvider(String baseURL) : _baseURL = baseURL;
Future<String> makePostRequest(
String endpoint,
Map<String, dynamic> body, {
Map<String, String>? headers,
bool setTrustedCertificate = false,
}) async {
final client = setTrustedCertificate
? _clientWithPinnedCertificate()
: HttpClient();
final url = Uri.https(_baseURL, endpoint);
final response = await client.postUrl(url)
..headers.addAll(headers ?? <String, String>{})
..write(json.encode(body));
final request = await response.close();
if (request.statusCode < 200 || request.statusCode >= 300) {
throw ApiException(
request.statusCode,
request.reasonPhrase,
);
}
final responseBody = await request
.transform(const Utf8Decoder(allowMalformed: true))
.reduce((previous, element) => previous + element);
return responseBody;
}
HttpClient _clientWithPinnedCertificate() {
final context = SecurityContext();
context.setTrustedCertificatesBytes(
GlobalConstants.certificate,
password: certificatePassword,
);
final client = HttpClient(context: context);
client.badCertificateCallback = (
X509Certificate certificate,
String host,
int port,
) {
print('Bad certificate: ${certificate.sha1} for host $host:$port');
return false;
};
return client;
}
}
经过长时间尝试各种配置后,我设法找到了解决方案,而且比我想象的要深入一些。
首先,确保您没有在单独的隔离区调用 rootBundle.load()
。我一直在这样做,这引发了模棱两可的错误。
其次,如果您有 .p12
文件,则不需要 .ca
(或转换为 .pem
)。您所要做的就是使用以下代码片段
// Here, certificate is a Uint8List
final context = SecurityContext.defaultContext
..useCertificateChainBytes(
certificate,
password: /* input certificate passphrase */
),
..usePrivateKeyBytes(
certificate,
password: /* input certificate passphrase */
);
final client = HttpClient(context: context);
client.badCertificateCallback = (
X509Certificate cert,
String host,
int port,
) {
// Handle certificate that can't be authenticated
// Returning 'true' by itself is not really safe...
return true;
};
确保在这两种情况下都提供密码。
我正在尝试使用 HttpClient
在 Flutter 应用程序中执行 SSL 证书固定。我之前已经成功地在本机 Android 应用程序中执行了固定。这是我收到的错误消息:
E/flutter (28810): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Exception: TlsException: Failure trusting builtin roots (OS Error:
E/flutter (28810): BAD_PKCS12_DATA(pkcs8_x509.c:645), errno = 0)
E/flutter (28810): #0 _SecurityContext.setTrustedCertificatesBytes (dart:io-patch/secure_socket_patch.dart:233:59)
E/flutter (28810): #1 ApiProvider._clientWithPinnedCertificate (package:app/api/api.provider.dart:68:13)
E/flutter (28810): #2 ApiProvider.makePostRequest (package:app/api/api.provider.dart:45:11)
E/flutter (28810): #3 _fireAuthenticationRequestIsolate (package:app/api/repositories/auth.repository.dart:14:41)
E/flutter (28810): #4 _IsolateConfiguration.apply (package:flutter/src/foundation/_isolates_io.dart:84:34)
E/flutter (28810): #5 _spawn.<anonymous closure> (package:flutter/src/foundation/_isolates_io.dart:91:65)
E/flutter (28810): #6 _spawn.<anonymous closure> (package:flutter/src/foundation/_isolates_io.dart:90:5)
E/flutter (28810): #7 Timeline.timeSync (dart:developer/timeline.dart:163:22)
E/flutter (28810): #8 _spawn (package:flutter/src/foundation/_isolates_io.dart:88:35)
E/flutter (28810): #9 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:286:17)
E/flutter (28810): #10 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
下载 .cer
文件后,我使用以下命令将其转换为 .pem
:
openssl x509 -inform pem -in test.ca -outform der -out test.pem
我从 assets
文件夹加载证书,如下所示:
const certificatePath = 'assets/test.pem';
final certificateBytes = await rootBundle.load(certificatePath);
final certificate = certificateBytes.buffer.asUint8List();
这是触发 HTTP POST 请求的 API 提供商的代码
class ApiProvider {
final String _baseURL;
ApiProvider(String baseURL) : _baseURL = baseURL;
Future<String> makePostRequest(
String endpoint,
Map<String, dynamic> body, {
Map<String, String>? headers,
bool setTrustedCertificate = false,
}) async {
final client = setTrustedCertificate
? _clientWithPinnedCertificate()
: HttpClient();
final url = Uri.https(_baseURL, endpoint);
final response = await client.postUrl(url)
..headers.addAll(headers ?? <String, String>{})
..write(json.encode(body));
final request = await response.close();
if (request.statusCode < 200 || request.statusCode >= 300) {
throw ApiException(
request.statusCode,
request.reasonPhrase,
);
}
final responseBody = await request
.transform(const Utf8Decoder(allowMalformed: true))
.reduce((previous, element) => previous + element);
return responseBody;
}
HttpClient _clientWithPinnedCertificate() {
final context = SecurityContext();
context.setTrustedCertificatesBytes(
GlobalConstants.certificate,
password: certificatePassword,
);
final client = HttpClient(context: context);
client.badCertificateCallback = (
X509Certificate certificate,
String host,
int port,
) {
print('Bad certificate: ${certificate.sha1} for host $host:$port');
return false;
};
return client;
}
}
经过长时间尝试各种配置后,我设法找到了解决方案,而且比我想象的要深入一些。
首先,确保您没有在单独的隔离区调用 rootBundle.load()
。我一直在这样做,这引发了模棱两可的错误。
其次,如果您有 .p12
文件,则不需要 .ca
(或转换为 .pem
)。您所要做的就是使用以下代码片段
// Here, certificate is a Uint8List
final context = SecurityContext.defaultContext
..useCertificateChainBytes(
certificate,
password: /* input certificate passphrase */
),
..usePrivateKeyBytes(
certificate,
password: /* input certificate passphrase */
);
final client = HttpClient(context: context);
client.badCertificateCallback = (
X509Certificate cert,
String host,
int port,
) {
// Handle certificate that can't be authenticated
// Returning 'true' by itself is not really safe...
return true;
};
确保在这两种情况下都提供密码。