Xamarin.Android Mqtt Client with TLS Unauthorized result - android

I'm basically new to mobile development and stuck for a few weeks because,
I am unable to connect XAMARIN Android app with MQTTT over TLS.
Maybe i am using the wrong library or is it an Xamarin error?
My certificate is a .crt file from m21.cloudmqtt.com.
https://crt.sh/?id=5253106089
First was using System.Net MQTT but they are currently unable to work over TLS.
So i currently i am using MQTTNet, with (for the moment) the default certificate from m21.cloud.com which i have stored in Assets folder.I tested this local with and without a certificate and its working fine.
The MQTTNet Client with cert from local folder is like this, and works like it should:
var caCert = new X509Certificate2("C:/pathtocert.crt");
var source = new CancellationTokenSource().Token;
var token = source;
var factory = new MqttFactory();
var mqttClient = factory.CreateMqttClient();
var mqttOptions = new MqttClientOptionsBuilder()
.WithTcpServer(server, port)
.WithClientId(clientId)
.WithCredentials(username, pswd)
.WithTls(new MqttClientOptionsBuilderTlsParameters
{
UseTls = true,
Certificates = new List<X509Certificate> { caCert }
})
.Build();
mqttClient.ConnectAsync(mqttOptions, token).Wait(token);
To get the Certifcate from Android Assets Folder i used the same client code as above and t et the certificate i used:
using (var assetStream = await Xamarin.Essentials.FileSystem.OpenAppPackageFileAsync("filename.crt"))
using (var memStream = new MemoryStream())
{
assetStream.CopyTo(memStream);
caCert = new X509Certificate(memStream.ToArray());
}
I dont understand why its not working, for now its also okay if the certificate isn't used but it needs to use TLS. But tried, and i still get unauthorized error.
var mqttOptions = new MqttClientOptionsBuilder()
.WithTcpServer(server, port)
.WithClientId(clientId)
.WithCredentials(username, pswd)
.WithTls(new MqttClientOptionsBuilderTlsParameters
{
UseTls = true,
})
.Build();
Thanks in adnvance.

Related

xamarin android : httpclient PostAsync

we have an app under xamarin android build with visual studio 2017.
this app works since three years without any problems.
since two weeks and I don't know why actually some device can't sync with our back end.
It's really strange because nothing has change in this part .
this error does not appear on all devices but on one or two from time to time
we use the dll httpClient for to sync the datas with our backend.
If i put a break point inside the postAsync I have an exception with this -> Cannot access a disposed object. Object name: 'System.Net.Sockets.NetworkStream
Any one has an idea about how to solve this ? also what does it meam ?
Here is it the code of the postAsync method :
thanks for our time and comment guys
public override HttpResult ExecutePost(Uri target, string body)
{
var client = new HttpClient();
client.MaxResponseContentBufferSize = MaxHttpResponseBufferSize;
try
{
var requestContent = new StringContent(body, RequestContentEncoding, RequestContentType);
var response = client.PostAsync(target, requestContent).Result;
if (response.IsSuccessStatusCode)
{
var content = response.Content.ReadAsStringAsync().Result;
return new HttpResult(content, null, null);
}
return new HttpResult(null, "Response is empty", response.StatusCode.ToString());
}
catch (Exception e)
{
return new HttpResult(null, "Problem with the HttpPost", e.Message);
}
}
I experienced the same issue. Have been battling for 6 hours on this issue.
If you read the error, I was getting (Failed to connect to localhost/127.0.0.1:7113). If you put localhost in your browser or swagger tool it will work but if you put https://127.0.0.1:7113/api/weatherforecast in your browser it will not work. It will give you a certificate problem.
So I think you have to resolve 127.0.0.1 to localhost with https certificate on your local dev machine.
I'm building a MAUI app with Visual Studio 2022 Preview.
So I solved this issue by deploying my API to AZURE.
Then update to the azure url for example:
string apiUrl = "https://weatherforecast.azurewebsites.net/api/weatherforecast";
and then it worked brilliantly. Like super brilliantly.
Here is my code:
public void LoginAsync()
{
HttpClient client = new HttpClient();
string apiUrl = "https://weatherforecast.azurewebsites.net/api/weatherforecast";
UserCredentials.EmailAddress = LoginUIEntity.EmailAddress;
UserCredentials.Password = LoginUIEntity.Password;
string serialized = JsonConvert.SerializeObject(UserCredentials);
var inputMessage = new HttpRequestMessage
{
Content = new StringContent(serialized, Encoding.UTF8, "application/json")
};
inputMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
var message = client.PostAsync(apiUrl, inputMessage.Content).Result;
if (message.IsSuccessStatusCode)
{
var apiResponse = message.Content.ReadAsStringAsync();
UserCredentials = JsonConvert.DeserializeObject<UserCredentials>(apiResponse.Result);
if (UserCredentials.IsValid)
{
UserCredentials.IsLoggedIn = true;
}
else
{
ErrorMessage = "Invalid credentials supplied.";
}
}
}
catch (Exception ex)
{
ErrorMessage = "An error has occurred. Please contact support if the error persists.";
}
}
}
thanks for the link your provide.
I've try up the buffer on the postasync / try to sync in wifi OR 3G / delete special character in json / ...
but nothing work
we have move the prod database to the test and try to sync the data to the test database with postman. with postman the result was ENTITY TOO LARGE !
Json is size > 1.2 mega and the default value inside IIS is set to 1 mega
Here is it the problem ...
thanks problem solve

Xamarin Forms, Android and HttpClient issue

I've created in Xamarin Forms for iOS a HttpClient function to send a picture from the device to my server. The core function is
var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(fileBytes);
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fName
};
fileContent.Headers.ContentDisposition.Parameters
.Add(new NameValueHeaderValue("userId", UserId.ToString()));
content.Add(fileContent);
using (var client = new HttpClient()) {
client.DefaultRequestHeaders.Add("authenticationToken", SyncData.Token);
HttpResponseMessage response = await client.PostAsync(url, content);
if (response.IsSuccessStatusCode)
{
// more code
}
}
I'm using System.Net.Http. I tried to use the same function for a project in Android but surprisingly it doesn't work. The problem is in the header: if I inspect fileContent I can see every keys but for webapi on the server FileName is not received.
After some logs, I changed this function adding more client.DefaultRequestHeaders like
client.DefaultRequestHeaders.Add("FileName", fName);
Now the webapi receives FileName param.
Now my question is: what did I wrong?
Personally, I use the Add method on MultipartFormDataContent that accepts a filename.
var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(fileBytes);
...
// Use the overload Add method which accepts a file name
content.Add(fileContent, "FileName", fName);
...
I'm not sure if this will solve your problem or not, but it works for me.

netstream no working on adobe air - amazon s3 - signed cloudfront

I can't seem to get net stream to work on my adobe air application. Android or desktop.
Here is the code with the valid link. The link works on VLC.
Any suggestions?
public function Main() {
url = "rtmp://s1p2w2yhnipjkt.cloudfront.net/cfx/st/mp4:demo.mp4?Expires=1594281188&Signature=AW0M39xRqX5bjhlw4EPMvdPzum8~gbK6Wsl7vkI3av6cWDXQ36lCfTlnpOXse6qiP9RSbuT-jhor84DHvZg7yPmvnnlPgAEQlndtgsBvzwUj~kGXES~~VWvHGVuUHTDnK~rAWcOmzpbRi-jWPpN71Ks2wnJeri596lqh2dOkUcg_&Key-Pair-Id=APKAI7XWAS4L22TVE3HA";
_netConnection = new NetConnection();
_netConnection.addEventListener(NetStatusEvent.NET_STATUS, onConnect);
_netConnection.client = {onBWDone:onNetConnectionBWDone};
_netConnection.connect(url);
_netConnection.connect("rtmp://s1p2w2yhnipjkt.cloudfront.net/cfx/st");
trace("connect")
}
private function onConnect(event:NetStatusEvent):void {
trace(event.info.code);
if(event.info.code == "NetConnection.Connect.Success")
{
_netStream = new NetStream(_netConnection);
_netStream.client = {onMetaData:onMetaData, onPlayStatus :onPlayStatus};
var video:Video = new Video();
video.attachNetStream(_netStream)
_netStream.play("mp4:demo.mp4");
addChild(video);
}
}
I tested your stream in my android device and it's working fine, and I think that your forgot to use the params with your stream, so you can do like this :
var url:String = 'rtmp://s1p2w2yhnipjkt.cloudfront.net/cfx/st',
// or rtmp://s1p2w2yhnipjkt.cloudfront.net:80/cfx/st
stream:String = 'mp4:demo.mp4?Expires=1594281188&Signature=AW0M39xRqX5bjhlw4EPMvdPzum8~gbK6Wsl7vkI3av6cWDXQ36lCfTlnpOXse6qiP9RSbuT-jhor84DHvZg7yPmvnnlPgAEQlndtgsBvzwUj~kGXES~~VWvHGVuUHTDnK~rAWcOmzpbRi-jWPpN71Ks2wnJeri596lqh2dOkUcg_&Key-Pair-Id=APKAI7XWAS4L22TVE3HA';
// ...
_netConnection.connect(url);
// ...
_netStream.play(stream);
Of course you have to add the INTERNET permission to your app to be able to get the stream, and also to be sure that your device can connect to the video server via the port 1935 or 80 (at worst).
Hope that can help.

How to make RESTAPI calls in android using node.js

I have created a RESTAPI in Node.js and trying it to invoke into my android application. But i am not able to make the request to the Node.js Rest API.
My code are as follows:
Node.js
var restify = require('restify');
var request = require('request');
var http = require('http');
var appContext = require('./config.js');
function labsAPI(jsonparseStr) {
return JSON.stringify(labsapi);
}
function searchbase(req, res, next){
var options = {
host: appContext.host,
path: appContext.path+req.params.name+appContext.queryString
};
cbCallback = function(response) {
var str = '';
response.on('data', function (chunk) {
str += chunk;
});
response.on('end', function () {
jsonparseStr = JSON.parse(str);
json_res = labsAPI(jsonparseStr);
res.writeHead(200,{'Content-Type':'application/json'});
res.end(json_res);
});
}
http.request(options, cbCallback).end();
}
var server = restify.createServer({name:'crunchbase'});
server.get('/search/:name',searchbase);
server.listen(appContext.port, function() {
console.log('%s listening at %s', server.name, server.url);
});
After running my code like : localhost:8084/search/name
I am able to return the output to the browser a valid Json.
Now i want to consume this web service into my android application , I am not able to figure out how to do it.
I tried some of the example
http://hmkcode.com/android-parsing-json-data/
In the above blog in MainActivity.java i changed my url to
new HttpAsyncTask().execute("http://127.0.0.1:8084/search/name");
but it is displaying nothing
127.0.0.1 is the IP address for localhost. From your browser localhost resolves to your computer, from your Android device localhost resolves to your Android device, but your node application isn't running on it.
You need to figure out what's your computer's remote address. LAN or WLAN would be enough as long as your on the same network as your Android device.
Also make sure firewall settings allow access to your computer.

Google Play Android Developer API from C#/.NET service - (400) Bad Request

I'm trying to access a Purchase Status API from my ASP.NET web server using Google APIs .NET Client Library which is a recommended way for using Purchase API v1.1. However, the Authorization page of this API suggests direct web requests to Google's OAuth2 pages instead of using the corresponding client libraries.
OK, I tried both methods with all variations I could imagine and both of them lead to "The remote server returned an error: (400) Bad Request.".
Now what I've done to get to my point. First I've made all steps 1-8 under the Creating an APIs Console project of the Authorization page. Next I generated a refresh token as described there. During refresh token generation I chose the same Google account as I used to publish my Android application (which is in published beta state now).
Next I've created a console C# application for test purposes in Visual Studio (may be console app is the problem?)
and tried to call the Purchase API using this code (found in some Google API examples):
private static void Main(string[] args)
{
var provider =
new WebServerClient(GoogleAuthenticationServer.Description)
{
ClientIdentifier = "91....751.apps.googleusercontent.com",
ClientSecret = "wRT0Kf_b....ow"
};
var auth = new OAuth2Authenticator<WebServerClient>(
provider, GetAuthorization);
var service = new AndroidPublisherService(
new BaseClientService.Initializer()
{
Authenticator = auth,
ApplicationName = APP_NAME
});
var request = service.Inapppurchases.Get(
PACKAGE_NAME, PRODUCT_ID, PURCHASE_TOKEN);
var purchaseState = request.Execute();
Console.WriteLine(JsonConvert.SerializeObject(purchaseState));
}
private static IAuthorizationState GetAuthorization(WebServerClient client)
{
IAuthorizationState state =
new AuthorizationState(
new[] {"https://www.googleapis.com/auth/androidpublisher"})
{
RefreshToken = "4/lWX1B3nU0_Ya....gAI"
};
// below is my redirect URI which I used to get a refresh token
// I tried with and without this statement
state.Callback = new Uri("https://XXXXX.com/oauth2callback/");
client.RefreshToken(state); // <-- Here we have (400) Bad request
return state;
}
Then I tried this code to get the access token (I found it here: Google Calendar API - Bad Request (400) Trying To Swap Code For Access Token):
public static string GetAccessToken()
{
var request = WebRequest.Create(
"https://accounts.google.com/o/oauth2/token");
request.Method = "POST";
var postData =
string.Format(
#"code={0}&client_id={1}&client_secret={2}&redirect_uri={3}&grant_type=authorization_code",
// refresh token I got from browser
// also tried with Url encoded value
// 4%2FlWX1B3nU0_Yax....gAI
"4/lWX1B3nU0_Yax....gAI",
// ClientID from Google APIs Console
"919....1.apps.googleusercontent.com",
// Client secret from Google APIs Console
"wRT0Kf_bE....w",
// redirect URI from Google APIs Console
// also tried Url encoded value
// https%3A%2F%2FXXXXX.com%2Foauth2callback%2F
"https://XXXXX.com/oauth2callback/");
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length;
using (var dataStream = request.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
}
try
{
// request.GetResponse() --> (400) Bad request again!
using (var response = request.GetResponse())
{
using (var dataStream = response.GetResponseStream())
{
using (var reader = new StreamReader(dataStream))
{
var responseFromServer = reader.ReadToEnd();
var jsonResponse = JsonConvert.DeserializeObject<OAuth2Response>(responseFromServer);
return jsonResponse.access_token;
}
}
}
}
catch (Exception ex) { var x = ex; }
return null;
}
So, to sum up all my long story:
Is it possible at all to pass OAuth2 authorization using either of methods above from a C# Console Application (without user interaction)?
I've double checked the redirect URI (since I saw a lot of discussed troubles because of it here on stackoverflow) and other parameters like ClientID and ClientSecret. What else I could do wrong in the code above?
Do I need to URL encode a slash in the refresh token (I saw that the first method using client library does it)?
What is the recommended way of achieving my final goal (Purchase API access from ASP.NET web server)?
I'll try to answer your last question. If you access your own data account, you dont need to use client id in oAuth2. Let's use service account to access Google Play API.
Create a service account in Google Developer Console > Your project > APIs and auth > Credentials > Create a new key. You will download a p12 key.
Create a C# project. You can choose console application.
Install google play api library from Google.Apis.androidpublisher. Nuget. You can find other library for dotnet in Google APIs Client Library for .NET
Link google api project with your google play account in API access
Authenticate and try to query information. I'll try with listing all inapp item. You can just change to get purchase's status
String serviceAccountEmail = "your-mail-in-developer-console#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"physical-path-to-your-key\key.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { "https://www.googleapis.com/auth/androidpublisher" }
}.FromCertificate(certificate));
var service = new AndroidPublisherService(
new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "GooglePlay API Sample",
});
// try catch this function because if you input wrong params ( wrong token) google will return error.
var request = service.Inappproducts.List("your-package-name");
var purchaseState = request.Execute();
// var request = service.Purchases.Products.Get(
//"your-package-name", "your-inapp-item-id", "purchase-token"); get purchase'status
Console.WriteLine(JsonConvert.SerializeObject(purchaseState));
You should do the following in your
private static IAuthorizationState GetAuthorization(WebServerClient client) method:
private IAuthorizationState GetAuthorization(WebServerClient client)
{
IAuthorizationState state = AuthState;
if (state != null)
{
return state;
}
state = new AuthorizationState()
{
RefreshToken = "4/lWX1B3nU0_Ya....gAI",
Callback = new Uri(#"https://XXXXX.com/oauth2callback/")
};
client.RefreshToken(state);
// Store and return the credentials.
HttpContext.Current.Session["AUTH_STATE"] = _state = state;
return state;
}
Let me know if it works for you.
Be aware that we know that the whole OAuth2 flow is awkward today, and we are working to improve it.

Categories

Resources