I need to use self signed https certificate with flutter app. Unfortunately I can't solve a problem. What ever I try, I always get this error "[VERBOSE-2:ui_dart_state.cc(199)] Unhandled Exception: HandshakeException: Handshake error in client (OS Error: CERTIFICATE_VERIFY_FAILED: application verification failure(handshake.cc:359))"
I tried self signed certificate, tried with self signed with CA - no success. Tried to downgrade server version to tls1.2 (I found some old issues about it) - does not help. This is the code:
ByteData bytes = await rootBundle.load('ssl/certificate2019.pem');
SecurityContext clientContext = SecurityContext();
clientContext.setTrustedCertificatesBytes((bytes.buffer.asInt8List()));
var httpClient = new HttpClient(context: clientContext);
httpClient.badCertificateCallback = (X509Certificate cert, String host, int port){
print("!!!!Bad certificate host $host $port");
return false;
};
var uri = Uri.parse('https://localhost:443');
final result = await httpClient.getUrl(uri).timeout(Duration(milliseconds: 3000));
result.close();
It seems that setTrustedCertificatesBytes totaly do not make any difference with empty context, the error is same application verification failure(handshake.cc:359)
The last thing I tried is the examples from here https://github.com/dart-lang/sdk/issues/35981#issuecomment-536134040 from @M.Leonhard i suppose, which exclude mobile(flutter),because it is dart and exclude my server (server inside the example script) script but the result is the same. This is result of the dart script from example from the link above
The example source code:
import 'dart:io' show BytesBuilder, File, HttpClient, HttpClientRequest, HttpClientResponse, HttpHeaders, HttpRequest, HttpServer, InternetAddress, Process, SecurityContext, stderr, stdout;
import 'dart:convert' show utf8;
Future<void> shellCommand(String command) async {
print('Executing command $command');
final Process process = await Process.start('sh', ['-c', command]);
stdout.addStream(process.stdout);
stderr.addStream(process.stderr);
final int exitCode = await process.exitCode;
if (exitCode != 0) {
throw new Exception('Process exited with status $exitCode');
}
}
void main() async {
// Last year's certificates:
await shellCommand('openssl req -newkey rsa:2048 -nodes -keyout ca2018.privatekey.pem -subj "/OU=CA" -days 731 -x509 -out ca2018.certificate.pem');
// This year's certificates:
await shellCommand('openssl req -newkey rsa:2048 -nodes -keyout ca2019.privatekey.pem -subj "/OU=CA" -days 731 -x509 -out ca2019.certificate.pem');
await shellCommand('openssl req -newkey rsa:2048 -passout pass:password -keyout privatekey.pem -subj "/CN=localhost" -days 731 -sha256 -new -out csr2019.pem');
await shellCommand('openssl x509 -req -in csr2019.pem -CA ca2019.certificate.pem -CAkey ca2019.privatekey.pem -set_serial 1 -days 730 -sha256 -out certificate2019.pem');
await shellCommand('cat certificate2019.pem ca2019.certificate.pem > certificate2019.chain.pem');
final SecurityContext serverSecurityContext = new SecurityContext();
serverSecurityContext.useCertificateChainBytes(await new File('certificate2019.chain.pem').readAsBytes());
serverSecurityContext.usePrivateKey('privatekey.pem', password: 'password');
final HttpServer httpServer = await HttpServer.bindSecure(InternetAddress.loopbackIPv4, 0, serverSecurityContext);
httpServer.listen((HttpRequest request) {
request.response.write('body1');
request.response.close();
});
print('Server listening at https://localhost:${httpServer.port}/');
print('Making request.');
final SecurityContext clientSecurityContext = new SecurityContext(withTrustedRoots: false);
clientSecurityContext.setTrustedCertificatesBytes(await new File('ca2018.certificate.pem').readAsBytes());
clientSecurityContext.setTrustedCertificatesBytes(await new File('ca2019.certificate.pem').readAsBytes());
final HttpClient httpClient = new HttpClient(context: clientSecurityContext);
final HttpClientRequest request = await httpClient.getUrl(Uri(scheme: 'https', host: 'localhost', port: httpServer.port, path: '/'));
final HttpClientResponse response = await request.close();
final List<int> bytes = await response.fold(new BytesBuilder(), (BytesBuilder bytesBuilder, List<int> bytes) {
bytesBuilder.add(bytes);
return bytesBuilder;
}).then((BytesBuilder bytesBuilder) => bytesBuilder.takeBytes());
final String contenType = response.headers.value(HttpHeaders.contentTypeHeader) ?? '';
print('${response.statusCode} ${response.reasonPhrase} '
'content-type="$contenType" body="${utf8.decode(bytes)}"');
httpServer.close(force: true);
}
I can successfully do requests over https with postman with added .pem file to postman.
Also I can successfully do a request with curl
I cant figure out what I made wrong... Why setTrustedCertificatesBytes does not affect? I also tried with facebook certificate (download it and convert into pem) and SecurityContext() which by default exclude all systems certificates, (withTrustedRoots is false by default) and it works well. It seems that it checks certificate inside, and can verify, but cant verify self signed certificates! But I cant understand why this examples (like above) works? I suppose they should work if it is publushed .. so I do somthing wrong but can't understand what exactly.. Will wait any help! Thanks for readind till the end)
UPDATE ----- I tried do all steps again. openssl commands I used
- Root Private Key
openssl genrsa -out rootCA.key 2048
- Root CA certificate
openssl req -x509 -new -key rootCA.key -sha256 -days 365 -out rootCA.pem
- Server Private key
openssl genrsa -out server.key 2048
- Sign request
openssl req -new -key server.key -out server.csr
- Sign server Cert with CA
openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -days 365 -sha256 -out server.pem
- Concatenate into chain
cat server.pem rootCA.pem > chain.pem
I also entered company, in some places it is mentioned as important. Dart server code (in same directory with certificates)
void main() async {
final SecurityContext serverSecurityContext = new SecurityContext();
serverSecurityContext.useCertificateChainBytes(await new File('chain.pem').readAsBytes());
serverSecurityContext.usePrivateKey('server.key');
final HttpServer httpServer = await HttpServer.bindSecure(InternetAddress.loopbackIPv4, 0, serverSecurityContext);
httpServer.listen((HttpRequest request) {
request.response.write('body1');
request.response.close();
});
print('Server listening at https://localhost:${httpServer.port}/');
}
Request script code (in the same directory)
void main() async {
print('Making request.');
final SecurityContext clientSecurityContext = new SecurityContext();
clientSecurityContext.setTrustedCertificatesBytes(await new File('rootCA.pem').readAsBytes());
final HttpClient httpClient = new HttpClient(context: clientSecurityContext);
final HttpClientRequest request = await httpClient.getUrl(Uri(scheme: 'https', host: 'localhost', port: 443, path: '/'));
final HttpClientResponse response = await request.close();
final List<int> bytes = await response.fold(new BytesBuilder(), (BytesBuilder bytesBuilder, List<int> bytes) {
bytesBuilder.add(bytes);
return bytesBuilder;
}).then((BytesBuilder bytesBuilder) => bytesBuilder.takeBytes());
final String contenType = response.headers.value(HttpHeaders.contentTypeHeader) ?? '';
print('${response.statusCode} ${response.reasonPhrase} '
'content-type="$contenType" body="${utf8.decode(bytes)}"');
}
Result of openssl s_client -connect localhost:55132 -CAfile rootCA.pem
Result of request from Dart script
I got the same error(
I also tried another server not Dart - the same error on client.
I still sycsefully can connect with postman with rootCA.pem.
Maybe I do somthing wrong with certificates whan generate..
And I cant find this concrete error at the internet
I check time it is correct..
Maybe any suggestions what else I can do?