Rufat AA
Rufat AA

Reputation: 21

MAUI .NET 7 - How to make a http request with self signed certificate from app

I would like to share my experience of making requests with a self-signed certificate in the MAUI .NET 7 app (specifically for the Android part).

The original problem comes from AndroidMessageHandler.ClientCertificates that are not initialized (null) in MAUI .NET 7 when native AndroidMessageHandler is used.

In application used self-signed certificates with private key generation based on RSA standard functionality that is in .NET 7 (For .NET 6 you could use the BouncyCastle NuGet package to archive that)

I could find only one suggestion without a working solution for that problem https://github.com/xamarin/xamarin-android/issues/7274 (though it was for Xamarin Android, but used like ignition for me)

so in order to make it work here is the steps:

  1. add a custom message handler inherited from AndroidMessageHandler
  2. in the constructor init ClientCertificates and add a self-signed certificate to it
  3. add the following overrides for ConfigureKeyStore, ConfigureKeyManagerFactory, and ConfigureTrustManagerFactory
  4. at ConfigureKeyStore import the self-signed cert and private key into the newly created KeyStore (here you need to make a java certificate from .NET RSA stuff - I could provide code). This is one of the important steps in order to get a certificate in an HTTP request
  5. also you need to provide ServerCertificateCustomValidationCallback at the constructor so you could handle SslPolicyErrors.RemoteCertificateChainErrors because the certificate is self-signed

that's all, if you like I could also place a working code

sharing my experience with developers as there is no working solution as of Match 2023

Upvotes: 2

Views: 2374

Answers (2)

Eli
Eli

Reputation: 444

For Android, this post does it.

For iOS, I do it like this. In Info.plist add

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>YOUR_HOST_HERE</key>
            <dict>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
        </dict>
    </dict>

In the code, do

X509Certificate2 clientCertificate = new X509Certificate2();
try
{
  using(Stream cs = await FileSystem.Current.OpenAppPackageFileAsync(YOUR_PEM_FILE_NAME))
  {
    Byte[] raw = new Byte[cs.Length];
    for (Int32 i = 0; i < cs.Length; ++i)
      raw[i] = (Byte)cs.ReadByte();
     clientCertificate = new X509Certificate2(raw, YOUR_PWD, X509KeyStorageFlags.DefaultKeySet);
  }
}
catch(Exception e)
{
  //whatever
}
var handler = new NSUrlSessionHandler();
handler.TrustOverrideForUrl = (session, url, trust) => 
{
  var uri = new Uri(url);
  var ok = uri.Host switch
    {
      "YOUR_HOST_HERE" => true,
      _ => false
    };
    if (!ok) return false;
    trust.SetAnchorCertificates(new X509Certificate2Collection(clientCertificate));
    NSError error;
    var trustCertificate = trust.Evaluate(out error);
    if (error is not null)
    {
       //whatever
    }
    return trustCertificate;
};
var httpClient = new (handler);
//Do as usual with the httpClient

Note that your .pem file should be in Resources/Raw as a MauiAsset.

Upvotes: 0

H.A.H.
H.A.H.

Reputation: 3907

I have different solution.

  1. Load all certificates from the chain in KeyStore.

  2. Init TrustManagerFactory with the KeyStore.

  3. Init SSLContext with that TrustManagerFactory.

  4. Create new SSLSocketFactory, using that SSLContext. Key part of this process, is the injection of the hostname and address, in the method for creating sockets.

  5. Create Socket, and set additional parameters (Timeout, EnabledProtocols <<< really important, ClientMode, etc...)

  6. Handshake.

Upvotes: 1

Related Questions