-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Comments
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. 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. |
@whesse thank you very much for your reply.
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 |
The documentation for rawSocketConnect, at the last change 3 years ago, was changed to say
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.
|
@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 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 |
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. |
#35981 (comment) has some working example code that uses |
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 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:
|
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:
Erlang Server
Dart client
Erlang Server
Dart client
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.
Erlang Server
Dart client
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.
|
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. |
Just wanted to mention, I had ran into to problems with |
/cc @aam |
I can also confirm this issue: https://stackoverflow.com/questions/72659221/certificate-verify-failed-although-it-should-be-valid |
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! |
@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? Update: This caused me ~5 days of pain. Hopefully this will help somebody. |
@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! 🎉 |
https://gist.github.com/bdshadow/ad64d6f3660422e8ff06572f4bf6e7fd
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:
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!The text was updated successfully, but these errors were encountered: