I'm using an online store for user images uploaded with our App secured by SSL. The upload works all well as I'm using the WebClient with the certificate attached. But when I'm trying to use the Xamarin.Forms.Image component e.g. with the Source set to "https://blabla.com/upload/image123.jpg" the image can't be loaded on Android. On iOS this works as I've got a custom NSUrlProtocol which handles the SSL connection.
var image = new Image();
//will use ImageLoaderSourceHandler
image.Source = "https://blabla.com/upload/image123.jpg";
In case of a WebClient I attach the X509Certificate2 (private key and password) to HttpWebRequest.ClientCertificates and it works. But I'm lost on how I can provide that certificate to whatever loading mechanism is behind ImageLoaderSourceHandler.
How can I make this work on Android?
So I ended up setting up my own SecuredUriImageSource:
var image = new Image();
//will use SecuredImageLoaderSourceHandler
image.Source = new SecuredUriImageSource ("https://blabla.com/upload/image123.jpg");
Which uses this custom handler to load the image, while WebClientEx attaches the actual Certificate to the connection.
[assembly: ExportImageSourceHandler(typeof(SecuredUriImageSource), typeof(SecuredImageLoaderSourceHandler))]
namespace Helpers
{
public class SecuredUriImageSource : ImageSource
{
public readonly UriImageSource UriImageSource = new UriImageSource();
public static SecuredUriImageSource FromSecureUri(Uri uri)
{
var source = new SecuredUriImageSource ();
source.UriImageSource.Uri = uri;
return source;
}
}
public class SecuredImageLoaderSourceHandler : IImageSourceHandler
{
public async Task<Bitmap> LoadImageAsync(ImageSource imagesource, Android.Content.Context context, CancellationToken cancelationToken = default(CancellationToken))
{
var imageLoader = imagesource as SecuredUriImageSource;
if (imageLoader != null && imageLoader.UriImageSource.Uri != null)
{
var webClient = new WebExtensions.WebClientEx();
var data = await webClient.DownloadDataTaskAsync(imageLoader.UriImageSource.Uri, cancelationToken).ConfigureAwait(false);
using (var stream = new MemoryStream(data) )
return await BitmapFactory.DecodeStreamAsync(stream).ConfigureAwait(false);
}
return null;
}
}
}