Nil Pun
Nil Pun

Reputation: 17383

How to pass client certificate using AutoRest client

We are using AutoRest for generating client code based on API Swagger files.

I'm trying to pass client certificate to the API. But noticed that generated client code doesn't accept WebRequestHandler.

Generated code looks like below:

public MyTestApiV1(Uri baseUri, params DelegatingHandler[] handlers) : this(handlers)
        {
            if (baseUri == null)
            {
                throw new ArgumentNullException("baseUri");
            }
            this.BaseUri = baseUri;
        }

I feel like I'm missing something here. Has anyone managed to send client certificate using AutoRest?

Tried this but webRequestHandler is always null:

var webRequestHandler = client.HttpMessageHandlers.First() as WebRequestHandler;
            if (webRequestHandler != null)
            {
                var secretRetrieved = keyVault.GetSecretAsync("my-cert");
                var pfxBytes = Convert.FromBase64String(secretRetrieved.Result);
                // or recreate the certificate directly
                var certificate = new X509Certificate2(pfxBytes);
                webRequestHandler.ClientCertificates.Add(certificate);
            }

Upvotes: 2

Views: 1530

Answers (2)

Badgerspot
Badgerspot

Reputation: 2521

.net Core version

Ivan R's answer led me on the right path but it's a little different for .net core (2.2 at this point in time) as WebRequestHandler is not available in core.

I had to use a pfx file and password in my case. GetNumberPassedIn isn't in the generic Petstore Swagger template but was what I was testing with.

Program.cs:

using System;
using System.Net.Http;

namespace SimpleApi2.Console
{

    class Program
    {
        static void Main(string[] args)
        {
            var certificate = new CertInfo().GetCertFromPfx(Const.PfxPath, Const.PfxPassword);

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

            var petStore = new SwaggerPetstore(client, true);
            petStore.BaseUri = new Uri(Const.PublicUrl);
            var result = petStore.GetNumberPassedIn(135, Const.ApiKey);
            System.Console.WriteLine(result.ToString());
            System.Console.ReadKey();
        }
    }
}

CertInfo.cs:

using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Security;

namespace SimpleApi2.Console
{
    class CertInfo
    {
        internal static byte[] ReadFile(string fileName)
        {
            FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            int size = (int)f.Length;
            byte[] data = new byte[size];
            f.Read(data, 0, size);
            f.Close();
            return data;
        }
        public CertInfo() { }

        public X509Certificate2 GetCertFromPfx(string pfxFilePath, string password)
        {
            try
            {

                byte[] rawData = ReadFile(pfxFilePath);
                var passwordAsChars = password.ToCharArray();

                var securePassword = new SecureString();

                foreach (char c in password)
                    securePassword.AppendChar(c);

                securePassword.MakeReadOnly();

                X509Certificate2 x509 = new X509Certificate2(pfxFilePath, password,
                    X509KeyStorageFlags.UserKeySet);

                WriteCertInfo(x509);

                return x509;
            }
            catch (DirectoryNotFoundException)
            {
                System.Console.WriteLine("Error: The directory specified could not be found.");
                throw;
            }
            catch (IOException)
            {
                System.Console.WriteLine("Error: A file in the directory could not be accessed.");
                throw;
            }
            catch (NullReferenceException)
            {
                System.Console.WriteLine("File must be a .cer file. Program does not have access to that type of file.");
                throw;
            }
        }

        private static void WriteCertInfo(X509Certificate2 x509)
        {
            //Print to console information contained in the certificate.
            System.Console.WriteLine("{0}Subject: {1}{0}", Environment.NewLine, x509.Subject);
            System.Console.WriteLine("{0}Issuer: {1}{0}", Environment.NewLine, x509.Issuer);
            System.Console.WriteLine("{0}Version: {1}{0}", Environment.NewLine, x509.Version);
            System.Console.WriteLine("{0}Valid Date: {1}{0}", Environment.NewLine, x509.NotBefore);
            System.Console.WriteLine("{0}Expiry Date: {1}{0}", Environment.NewLine, x509.NotAfter);
            System.Console.WriteLine("{0}Thumbprint: {1}{0}", Environment.NewLine, x509.Thumbprint);
            System.Console.WriteLine("{0}Serial Number: {1}{0}", Environment.NewLine, x509.SerialNumber);
            System.Console.WriteLine("{0}Friendly Name: {1}{0}", Environment.NewLine, x509.PublicKey.Oid.FriendlyName);
            System.Console.WriteLine("{0}Public Key Format: {1}{0}", Environment.NewLine, x509.PublicKey.EncodedKeyValue.Format(true));
            System.Console.WriteLine("{0}Raw Data Length: {1}{0}", Environment.NewLine, x509.RawData.Length);
            System.Console.WriteLine("{0}Certificate to string: {1}{0}", Environment.NewLine, x509.ToString(true));
        }
    }
}

Upvotes: 0

Ivan R.
Ivan R.

Reputation: 1915

You can use another overloaded constructor:

/// <summary>
/// Initializes ServiceClient using base HttpClientHandler and list of handlers.
/// </summary>
/// <param name="rootHandler">Base HttpClientHandler.</param>
/// <param name="handlers">List of handlers from top to bottom (outer handler is the first in the list)</param>
protected ServiceClient(HttpClientHandler rootHandler, params DelegatingHandler[] handlers)

ServiceClient is the base class for generated clients. Therefore, code might look like:

var secretRetrieved = keyVault.GetSecretAsync("my-cert");
var pfxBytes = Convert.FromBase64String(secretRetrieved.Result);
// or recreate the certificate directly
var certificate = new X509Certificate2(pfxBytes);
WebRequestHandler webRequestHandler = new WebRequestHandler();
webRequestHandler.ClientCertificates.Add(certificate);
var client = new MyTestApiV1(webRequestHandler);
client.BaseUri = baseUri;

Upvotes: 3

Related Questions