CERTIFICATE_VERIFY_FAILED: Hostname mismatch(handshake.cc:352)) for TCP connection on local server - flutter

My code is connecting to AWS-END-POINT properly but when I tried connecting to Greengrass core using local network ip. I get this error.
E/flutter (12349): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: HandshakeException: Handshake error in client (OS Error:
E/flutter (12349): CERTIFICATE_VERIFY_FAILED: Hostname mismatch(handshake.cc:352))
I have already checked the greengrass core. it's working fine. It is connecting to web client very well.
I think there might be some issue of using ip address instead of URL address. but i am not sure. Can anyone help please?
The Code I am running is:
import 'dart:async';
import 'dart:io';
import 'package:mqtt_client/mqtt_client.dart';
import 'dart:convert' show utf8;
import 'dart:convert';
Future<int> main() async {
const String url =
'192.168.8.106';
const int port = 8883;
const String clientId =
'MY CLIENT ID';
MqttClient client = MqttClient(url,clientId);
client.port = port;
client.secure = true;
final SecurityContext context = new SecurityContext(withTrustedRoots: true);
context.setTrustedCertificatesBytes(utf8.encode(' CERT '));
context.useCertificateChainBytes(utf8.encode(' CERT '));
context.usePrivateKeyBytes(utf8.encode(' PRIVEATE KEY '));
client.securityContext = context;
client.setProtocolV311();
// logging if you wish
client.logging(on: false);
print('Before Connecting');
try{
await client.connect();
}catch(e){
print('CATCH IS : ');
print (e);
}
print('After Connecting');
if (client.connectionStatus.state == MqttConnectionState.connected) {
print('iotcore client connected');
} else {
client.disconnect();
}
print('Sleeping....');
for (int i=1; i>0; i++)
{
const String topic = '\$aws/things/Pi_tmfacility_0_1/shadow/update';
Map<dynamic, dynamic> payload =
{'state': {
'desired': {
'number' : i
}
}
};
final MqttClientPayloadBuilder builder = MqttClientPayloadBuilder();
builder.addString(json.encode(payload));
print('into the publish to get single device shadow ');
client.publishMessage(topic, MqttQos.atMostOnce, builder.payload);
print('Ready to Sleep');
await MqttUtilities.asyncSleep(10);
print('Loop no = $i');
}
print('Disconnecting');
client.disconnect();
return 0;
}

The problem is that the CN (or SANs) in the certificate presented by the local machine do not include 192.168.8.106.
You can verify this by using the openssl s_client command:
openssl s_client -connect 192.168.8.106:8883 -CAfile /path/to/ca/cert
This means that the SSL/TLS library in flutter will complain that certificate doesn't reliably represent that machine.
This is important as this is what stops Man-in-the-Middle attacks.
You have 2 options to solve this.
reissue the certificate with a CN or SAN entry with 192.168.8.106
See if you can find a way to influence the Certificate verification. There are examples of how to do this with the dart http library (https://stackoverflow.com/a/59303283/504554) but I haven't found this in the MQTT client library (I haven't looked that hard).
You have to be very careful if you go with option 2 to ensure that you do not open up too big a hole for Man-in-the-middle attacks.

I got the same error in my flutter app but my solution and reason was a bit different.
My certificate was "*.xxxxxx.com" (star certificate).
(xxxxxx.com is not for +18 site, just sample :) )
My subdomain name was sub_domain.xxxxxx.com.
Solution was simple, but it take time to solve it.
"_" (underscore) was the main problem in the domain name.
I changed it to subdoman.xxxxxx.com and it worked.

Related

How can I get devices name after gathering all the IP addresses connected in a network? Flutter

I am using lan_scanner flutter package to gather IP Addresses of a network.I want to also get the hostname of each particular address. Any ideas on how can I achieve that?
I tried using some other packages but to no avail.
you can connect to that device and ask for it's hostname
first add a server socket to listen for clients (devices)
const int port = 7575;
try {
ServerSocket server = await ServerSocket.bind(deviceIp, port);
server.listen((client) {
client.listen((data) {
final ip = client.remoteAddress.address;
final hostname = String.fromCharCodes(data);
print("hostname of $ip is $hostname");
});
});
} on SocketException catch(_) {
print('failed to create a server socket');
}
then for each device connect to the server and send the device hostname
try {
Socket client = await Socket.connect(targetIp, port);
client.write(Platform.localHostname);
client.destroy();
} on SocketException catch(_) {
print('failed to connect to the server');
}

Flutter: Is it mandatory to selfSign server to call api in flutter with https

I have a spring boot server that doesn't have ssl certificate and NOT selfsigned. I want to call certain apis with https://domainName.com/apiPath . (with https:// not http://)
So I found that I can set configs as to accept bad certificates. like this code...
import 'dart:io';
HttpClient client = new HttpClient();
client.badCertificateCallback =((X509Certificate cert, String host, int port) => true);
io.HttpClientRequest request = await client.postUrl(Uri.parse(
"https://domainname.com/apiPath"));
request.headers.set('Content-Type', 'application/json');
request.add(utf8.encode(jsonEncode(body)));
io.HttpClientResponse result = await request.close();
api call works with http://... But it doesn't work with https://
this is the error.
E/flutter ( 8183): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: SocketException: Connection refused (OS Error: Connection refused, errno = 111), address = <domain name>, port = 33028
Can I know, for this to work is it mandotory to self sign the backend(server)?
(may be this is a stupid question. I don't know much about this topic)

Flutter Dio Networking: SSL pinning using certificate's public hashed key string (not a file)

I currently use the following snippet to include my SSL certificate file into the http client:
final List<int>? _certBytes; //I read it from .cer file included in the project
(_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client) {
if (_certBytes != null) {
SecurityContext sc = SecurityContext();
sc.setTrustedCertificatesBytes(_certBytes!);
HttpClient httpClient = HttpClient(context: sc);
return httpClient;
} else {
client.badCertificateCallback =
((X509Certificate cert, String host, int port) => true);
return client;
}
};
while this code works well, it will stop working if the certificate is expired, which means that I need to add a new certificate file into the app and upload again to the app stores, so I decided to host the certificate as a hashed string in Firebase's Remote Config and read it upon app launch, so I can change the certificate remotely without building new versions for the app, but couldn't find a way to set the SecurityContext with a certificate hashed string in the Dio's HTTPClient

Dart gRPC TLS certificates with PEMs

I'm having a bit of trouble sorting out how to adapt my Dart gRPC client to use the same TLS settings that are working with my Go client. I've already validated that I can interface with the server suppling the correct CA cert, client cert and client key. In Go I'm using:
pemServerCA, err := ioutil.ReadFile("pems/ca-cert.pem")
if err != nil {
return nil, err
}
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(pemServerCA) {
return nil, fmt.Errorf("failed to add server CA's certificate")
}
// Load client's certificate and private key
clientCert, err := tls.LoadX509KeyPair("pems/client-cert.pem", "pems/client-key.pem")
if err != nil {
return nil, err
}
// Create the credentials and return it
config := &tls.Config{
Certificates: []tls.Certificate{clientCert},
RootCAs: certPool,
}
Just supplying that in case it helps demonstrate what's working. In Dart I'm doing this:
ChannelCredentials credentials = ChannelCredentials.secure(
certificates: utf8.encode(grpcCertificate),
onBadCertificate: (certificate, host) {
return host == apiURL + ':' + apiPort.toString();
},
);
grpcCertificate contains the contents of client-key.pem. I suspect this is not correct. I'm not very skilled with certificates like this so I'm a bit at a loss. What value should I be supplying to certificates to achieve a successful handshake with the server?
From the above it seems like I need to parse my PEMs into X.509. In Go that's super easy, not sure how to handle this in Dart.
Edit: I've made a bit of progress:
List<int> list = grpcCertificate.codeUnits;
Uint8List cert = Uint8List.fromList(list);
ChannelCredentials credentials = ChannelCredentials.secure(
certificates: cert,
authority: 'localhost',
onBadCertificate: (certificate, host) {
return host == apiURL + ':' + apiPort.toString();
},
);
The server seems to hate this less and spits out:
flutter: gRPC Error (code: 14, codeName: UNAVAILABLE, message: Error connecting: TlsException: Failure trusting builtin roots (OS Error:
BAD_PKCS12_DATA(pkcs8_x509.c:645), errno = 0), details: null, rawResponse: null)
Thanks.
I ended up receiving somewhat of a proper answer on the grpc-dart issues page. The solution looks something like this:
class MyChannelCredentials extends ChannelCredentials {
final Uint8List? certificateChain;
final Uint8List? privateKey;
MyChannelCredentials({
Uint8List? trustedRoots,
this.certificateChain,
this.privateKey,
String? authority,
BadCertificateHandler? onBadCertificate,
}) : super.secure(
certificates: trustedRoots,
authority: authority,
onBadCertificate: onBadCertificate);
#override
SecurityContext get securityContext {
final ctx = super.securityContext;
if (certificateChain != null) {
ctx.useCertificateChainBytes(certificateChain);
}
if (privateKey != null) {
ctx.usePrivateKeyBytes(privateKey);
}
return ctx;
}
}
final cred = MyChannelCredentials(
trustedRoots: File('pems/ca-cert.pem').readAsBytesSync(),
certificateChain: File('pems/client-cert.pem').readAsBytesSync(),
privateKey: File('pems/client-key.pem').readAsBytesSync(),
authority: 'localhost',
);

CERTIFICATE_VERIFY_FAILED when trying to import data from API

I'm trying to learn Flutter/Dart and I'm having to much problems. Now I'm trying to obtain some values from an API. My code is:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:myapp01_apirequest/src/models/uplink_models.dart';
class UplinksProvider{
String _url = 'xxx.yyy.com';
Future<List<Uplink>> getEnCines() async{
try{
final url = Uri.https(_url, ':14442/api/external/login', {
'username': 'Joe689',
'password': '15.Job_1825zz'
});
final resp = await http.get(url);
final decodedData = json.decode(resp.body);
print('Patata');
print(decodedData);
return [];
}catch (error){
print('++++++++++///////++++++++++++++++');
print(error);
print('++++++++++*******++++++++++++++++');
}
}
}
Reading the Uri constructor documentation I understood that I have to split in 3 my url.
First one is authority. In my case I think is xxx.yyy.com.
Second is the unencodedPath. In my case I think is :14442/api/external/login.
Finally a map with params in my case the username and pass (the only thing I'm pretty sure is correct in my code).
If I do this, any problem appears, but the print('Patata'); and print(decodeData); don't appear. In addition, a file called io_client.dart opens and marks the next line.
var ioRequest = (await _inner.openUrl(request.method, request.url))
The console shows nothing (I think):
Launching lib\main.dart on LG M700 in debug mode...
lib\main.dart:1
Formato de par�metros incorrecto:
√ Built build\app\outputs\flutter-apk\app-debug.apk.
Connecting to VM Service at ws://127.0.0.1:55883/KBAtv2-Jljc=/ws
Why no errors appears but I can't obtain my desired data?
EDIT:
According for what #Preet Shah's said I press the "VS run button" and appears the following exception three times:
I/flutter ( 1932): ++++++++++///////++++++++++++++++
I/flutter ( 1932): HandshakeException: Handshake error in client (OS Error:
I/flutter ( 1932): CERTIFICATE_VERIFY_FAILED: Hostname mismatch(handshake.cc:354))
I/flutter ( 1932): ++++++++++*******++++++++++++++++
And finally appears:
I/Choreographer( 1932): Skipped 148531 frames! The application may be doing too much work on its main thread.
D/vndksupport( 1932): Loading /vendor/lib/hw/android.hardware.graphics.mapper#2.0-impl.so from current namespace instead of sphal namespace.
D/vndksupport( 1932): Loading /vendor/lib/hw/gralloc.msm8937.so from current namespace instead of sphal namespace.
I/Choreographer( 1932): Skipped 35 frames! The application may be doing too much work on its main thread.
Then the problem is a certification problem, CERTIFICATE_VERIFY_FAILED. As I feel more comfortable with python, I have done some tests to understand the problem. I have been able to verify that this API requires having all the certification verifications in false, otherwise it never leaves the loop. Here is my Python code (just to show what I'm saying).
import requests
import json
log_params = {'username': 'Joe689', 'password': '15.Job_1825zz'}
headers = {'Content-type': 'application/json'}
url = 'https://xxx.yyy.com:14442/api/external/login'
response = requests.post(url, data=json.dumps(params), headers=self.headers, verify=False)
finalRes = json.loads(response.text)
As I said, this code is just for me to understand the problem because I am a newbie to Dart. Here I found this answer and it seems has the solution but I don't know how to implement it, using my Uri.https estructure (maybe it's not possible).
I tried this, but isn't working:
Map<String, String> requestHeaders = {
'Content-type': 'application/json'
};
final resp = await http.get(url, headers:requestHeaders);
Thank you very much.
Try this:
HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
String url ='xxx.yyy.com:14442/api/external/login';
Map map = {
"email" : "Joe689" ,
"password" : "15.Job_1825zz"
};
HttpClientRequest request = await client.getUrl(Uri.parse(url));
request.headers.set('content-type', 'application/json');
request.add(utf8.encode(json.encode(map)));
HttpClientResponse response = await request.close();
String reply = await response.transform(utf8.decoder).join();
print(reply);
Now, check what reply contains. And accordingly, return the data.