Last active
August 17, 2026 05:18
-
-
Save itn3000/61c6bb3d97e61e4fd446dbf2b0a8aca1 to your computer and use it in GitHub Desktop.
how to send HTTPS request with client certificate in .NET 10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System.Net.Security; | |
| using System.Security.Cryptography; | |
| using System.Security.Cryptography.X509Certificates; | |
| using var cts = new CancellationTokenSource(); | |
| await Task.WhenAll(Task.Run(() => | |
| { | |
| Console.ReadLine(); | |
| cts.Cancel(); | |
| }), RequestLoop(args[0], cts.Token)); | |
| Console.WriteLine("Hello, World!"); | |
| async Task RequestLoop(string url, CancellationToken ct) | |
| { | |
| var hoge = Environment.GetEnvironmentVariable("CERT_PASS"); | |
| // if using X509CertificateLoader.LoadPkcs12, 0x8009030D is raised when HttpClient.GetAsync | |
| var clientCerts = X509CertificateLoader.LoadPkcs12Collection(File.ReadAllBytes("client.p12"), hoge, X509KeyStorageFlags.EphemeralKeySet); | |
| var clientCert = clientCerts.First(x => x.HasPrivateKey); | |
| using var clientHandler = new SocketsHttpHandler(); | |
| clientHandler.SslOptions.ClientCertificates = clientCerts; | |
| clientHandler.SslOptions.CertificateRevocationCheckMode = X509RevocationMode.NoCheck; | |
| clientHandler.SslOptions.LocalCertificateSelectionCallback = (sender, target, collection, remoteCert, acceptable) => | |
| { | |
| Console.WriteLine($"target = {target},acceptable = {string.Join("|", acceptable)},remote={remoteCert?.Subject}"); | |
| return clientCert; | |
| }; | |
| clientHandler.SslOptions.RemoteCertificateValidationCallback = (sender, x509, chain, policyError) => | |
| { | |
| if (x509 != null) | |
| { | |
| Console.WriteLine($"remote x509 {x509.Subject}"); | |
| } | |
| return true; | |
| }; | |
| using var client = new HttpClient(clientHandler); | |
| while (!ct.IsCancellationRequested) | |
| { | |
| try | |
| { | |
| using var res = await client.GetAsync(url, ct); | |
| Console.WriteLine($"{res.StatusCode}"); | |
| var str = await res.Content.ReadAsStringAsync(ct); | |
| Console.WriteLine($"response body: '{str}'"); | |
| } | |
| catch (OperationCanceledException) | |
| { | |
| break; | |
| } | |
| catch (Exception e) | |
| { | |
| Console.WriteLine($"failed to request client: {e}"); | |
| } | |
| try | |
| { | |
| await Task.Delay(10 * 1000, ct); | |
| } | |
| catch { } | |
| } | |
| // if X509Certificate2 is imported with not EphemeralKeySet and not disposed, secret key is remaining as garbage file in windows | |
| foreach(var cert in clientCerts) | |
| { | |
| try | |
| { | |
| cert.Dispose(); | |
| } catch {} | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment