Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setTrustedCertificates doesn't work. Causes HandshakeException all the time #35462

Closed
bdshadow opened this issue Dec 20, 2018 · 17 comments
Closed
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-io

Comments

@bdshadow
Copy link

  • Dart VM version: 2.1.0 (Tue Nov 13 18:22:02 2018 +0100) on "linux_x64"
  • Fedora 28

https://gist.github.com/bdshadow/ad64d6f3660422e8ff06572f4bf6e7fd

import 'dart:convert';
import 'dart:io';

void main() {
  var url = "https://192.168.39.219:8443/version";
  SecurityContext context = SecurityContext();
  context.setTrustedCertificates("/home/dbocharo/.minikube/ca.crt");
  context.usePrivateKey("/home/dbocharo/.minikube/ca.key");
  HttpClient client = new HttpClient(context: context);
  client.getUrl(Uri.parse(url))
      .then((HttpClientRequest request) => request.close())
      .then((HttpClientResponse response) {
    response.transform(utf8.decoder).listen((contents) {
      print(contents);
    });
  });
}

As you can see i try to connect to kubernetes run by minikube. But i always have "CERTIFICATE_VERIFY_FAILED: Hostname mismatch" error. The certificates are correct because:

  • "curl --cert ~/.minikube/ca.crt --key ~/.minikube/ca.key https://192.168.39.219:8443/version" runs fine from the terminal
  • When adding this "ca.crt" to android i'm able to do the similar stuff from android using java without getting handshake errors.

I also tried experimenting with apiserver.crt, client.crt and their keys (though it doesn't make much sense because of the above items). I found that people have the same problem https://stackoverflow.com/questions/46135757/ssl-handshake-error-on-self-signed-cert-in-flutter?rq=1 and there is no a proper reply. I also tried using IOClient from package:http/io_client.dart - the error is the same. I googled everything i could imagine :) Also i found here #27246 that @whesse is your ssl expert :) It would be great if William or anybody else could help me! Thank you in advance!

@whesse
Copy link
Contributor

whesse commented Dec 20, 2018

Some problems I ran across when including a certificate chain on the server were the order of the certificates, and whether the chain included the root.
This is using trusted certificates and a key on the client side, so I am not sure what the key is being used for. If you are trying to use a client certificate, then I think you also need to set the certificate chain to be sent with the client certificate, with useCertificateChainBytes. This is what the example in the client certificate test does.

The test tests/standalone_2/io/https_client_certificate_test is currently failing, so we need to go back and find out when that test started failing, and maybe prepare a stronger version that has separate server and client certificates.
@zanderso

@bdshadow
Copy link
Author

bdshadow commented Dec 20, 2018

@whesse thank you very much for your reply.
I don't think that i need to set the certificate chain. I don't think so, because of the reason i described:

  • curl --cert ~/.minikube/ca.crt --key ~/.minikube/ca.key https://192.168.39.219:8443/version works fine. Nothing else is needed. The key and certificate are fine.
  • and these certificate is ok for them same app in android (java).

So it must be no difference with dart. At least i don't see a reason why would dart client need more than other clients

@whesse
Copy link
Contributor

whesse commented Dec 20, 2018

The documentation for rawSocketConnect, at the last change 3 years ago, was changed to say

    • If a certificate and key are set on the client, using useCertificateChain
    • and usePrivateKey, and the server asks for a client certificate,
    • then that client certificate is sent to the server.

Our internal implementation may be deciding based on whether certificate chain is set or not, whether to send the client certificate. Also, the client certificate doesn't need to be set as trusted by your side at all, it just needs to be set as the certificate to send. So I think that setTrustedCertificates is not needed at all, and setCertificiateChain is what you want. I assume that ca.crt is a client certificate, that the file includes a chain of certificates, and that ca.key is the key to the client certificate.

Dart doesn't need more than the other clients, it just needs to be told what they are - a client certificate chain, not a root cert to be trusted when connecting. The documentation for curl says that --cert is the Client certificate file and password. The dart equivalent is setCertificateChain. The curl equivalent of setTrustedCertificates is the --cacert option.

If this isn't a client certificate, and just the root cert for the server you are connecting to, then you would not need to include the private key, just call the trust function. But I am guessing this is a client certificate. We programmed the Dart client, to call all the low level BoringSSL library functions, so it will only do what we programmed it to do.

 

@bdshadow
Copy link
Author

@whesse i tried

context.useCertificateChain("/home/dbocharo/.minikube/ca.crt");
context.usePrivateKey("/home/dbocharo/.minikube/ca.key");

and it led to a little bit different HandshakeException: CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate instead of Hostname mismatch i had before.

The problem is that i'm little confused with minikube certificates and can't find proper documentation. I genereates a bunch of them:

[dbocharo@dbocharo .minikube]$ ls -l
-rw-r--r--. 1 dbocharo dbocharo 1298 Dec 16 02:44 apiserver.crt
-rw-------. 1 dbocharo dbocharo 1675 Dec 16 02:44 apiserver.key
-rw-rw-r--. 1 dbocharo dbocharo 1298 Dec 17 00:46 apiserver.pem
-rw-r--r--. 1 dbocharo dbocharo 1066 Dec 16 02:44 ca.crt
-rw-------. 1 dbocharo dbocharo 1679 Dec 16 02:44 ca.key
-r----x--x. 1 dbocharo dbocharo 1042 Dec 16 02:43 ca.pem
-r----x--x. 1 dbocharo dbocharo 1082 Dec 16 02:43 cert.pem
-rw-r--r--. 1 dbocharo dbocharo 1103 Dec 16 02:44 client.crt
-rw-------. 1 dbocharo dbocharo 1679 Dec 16 02:44 client.key
-r----x--x. 1 dbocharo dbocharo 1679 Dec 16 02:43 key.pem
-rw-r--r--. 1 dbocharo dbocharo 1074 Dec 16 02:44 proxy-client-ca.crt
-rw-------. 1 dbocharo dbocharo 1679 Dec 16 02:44 proxy-client-ca.key
-rw-r--r--. 1 dbocharo dbocharo 1103 Dec 16 02:44 proxy-client.crt
-rw-------. 1 dbocharo dbocharo 1679 Dec 16 02:44 proxy-client.key
/// and some more other stuff, not related to certificates

Here https://github.com/kubernetes/minikube/blob/9b21d3c7e4b40d91401cd1440c97eac550dc6bd0/pkg/minikube/bootstrapper/certs.go#L71 is where they do it. According to it i also tried

context.useCertificateChain("/home/dbocharo/.minikube/apiserver.crt");
context.usePrivateKey("/home/dbocharo/.minikube/apiserver.key");

and

context.useCertificateChain("/home/dbocharo/.minikube/apiserver.crt");
context.usePrivateKey("/home/dbocharo/.minikube/apiserver.key");
context.setClientAuthorities("/home/dbocharo/.minikube/ca.crt");

and with client.crt

context.useCertificateChain("/home/dbocharo/.minikube/client.crt");
context.usePrivateKey("/home/dbocharo/.minikube/client.key"); //and without it too

but still have CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate

@whesse
Copy link
Contributor

whesse commented Dec 20, 2018

You may need to concatenate the two certificates, the client cert and the api cert authorizing it, into a single file that is the certificate chain. Other than that, I would search the internet for the error message you are getting, and for how client certificates work and the various errors that could be gotten from various stages. You could set up network capturing, to find out what is being correctly sent on the curl command, but is lacking on the Dart version.

@kevmoo kevmoo added area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-io labels Dec 20, 2018
@mleonhard
Copy link

#35981 (comment) has some working example code that uses SecurityContext.setTrustedCertificatesBytes().

@mleonhard
Copy link

This is blocking our production deployment. I was planning to use certificate pinning to prevent production app builds from talking to staging/qa/dev APIs, but that is prevented by #39425. Then I thought I could set the certificate as trusted, but it seems that setTrustedCertificates is completely broken.

Sadness and stress.

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 {
  await shellCommand(
      'openssl req -newkey rsa:2048 -nodes -keyout root.privatekey.pem -subj "/OU=root" -days 731 -sha256 -x509 -out root.certificate.pem');
  // await shellCommand('openssl x509 -in root.certificate.pem -text');

  await shellCommand(
      'openssl req -batch -newkey rsa:2048 -nodes -keyout issuer.privatekey.pem -subj "/OU=issuer" -days 731 -sha256 -new -out issuer.csr.pem');
  // await shellCommand('openssl req -in issuer.csr.pem -verify -text -noout');

  await File('issuer.crt.config')
      .writeAsString('[ v3_ca ]\nbasicConstraints = CA:TRUE\n');
  await shellCommand(
      'openssl x509 -req -in issuer.csr.pem -extfile issuer.crt.config -extensions v3_ca -CA root.certificate.pem -CAkey root.privatekey.pem -set_serial 1 -days 730 -sha256 -out issuer.certificate.pem');
  // await shellCommand('openssl x509 -in issuer.certificate.pem -text');

  await shellCommand(
      'openssl req -newkey rsa:2048 -passout pass:password -keyout api.privatekey.pem -subj "/CN=localhost" -days 731 -sha256 -new -out api.csr.pem');
  await shellCommand(
      'openssl x509 -req -in api.csr.pem -CA issuer.certificate.pem -CAkey issuer.privatekey.pem -passin pass:password -set_serial 1 -days 730 -sha256 -out api.certificate.pem');
  await shellCommand(
      'cat api.certificate.pem issuer.certificate.pem > api.certificate.chain.pem');

  final SecurityContext serverSecurityContext = new SecurityContext();
  serverSecurityContext.useCertificateChainBytes(
      await new File('api.certificate.chain.pem').readAsBytes());
  serverSecurityContext.usePrivateKey('api.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('');
  print('Connecting to server with openssl and printing certs');
  await shellCommand(
      'sleep 1 |openssl s_client -CAfile root.certificate.pem -showcerts -verify 10 -connect localhost:${httpServer.port}');

  print('');
  print('Making request with curl');
  await shellCommand(
      'curl -visS --max-time 5 --connect-timeout 5 --cacert root.certificate.pem https://localhost:${httpServer.port}/');

  print('');
  print('Making request with Dart');
  final SecurityContext clientSecurityContext =
      new SecurityContext(withTrustedRoots: false);
  clientSecurityContext.setTrustedCertificatesBytes(await new File(
    // 'api.certificate.chain.pem'
    'root.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);
}

Output:

$ dart main_bug.dart
Executing command openssl req -newkey rsa:2048 -nodes -keyout root.privatekey.pem -subj "/OU=root" -days 731 -sha256 -x509 -out root.certificate.pem
Generating a 2048 bit RSA private key
.........................................................................+++
.......................................................................+++
writing new private key to 'root.privatekey.pem'
-----
Executing command openssl req -batch -newkey rsa:2048 -nodes -keyout issuer.privatekey.pem -subj "/OU=issuer" -days 731 -sha256 -new -out issuer.csr.pem
Generating a 2048 bit RSA private key
...................+++
.............................................................+++
writing new private key to 'issuer.privatekey.pem'
-----
Executing command openssl x509 -req -in issuer.csr.pem -extfile issuer.crt.config -extensions v3_ca -CA root.certificate.pem -CAkey root.privatekey.pem -set_serial 1 -days 730 -sha256 -out issuer.certificate.pem
Signature ok
subject=/OU=issuer
Getting CA Private Key
Executing command openssl req -newkey rsa:2048 -passout pass:password -keyout api.privatekey.pem -subj "/CN=localhost" -days 731 -sha256 -new -out api.csr.pem
Generating a 2048 bit RSA private key
.................................+++
.................................................................+++
writing new private key to 'api.privatekey.pem'
-----
Executing command openssl x509 -req -in api.csr.pem -CA issuer.certificate.pem -CAkey issuer.privatekey.pem -passin pass:password -set_serial 1 -days 730 -sha256 -out api.certificate.pem
Signature ok
subject=/CN=localhost
Getting CA Private Key
Executing command cat api.certificate.pem issuer.certificate.pem > api.certificate.chain.pem
Server listening at https://localhost:63726/

Connecting to server with openssl and printing certs
Executing command sleep 1 |openssl s_client -CAfile root.certificate.pem -showcerts -verify 10 -connect localhost:63726
verify depth is 10
depth=2 OU = root
verify return:1
depth=1 OU = issuer
verify return:1
depth=0 CN = localhost
verify return:1
CONNECTED(00000005)
---
Certificate chain
 0 s:/CN=localhost
   i:/OU=issuer
-----BEGIN CERTIFICATE-----
MIICmTCCAYECAQEwDQYJKoZIhvcNAQELBQAwETEPMA0GA1UECwwGaXNzdWVyMB4X
DTIwMDEwNDAyMzg1NVoXDTIyMDEwMzAyMzg1NVowFDESMBAGA1UEAwwJbG9jYWxo
b3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnbF5A0bFzAxITrZL
MqC1eU53Wlbiq3LxhQAfdm90uluJ/c7bKvVcZkg5amq98Wg4QST+8cfkyUI5/yQ+
8Z1ThsayAGWAbxZzaUwfdMvfMNVAfO4jBjKSAov2t1r1LCQPsuj6apgdgFF0MTKT
WpEueCaH4JA4Anibu8nVn+5X3zYiBhCZZXmKY7zPk020FY3shSn/S87EG5RJT7z3
ByhusXskrPAaZlOTSanFyQUZt8KO/5WjLpoTQbYGtdeCtqGDQOSXBbRfTLeMstdl
38+bm7Bh29tUxORH67TK8DMWttHIFcmKqNv6w9saG0n9D0oEWIBp3P9ktxeinLt3
2jyVtwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQA+BYn5prfySENxCm6HfULOEHuS
DDPsefe9UfdvZOLWk2unvcIJ4o+egJkDewIPcw2zfeOjm4s0TuUv/0l+fgeWhC5t
M+cJiwavEeMq4WFLSMoqNFbBkOD1Ub7FYoc6/aa9YBBLD1B/i23Cc12p1hVddFFd
xhSbB0zkD+rznfRiFqjPQ9BaytX0e63neFRuhsF+e036ATfnROuJaxcwJpYSVCxj
lpse8Molestv6M8n3mnm+AsOvtIwQHoQL9nKJbi/i+x+wja7x3guIggSGyWYWtuN
EbhdsxPzn4ntwHCEbAc3fgHCKTARUe+95lwRhkuL0KRDZbQH3awpBiBlKMmD
-----END CERTIFICATE-----
 1 s:/OU=issuer
   i:/OU=root
-----BEGIN CERTIFICATE-----
MIICqzCCAZOgAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQLDARyb290
MB4XDTIwMDEwNDAyMzg1NVoXDTIyMDEwMzAyMzg1NVowETEPMA0GA1UECwwGaXNz
dWVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAws+EveRy5lhaZZAu
qUgR0UBO3oCjDVsYKe3aq84Rtzk8ndi2lwc1IxP5H2zzUAKS9FRtVmTicdOm8Aak
L0ZXhc4xcOVacAwjX3cM3PWpci7m1CxBYvRq35pgs11hGf8hp39pSoS5vDUr0gr2
SMmxs1ibF3j0Z1o4/JT+jc+vXrBg1oQ316qUn6wCnRt2h7l65QDpe2gxvtg6EZ7L
yadXhdOTrloYmxrE06WxmguM3Mq1GddAS0VFgYq/UOhxJQ5u4fkVrNokbxHn638K
7RRdv1EFPwXbeY8ROs3qqSiGTj+ZnIOi2yr/FfM774104ZKj6Ag7iCHtU7Nx9Z8E
5fALbQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAR
q4fvKc1Ocru1KlBerzbVbFg56s0M9JcGZR0PC3VALnH//UFpDEzvMzku0YSyVS8z
HZSPH4Gs4XjLGgJC3nRj8pjCELB+0i31SJGe67tE5IgXm3ZL2ajV9BIUmkowBkVj
5+z8FmibA6k2B4byQQo5erOrkhAl5kvxVlRM6iw4WlHM8M9JKX+Mov6ZETyLIwTF
hhfGIV5J+bOxY8GlBMawQosn8khrvE7j7lJFPnhSK9L1DhUVMkAVcS4D2VzpDQop
a9cDMrrgadJh+KzWqHp1B6DlsxWN3/kfq4wWCkoHducWM2olQ3ye7tvZ8BOMMfM2
2MVEcmASSzUxsdcdXnD/
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=localhost
issuer=/OU=issuer
---
No client certificate CA names sent
Server Temp Key: ECDH, X25519, 253 bits
---
SSL handshake has read 1978 bytes and written 289 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: FC154837D4FE456261271BF050A3374CFE7E691B07E324F51EC6F50274FF24ED
    Session-ID-ctx: 
    Master-Key: F33091125A9C4C67FFE43A0AD54E27374B68D33C9B64E5EEF8995CE532A99042CF6A7073E35CE4286AF4F2B6FD3C7DAF
    TLS session ticket lifetime hint: 7200 (seconds)
    TLS session ticket:
    0000 - 5b b5 90 e7 7d 43 3b 35-c6 d8 6b b6 53 18 40 2c   [...}C;5..k.S.@,
    0010 - 1b 06 b4 96 65 5e 66 15-53 b0 88 98 66 b5 e9 96   ....e^f.S...f...
    0020 - d7 93 34 37 f7 55 ac 39-2a 9a 95 5a af 74 a5 93   ..47.U.9*..Z.t..
    0030 - 76 f9 3d 95 dc a4 35 b9-cf d1 89 a0 30 3d ec 7f   v.=...5.....0=..
    0040 - 5c 95 fc 0a 47 98 dc 99-30 ab f7 b4 c5 8a 8f 56   \...G...0......V
    0050 - 55 b5 a4 07 78 4e 44 3b-86 22 2d 1d 11 b2 2c 91   U...xND;."-...,.
    0060 - 50 d6 72 3a e6 b2 d2 e1-9e ac b6 62 b8 dc ef d2   P.r:.......b....
    0070 - e8 67 53 65 ff b1 ca d8-9b 87 5e 4b c2 a9 e7 1a   .gSe......^K....
    0080 - 8d 40 e8 27 f2 e0 22 7d-a9 d1 a7 f0 c7 61 63 d2   .@.'.."}.....ac.
    0090 - 1c 77 76 18 73 1d 93 ef-82 42 ed b1 2b cf cf e0   .wv.s....B..+...

    Start Time: 1578105535
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
---
DONE

Making request with curl
Executing command curl -visS --max-time 5 --connect-timeout 5 --cacert root.certificate.pem https://localhost:63726/
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 63726 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 63726 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: root.certificate.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [223 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [87 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [1369 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [300 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [37 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=localhost
*  start date: Jan  4 02:38:55 2020 GMT
*  expire date: Jan  3 02:38:55 2022 GMT
*  common name: localhost (matched)
*  issuer: OU=issuer
*  SSL certificate verify ok.
> GET / HTTP/1.1
> Host: localhost:63726
> User-Agent: curl/7.64.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< content-type: text/plain; charset=utf-8
< x-frame-options: SAMEORIGIN
< x-xss-protection: 1; mode=block
< transfer-encoding: chunked
< x-content-type-options: nosniff
< 
{ [15 bytes data]
* Connection #0 to host localhost left intact
* Closing connection 0
HTTP/1.1 200 OK
content-type: text/plain; charset=utf-8
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
transfer-encoding: chunked
x-content-type-options: nosniff

body1
Making request with Dart
Unhandled exception:
HandshakeException: Handshake error in client (OS Error: 
	CERTIFICATE_VERIFY_FAILED: ok(handshake.cc:354))
#0      main (package:cozydateapp/main_bug.dart:81:37)
<asynchronous suspension>
#1      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:307:19)
#2      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:174:12)
$ openssl version
LibreSSL 2.8.3
$ dart --version
Dart VM version: 2.8.0-dev.0.0.flutter-c547f5d933 (Fri Dec 27 00:43:14 2019 +0000) on "macos_x64"
$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel master, v1.13.6-pre.44, on Mac OS X 10.15.2 19C57, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 11.3)
[✓] Android Studio (version 3.5)
[!] IntelliJ IDEA Community Edition (version 2019.3.1)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[!] VS Code (version 1.41.1)
    ✗ Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
[✓] Connected device (1 available)

! Doctor found issues in 2 categories.
$ 

@zanderso
Copy link
Member

zanderso commented Jan 6, 2020

/cc @zichangg @bkonyi @a-siva

@zichangg zichangg self-assigned this Jan 21, 2020
@hubblejacklee
Copy link

It may not fully related to the issue but I provide the following information for reference:

I am using Erlang (Ranch) to create a server and use dart to connect it. In my experiment, I have these result:

  • case A - successfully handshake

Erlang Server

{ok, _} = ranch:start_listener(admin_ssl,
		ranch_ssl, #{socket_opts => [
                . . ., {certfile, "server.cer"}, {cacertfile, "ca.cer"}, {keyfile, "server.key"}
,{fail_if_no_peer_cert, true} , {verify, verify_peer}]}, . . 

Dart client

    final SecurityContext context = SecurityContext(withTrustedRoots: false);
    context.useCertificateChain("client.cer");
    context.usePrivateKey("client.key");
    Socket socket = await Socket.connect(serverIp, port);
    socket = await SecureSocket.secure(socket, host: "server"
      , context: context, onBadCertificate: (cert) => true);

  • case B - successfully handshake )

Erlang Server

	{ok, _} = ranch:start_listener(admin_ssl,
		ranch_ssl, #{socket_opts => [
                ...., {certfile, "server.cer"}, {cacertfile, "ca.cer"}, {keyfile, "server.key"}
, {verify, verify_peer}]},...

Dart client

    final SecurityContext context = SecurityContext(withTrustedRoots: false);
    Socket socket = await Socket.connect(serverIp, port);
    socket = await SecureSocket.secure(socket, host: "server"
      , context: context, onBadCertificate: (cert) => true);

PS The difference between case A and case B is {fail_if_no_peer_cert, true} and no cert added to dart client. Case A will be failed too if "context.usePrivateKey("client.key");" is skipped.

  • case C - failed due to SSLV3_ALERT_HANDSHAKE_FAILURE(tls_record.cc:587))

Erlang Server

	{ok, _} = ranch:start_listener(admin_ssl,
		ranch_ssl, #{socket_opts => [
                ...., {certfile, "server.cer"}, {cacertfile, "ca.cer"}, {keyfile, "server.key"}
,{fail_if_no_peer_cert, true} , {verify, verify_peer}]},...

Dart client

    final SecurityContext context = SecurityContext(withTrustedRoots: false);
    context.setTrustedCertificates("client.cer");
    Socket socket = await Socket.connect(serverIp, port);
    socket = await SecureSocket.secure(socket, host: "server"
      , context: context, onBadCertificate: (cert) => true);

PS In server log of case C, [{asn1_certificates,[]}] is received when server requests client certificate. In case A, client certificate is sent from dart side. So, it seems that setTrustedCertificates() and useCertificateChain() have different effect.

$> dart --version
Dart VM version: 2.7.0 (Fri Dec 6 16:26:51 2019 +0100) on "macos_x64"

@bonesyblue
Copy link

I too had an issue using the setTrustedCertificates and setClientAuthorities methods which resulted in a HandshakeException. Thanks to @whesse for your comment which pointed me in the direction of concatenating the client and root certificates. This solved the issues I was having establishing a secure TLS 1.2 connection in my Flutter app. My comment here: #37173 (comment) has some working example code.

@kapace
Copy link

kapace commented Mar 26, 2021

Just wanted to mention, I had ran into to problems with setTrustedCertificates() and then it occurred to me that it is only happening on MacOS platform (probably because it's using different code for certificate verification?). The same code that I'm using to trust a self-signed certificate fails for MacOS, but works on Linux/Android. If you are having trouble with this, perhaps try your code on another platform, and then we can confirm this as a platform bug?

@a-siva
Copy link
Contributor

a-siva commented Mar 29, 2021

/cc @aam

@Renneke
Copy link

Renneke commented Jun 21, 2022

@bkonyi
Copy link
Contributor

bkonyi commented Jun 21, 2022

cc @brianquinlan

@brianquinlan
Copy link
Contributor

The original issue (certificate verification failing when the host is a IP address) should be fixed in Dart 3.1.

If there are other issues, could people please file separate bugs?

And thanks to @vially for fixing this!

@Nebojsa92
Copy link

Nebojsa92 commented Dec 25, 2023

@kapace It's been a while, but now I'm facing same issue and only on iOS. I didn't find any resolution of that issue. Did you have any luck?
Screenshot 2023-12-25 at 09 51 02

Update:
For users that are facing issues like this in picture above, but have everything in place (setTrustedCertificates in correct format and everything), please pay attention to guideilines from apple regarding trusted certificates: https://support.apple.com/en-au/103769

This caused me ~5 days of pain. Hopefully this will help somebody.

@rkfcccccc
Copy link

rkfcccccc commented Jul 27, 2024

@Nebojsa92 I spent 1 day working on this and saved a lot of time thanks to your help. You really helped me out 🫡

In my case, the problem was with the certificate expiration date - when I changed it to 825 days (which is the required maximum time by Apple), everything worked perfectly for me! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-io
Projects
None yet
Development

No branches or pull requests