5

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?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Florian
  • 465
  • 5
  • 17

4 Answers4

4

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;
        }
    }
}
Florian
  • 465
  • 5
  • 17
2

I followed the settings in https://blog.xamarin.com/securing-web-requests-with-tls-1-2/ and the HTTPS source just starts to load.

Edit:

You can open the property page of the "Android project" --> "Android Options" --> "Advanced" and select HttpClient Implementation to be Managed code, and use Native TLS 1.2+ for the next option

Yuanyi Wu
  • 199
  • 2
  • 9
1

I had to update all Xamarin.Android packages for it to work

Benoit Canonne
  • 445
  • 6
  • 6
0

You can alternatively use StreamImageSource or ImageSource.FromStream(() => ...) and provide your own custom logic for providing the image stream.

var image = new Image();

image.Source = new StreamImageSource() {
     Stream = async (CancellationToken cancellationToken) => {
         Stream stream = //...custom logic using your own HttpClient / WebClient for obtaining the data stream;

         return stream;
     }
}
Ted Chirvasiu
  • 358
  • 1
  • 3
  • 10