0

Does anyone know the best way to implement accessing a REST service which has a Client Certificate in Xamarin targeting Android?. I'm using .NET Standard 2.0 project for the shared code.

I've tried WebRequestHandler to add the certificate, but mono does not seem to support this and the application won't run. I have also tried HttpClientHandler.

Code snippet below :

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
X509Certificate2 certificate = App.CertificateFile;

var handler = new WebRequestHandler
{
    ClientCertificateOptions = ClientCertificateOption.Manual
};

handler.ClientCertificates.Add(certificate);
var client = new HttpClient(handler);

There is a similar question posted here Using custom SSL client certificates System.Net.HttpClient on Mono but was a while ago so hoping things have improved. My code works correctly from a .Standard 2.0 project if called via a console app, but the same code does not work on the device.

trebor74
  • 133
  • 1
  • 10

1 Answers1

1

You can try AndroidClientHandler and Programatically using androidClientHandler:

AndroidClientHandler clientHandler = new AndroidClientHandler();
Java.Security.Cert.X509Certificate cert = null;
try
{
    CertificateFactory factory = CertificateFactory.GetInstance("X.509");
    using (var stream = Application.Context.Assets.Open("MyCert.pfx"))
    {
        cert = (Java.Security.Cert.X509Certificate)factory.GenerateCertificate(stream);
    }
    } catch (Exception e)
    {
        System.Console.WriteLine(e.Message);
    }
    if (clientHandler.TrustedCerts != null)
    {
        clientHandler.TrustedCerts.Add(cert);
    }
    else
    {
        clientHandler.TrustedCerts = new List<Certificate>();
        clientHandler.TrustedCerts.Add(cert);
    }
    HttpClient client = new HttpClient(clientHandler);

Update:

If the up codes doesn't work, you can try the Android Native implementation, which leverage the same thing as AndroidClientHandler, but are more flexible for use:

var keyStore = KeyStore.GetInstance("PKCS12");
string clientCertPassword = "password_of_certificate";
using (var stream = Application.Context.Assets.Open("cert.pfx"))
{
    keyStore.Load(stream, clientCertPassword.ToCharArray());
}
KeyManagerFactory kmf = KeyManagerFactory.GetInstance("x509");
kmf.Init(keyStore, clientCertPassword.ToCharArray());
IKeyManager[] keyManagers = kmf.GetKeyManagers();
SSLContext sslContext = SSLContext.GetInstance("TLS");
sslContext.Init(keyManagers, null, null);


String result = null;
HttpURLConnection urlConnection = null;
HttpStatus lastResponseCode;
try
{
    URL requestedUrl = new URL("https://10.106.92.42:444");
    urlConnection = (HttpURLConnection)requestedUrl.OpenConnection();
    if (urlConnection is HttpsURLConnection) {
    ((HttpsURLConnection)urlConnection).SSLSocketFactory = sslContext.SocketFactory;
    }
    urlConnection.RequestMethod = "GET";
    urlConnection.ConnectTimeout = 1500;
    urlConnection.ReadTimeout = 1500;

    lastResponseCode = urlConnection.ResponseCode;
    result = ReadFully(urlConnection.InputStream);
    string lastContentType = urlConnection.ContentType;
 }
 catch (Exception ex)
 {
    result = ex.ToString();
 }
 finally
 {
    if (urlConnection != null)
    {
        urlConnection.Disconnect();
    }
 }
Elvis Xia - MSFT
  • 10,801
  • 1
  • 13
  • 24
  • Thanks, Can I run this code from the Standard project or does it need to be from the Android Project? – trebor74 Mar 05 '18 at 09:58
  • It needs to be run from Android project, you need to create a [Dependency Service](https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/dependency-service/) for this. – Elvis Xia - MSFT Mar 05 '18 at 10:00
  • I've done that but am now getting a OPENSSL_internal:WRONG_TAG error. The certificate works OK from a console app so assume difference with Java implementation? – trebor74 Mar 05 '18 at 13:14
  • Please try the codes of my update of the answer. Let me know if any exception is thrown. – Elvis Xia - MSFT Mar 06 '18 at 05:55
  • Thanks, looks promising but I am now getting the following error : _Java.Lang.NullPointerException: Attempt to invoke interface method 'void com.android.okhttp.internal.http.Transport.writeRequestHeaders(com.android.okhttp.Request)' on a null object reference_ when trying to read the ResponseCode – trebor74 Mar 06 '18 at 09:25
  • I didn't use `okhttp`, how does it comes with exception from okhttp? Did you rewrite any codes with `okhttp`? – Elvis Xia - MSFT Mar 06 '18 at 09:32
  • I've checked and everything is https, so not sure why request is saying okhttp. Looks like urlConnection is null. – trebor74 Mar 08 '18 at 11:45
  • DId you enabled the Internet permission of your app? – Elvis Xia - MSFT Mar 09 '18 at 00:50
  • Yes, done that. We're switching authentication methods now, but may revisit later. Thanks for your help. – trebor74 Mar 09 '18 at 09:13