diff --git a/aspnetcore/security/authentication/certauth.md b/aspnetcore/security/authentication/certauth.md index 353c5cd303..ad7d89a42f 100644 --- a/aspnetcore/security/authentication/certauth.md +++ b/aspnetcore/security/authentication/certauth.md @@ -311,6 +311,79 @@ private static byte[] StringToByteArray(string hex) } ``` +If the app is reverse proxied by NGINX with the configuration `proxy_set_header ssl-client-cert $ssl_client_escaped_cert` or deployed on Kubernetes using NGINX Ingress, the client certificate is passed to the app in [URL-encoded form](https://developer.mozilla.org/docs/Glossary/percent-encoding). To use the certificate, decode it as follows: + +::: moniker range=">= aspnetcore-5.0" + +In `Startup.ConfigureServices` (`Startup.cs`): + +```csharp +services.AddCertificateForwarding(options => +{ + options.CertificateHeader = "ssl-client-cert"; + options.HeaderConverter = (headerValue) => + { + X509Certificate2 clientCertificate = null; + + if (!string.IsNullOrWhiteSpace(headerValue)) + { + string certPem = WebUtility.UrlDecode(headerValue); + clientCertificate = X509Certificate2.CreateFromPem(certPem); + } + + return clientCertificate; + }; +}); +``` + +::: moniker-end + +::: moniker range="< aspnetcore-5.0" + +Add the namespace for to the top of `Startup.cs`: + +```csharp +using System.Net; +``` + +In `Startup.ConfigureServices`: + +```csharp +services.AddCertificateForwarding(options => +{ + options.CertificateHeader = "ssl-client-cert"; + options.HeaderConverter = (headerValue) => + { + X509Certificate2 clientCertificate = null; + + if (!string.IsNullOrWhiteSpace(headerValue)) + { + var bytes = UrlEncodedPemToByteArray(headerValue); + clientCertificate = new X509Certificate2(bytes); + } + + return clientCertificate; + }; +}); +``` + +Add the `UrlEncodedPemToByteArray` method: + +```csharp +private static byte[] UrlEncodedPemToByteArray(string urlEncodedBase64Pem) +{ + var base64Pem = WebUtility.UrlDecode(urlEncodedBase64Pem); + var base64Cert = base64Pem + .Replace("-----BEGIN CERTIFICATE-----", string.Empty) + .Replace("-----END CERTIFICATE-----", string.Empty) + .Trim(); + + return Convert.FromBase64String(base64Cert); +} +``` + +::: moniker-end + The `Startup.Configure` method then adds the middleware. `UseCertificateForwarding` is called before the calls to `UseAuthentication` and `UseAuthorization`: ```csharp