Xamarin PCL self signed certificat for Android and IOS - xamarin

I'm trying to pass my full Rest service from http to https.
I created a self-signed certificate, I had it to IIS Express. I validate it on google Chrome and it work perfectly fine with postman. My rest service work in http and https.
I use a PCL project (IOS and Android) everything is working fine with http request but I have exception with https request. the exception message is null.
I tried to create a test certificate directly from Visual Studio 2015 but the button is disabled in properties ->Signing.
I also tried to install my self-signed certificate as a Trusted Root but no success for the communication between my simulator and my rest Service.
my code
public partial class MainPage : ContentPage
{
private string url = string.Empty;
private HttpClient _client;
public MainPage()
{
InitializeComponent();
switch (Device.RuntimePlatform)
{
case Device.iOS:
_client = new HttpClient(new NSUrlSessionHandler());
break;
case Device.Android:
_client = new HttpClient();
break;
}
_client = new HttpClient();
test();
}
private async void test()
{
//url = "http://192.168.1.106:9630/PrototypeB.svc/Test";
url = "https://192.168.1.106:44301/PrototypeB.svc/Test";
try
{
var _content = await _client.GetStringAsync(url);
List<Test> _posts = JsonConvert.DeserializeObject<List<Test>>(_content);
}
catch (HttpRequestException e)
{
string test = e.Message;
}
catch (Exception e)
{
string test = e.Message;
}
}
}
How can I communicate with my Android and IOS Simulator with https and self-signed certificate?

You can use ServicePointManager to ignore the certificate validation check.
Execute the code in your iOS and Android platforms like this:
System.Net.ServicePointManager.ServerCertificateValidationCallback += (se, cert, chain, sslerror) => {
return true;
};
References:
Untrusted HTTPS
certificate
HTTPS ignore
certificate
Ignore SSL certificate errors in Xamarin.Forms (PCL)
SSL Validation in
PCL
Also, ModernHttpClient Pro provide this feature, but it is not free.

Related

connect to a server with a self-signed certificate

Ive been doing some research about how to consume a web api executed on my localhost throught a xamarin app. The web api works perfect, I can adding and getting the data to/from my sql server using a web browser but if I try to connect xamarin to it Ive always received authentication error (Mono.Btls.MonoBtlsException: Ssl error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED). Im basically doind this. I followed this post:
http://xamarininterviewquestion.blogspot.com/2019/06/ssl-certificate-and-public-key-pinning.html
So as it definitly didnt work, Id like to try another way;
Set TLSConfig DangerousAcceptAnyServerCertificateValidator to true. Because for know Im happy if Im able to test it.
Thats cool but as Im not a pro I have no idea about implement this;
var httpHandler = new HttpClientHandler();
// Return `true` to allow certificates that are untrusted/invalid
httpHandler.ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
Ive got "DangerousAcceptAnyServerCertificateValidator" doesnt have a definition.
Thank you all in advance and sorry if Ive not been clear.
DangerousAcceptAnyServerCertificateValidator isn't applicable to Xamarin platforms, according to its documentation.
But literally you can write the same code like,
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
HttpClient client = new HttpClient(handler);
https://stackoverflow.com/a/64741829/11182
if you are using Refit then you can do this
public HttpClient PreparedClient()
{
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback += (sender, cert, chain, sslPolicyErrors) => { return true; };
HttpClient client = new HttpClient(handler) { BaseAddress = new Uri(EndpointConstants.BaseUrl) };
return client;
}
private T RefitApi<T>() => RestService.For<T>(PreparedClient());
and if you need to specify settings you can do this
private T RefitApiWithToken<T>() => RestService.For<T>(PreparedClient(), refitSettings);
When using Refit for Xamarin forms
public HttpClient PreparedClient()
{
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback += (sender, cert, chain, sslPolicyErrors) => { return true; };
HttpClient client = new HttpClient(handler) { BaseAddress = new Uri(EndpointConstants.BaseUrl) };
return client;
}
var apiResponse = RestService.For<T>(PreparedClient());

Xamarin Forms: System.Net.Http.HttpClient connect via https and ServerCertificateValidationCallback not hitted

In Xamarin Forms app I am using System.Net.Http.HttpClient to establish connection to server via https. Visual Studio version 16.5.4, Xamarin Forms version 4.5.0.617, android: target framework: Android 9.0 (Pie), iOS: SDK version 13.4. I want to accept only one certificate that comes from CA. Just after start, before first request, I am validating server certificate by:
private const string SupportedPublicKey = "118SDD782...HA4JD";
public static void SetUp()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertficate;
}
private static bool ValidateServerCertficate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
var certKey = certificate?.GetPublicKeyString();
return SupportedPublicKey == certificate?.GetPublicKeyString();
}
Program is hitting breakpoint at SetUp method, but the breakpoint inside event is never hitted. I have put there Console.WriteLine() there methods to check if debugger is broken, but console is clear, so program never reach that code.
Right now application on both platforms, on emulators and real devices, behaves like it accepts all certificates, no matter where they come from and connect to other servers via https.
I have tried to change project properties on android: HttpClient implementation from "default" to "Managed" and "android" and on iOS: from "managed(default)" to "NSUrlSession (iOS 7+)" and "CFNetwork (iOS 6+)" to but there is no effect.
How can I fix it?
Try to change your code to use the new HttpClientHandler.ServerCertificateCustomValidationCallback APIs from .NET Core.
public static void SetUp()
{
HttpClientHandler httpClientHandler = new HttpClientHandler();
httpClientHandler.ServerCertificateCustomValidationCallback = ValidateServerCertficate;
}
private static bool ValidateServerCertficate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
var certKey = certificate?.GetPublicKeyString();
return SupportedPublicKey == certificate?.GetPublicKeyString();
}
You could refer this on github

Xamarin .Net Core HttpClientHandler Method Not Implemented(VS for Mac)

I am writing a .net core(Standard 1.6) library that connects to my WebAPI. The WebApi requires a client certificate.
The .net core library is something being called from a Xamarin iOS app.
I cannot for the life of me send an HTTP request with a Client Certificate header.
I can use the library and post to the API with a client certificate from Visual Studio 2017 on a windows machine.
When I move the same project into my Xamarin iOS app using VS for Mac I get:
"Method Not Implemented" when setting the SslProtocol or adding the client certificate:
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.SslProtocols = SslProtocols.Tls12;
handler.ClientCertificates.Add(new X509Certificate2(certificate));
Relevant libraries:
using System.Net;
using System.Net.Http;
using System.Security;
using System.Security.Cryptography.X509Certificates;
Any help would be greatly appreciated.
Alright, this took me quite a while but i was able to send a client certificate in a web request to use for client auth on our server.
First, as awesome as Xamarin and .netCore are, they are missing alot of the methods .net developers are used to. I was not able to build a crossplatform request that would work on both Android and ios such as the HttpWebRequest.
For ios, i created a custom class that inherits from:NSUrlConnectionDataDelegate
I then override the:
public override void WillSendRequestForAuthenticationChallenge(NSUrlConnection
connection, NSUrlAuthenticationChallenge challenge)
{
byte[] cert = System.IO.File.ReadAllBytes("clientCertificate.pfx");
NSUrlCredential credential = iSecurity.ImportPK12File(cert, "certPassword");
challenge.Sender.UseCredential(credential, challenge);
}
I then created a class that returns the credential:
//cert is a byte array of a .pfx file included in the resource file
//iSecurity Custom class
NSUrlCredential credential = iSecurity.ImportPK12File(cert, "certpassword");
public static NSUrlCredential ImportPK12File(byte[] fileBytes, string passPhrase)
{
var cert = new X509Certificate2(fileBytes, passPhrase);
var options = NSDictionary.FromObjectAndKey(NSObject.FromObject(passPhrase), SecImportExport.Passphrase);
NSDictionary[] importStatus;
SecStatusCode statusCode = SecImportExport.ImportPkcs12(fileBytes, options, out importStatus);
if(statusCode != SecStatusCode.Success){
throw new Exception("Error importing certificate. ");
}
NSObject obj = importStatus[0]["trust"];
IntPtr secTrustRef = obj.Handle;
var identityHandle = importStatus[0][SecImportExport.Identity];
var identity = new SecIdentity(identityHandle.Handle);
var certificate = new SecCertificate(cert.GetRawCertData());
SecCertificate[] certificates = { certificate };
return NSUrlCredential.FromIdentityCertificatesPersistance(identity, certificates, NSUrlCredentialPersistence.ForSession);
}
You may also be able to override this method and send the creds:
public override void ReceivedAuthenticationChallenge(NSUrlConnection connection, NSUrlAuthenticationChallenge challenge)
{
base.ReceivedAuthenticationChallenge(connection, challenge);
}
And i may move it to there but in order to fire this off you create the delegate of your class that inherits from :NSUrlConnectionDataDelegate
and add this to your connection. Any request fired through this connection will override the method and pass the certificate.

System.MissingMethodException Method 'System.Net.Http.HttpClientHandler.set_Proxy' not found

This is a Xamarin solution and I am getting the error found in this message's title. Of course, I can easily confirm that there is a Proxy property on HttpClientHandler in the PCL project. And the solution builds without error. Only when I run does it produce this error (on either Droid or iOS) and does so at the point where it invokes the method in the PCL which instantiates the HttpClient. Note that it doesn't even get to that method. The error appears on the application start-up method; e.g., UIApplication.Main()
If I comment out the handler and instantiate HttpClient without a handler, it works fine as long as I'm on the open internet. But I'm trying to get this to work from behind a proxy.
Further investigation showed that the device projects had no references to System.Net.Http. So I added these -- and it indicates Xamarin.iOS and Xamarin.Android as the packages -- but it still produces the error.
I'm not clear what the error is telling me but I believe it means that the device project can't see System.Net.Http.HttpClientHandler?
private HttpClient GetHttpClient()
{
WebProxy proxy = new WebProxy(ProxyConfig.Url)
{
Credentials = new NetworkCredential(ProxyConfig.Username, ProxyConfig.Password)
};
// At runtime, when GetHttpClient is invoked, it says it cannot find the Proxy setter
HttpClientHandler handler = new HttpClientHandler
{
Proxy = proxy,
UseProxy = true,
PreAuthenticate = true,
UseDefaultCredentials = false,
};
HttpClient client = new HttpClient(handler);
// This works when not behind a proxy
//HttpClient client = new HttpClient();
return client;
}
public async Task GetWeatherAsync(double longitude, double latitude, string username)
{
// MissingMethodException is thrown at this point
var client = GetHttpClient();
client.BaseAddress = new Uri(string.Format("http://api.geonames.org/findNearByWeatherJSON?lat={0}&lng={1}&username={2}", latitude, longitude, username));
try
{
var response = await client.GetAsync(client.BaseAddress);
if (response.IsSuccessStatusCode)
{
var JsonResult = response.Content.ReadAsStringAsync().Result;
var weather = JsonConvert.DeserializeObject<WeatherResult>(JsonResult);
SetValues(weather);
}
else
{
Debug.WriteLine(response.RequestMessage);
}
}
catch (HttpRequestException ex)
{
Debug.WriteLine(ex.Message);
}
catch (System.Net.WebException ex)
{
Debug.WriteLine(ex.Message);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
Add the Microsoft.Net.Http NuGet package to your platform project too. If you run into an issue adding this, try installing the latest Microsoft.Bcl.Build package first. Then, after that is installed, add the HTTP package.

HttpWebRequest and WebClient returning NotFound on Windows Phone 7 but not i normal console application

I'm trying to download a regular JSON string from this url https://valueboxtest.lb.dk/mobile/categories from a Windows Phone 7 Application.
I have tried to both use WebClient and HttpWebRequest. They both throw an exception
“The remote server returned an error: NotFound”
This is the code for using the WebClient
var webClient = new WebClient();
webClient.DownloadStringCompleted += (client_DownloadStringCompleted);
webClient.DownloadStringAsync(new Uri("https://valueboxtest.lb.dk/mobile/categories"));
The eventhandler then just show the content, but e.Result throws the above mentioned exception:
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null && !e.Cancelled) MessageBox.Show(e.Result);
}
For the HttpWebRequest my code looks as follows:
var httpReq = (HttpWebRequest)WebRequest.Create(new Uri("https://valueboxtest.lb.dk/mobile/categories"));
httpReq.BeginGetResponse(HTTPWebRequestCallBack, httpReq);
With the following callback:
private void HTTPWebRequestCallBack(IAsyncResult result)
{
var httpRequest = (HttpWebRequest)result.AsyncState;
var response = httpRequest.EndGetResponse(result);
var stream = response.GetResponseStream();
var reader = new StreamReader(stream);
this.Dispatcher.BeginInvoke(
new delegateUpdate(update),
new Object[] { reader.ReadToEnd() }
);
}
And with the delegate method
delegate void delegateUpdate(string content);
private void update(string content)
{
MessageBox.Show(content);
}
Running it in a console application
Everything works just fine and the JSON string is returned with no problems and I am able to print the result to the console.
Different URL does work on WP7
The weird thing is that the URL http://mobiforge.com/rssfeed actually works fine in both of the above mentioned scenarios.
This issue occurs both in the Emulator and on an actual device.
What could be wrong? Is the REST service returning the data in misbehaving way? I really hope you can help me!
Note: I'm not running Fiddler2 at the same time!
The reason is because that site does not have a valid certificate. Just try it on Mobile Internet Explorer and you'll get the prompt about an issue with the certificate.
How to ignore SSL certificates
Mobile devices are stricter when it comes to SSL certificates.
If you want to get this app into a production environment, you'll either need to write a wrapper for this server (if it's not your own), or get a valid certificate. In the short-term, for testing, you can add a certificate into your device.
Here's a tool which might help you install a certificate.

Resources