certificate in httpclinet

برای ارسال درخواست HTTP ، با استفاده از یک URL استفاده می شود.HttpClient می تواند برای ساخت درخواست های Web API در برنامه Console ، برنامه Winform ، برنامه Web form ، Windows store Applicatio و... استفاده شود..سوال اصلی اینه که کاربردش چیه ؟وقتی یه درخواست از کلاینت ( مثلا وقتی کاربر در صفحه مرورگر محصولیو انتخواب میکنه ، خب این درخواست میاد سمت سرور و خب مشخصا ، قبلش این کاربر log in کرده و هم authaticate شده و هم authorize شده )اما وقتی دو تا سرویس داریم ( هر دو سمت بک اند هستند ) ، مثلا یک سرویس مسول اینه که درخواست کاربرو بگیره بعدش مثلا بیاد بریزتش تو یه سرویس دیگه برای ذخیره سازی مثل kafka بعد از این دوباره اون سرویس درخواستو میگیره و این بار میده به سرویس دیگه مثلا vdb . حالا وقتی درخواست بین دو تا سرویس هست که جفتشون هم بک اند مون هستند پس برای این که این مطمین بشیم این دو سرویس همو میشناسن و کسی نمیتونه خودشو جای یکی از اینا بزاره و هکمون کنه میایم از این قابلیت استفاده میکنیمتنظیمات سمت سرور ؟ یکی از این سرویس ها میشه سرورمون تو این سناریو ، یعنی سرویسی که api اونجا قرار داره و در دات نت این تنظیماتو براش باید انجام بدیم میایم در program از طریق kestrel این تنظیماتو بهش اضافه میکنیم که در اینجا certificatePath و certificateUtilities.pfxPassword هم می تونیم از appsetting بخونیم یا دستی بهش بدیمbuilder.Services.Configure<KestrelServerOptions>(options => options.ConfigureHttpsDefaults(listenOptions => { listenOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate; listenOptions.AllowAnyClientCertificate(); listenOptions.ServerCertificate = new X509Certificate2(certificatePath, CertificateUtilities.PfxPassword); }));داخل program یه کار دیگه ام باید بکنیم ، اونم اینه که اول باید فایل cert بسازیم در محلی که بهش میگیم که در نهایت در تنظیمات بالا بفرستیمش سمت کلاینت ، پس می تونیم این خطو به program اضافه کنیم CertificateUtilities.GenerateCertificate(certificatePath);که میاد یه تابع رو فراخوانی میکنه که یه جای دیگه اونو ساختیم (هر جا خواستید میتونید این تابعو بزارید)namespace myNameSpace;using System.Security.Cryptography.X509Certificates;using System.Security.Cryptography;public static class CertificateUtilities{ public const string PfxPassword = "MyPassword "; public static void GenerateCertificate(string certPath) { if (string.IsNullOrEmpty(certPath)) { میتونیم مثلا یه لاگ بزاریم اینجا } if (!File.Exists(certPath)) { GenerateCertificate(certPath, PfxPassword); if (!File.Exists(certPath)) { throw new ArgumentException($"Could not create new certificate in: {certPath}"); } } } private static void GenerateCertificate(string path, string password) { using var rsa = RSA.Create(2048); var req = new CertificateRequest("C=IR/CO=Amnpardaz CO./Team=Vishkav/App=VDB", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1); req.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, true)); req.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(req.PublicKey, false)); var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1)); var bytes = cert.Export(X509ContentType.Pfx, password); File.WriteAllBytes(path, bytes); }}لازم به ذکر است با انجام این تنظیمات در سمت سرور ( سمتی که api وجود دارد ) ، تنها فایل cert را ساخته ایم و اماده جهت ارسال به کلاینت کرده ایم .حال نکته ای که وجود دارد هنگامی که کلاینت به ما درخواستی ارسال می کند ، ما نیز باید در سمت سرور از درستی certificate کلاینت مطمین شویم ، پس این کار را مانند مثال زیر در بدنه خود api انجام می دهیم : [HttpGet("{Storage}/{Id}")] public IActionResult GetFile([FromServices] GetFileCommand command, [FromRoute] FileRequest request, CancellationToken cancellationToken) { var cert = this.HttpContext.Connection.ClientCertificate?.Thumbprint.ToString(); if (cert == null || this.options.Value.CertThumbprint != cert) { return new UnauthorizedResult(); } return command.Run(request, cancellationToken); }2. تنظیمات سمت کلاینت : بعد از اینکه سمت سرور کامل تنظیماتشو انجام دادیم ( ساخت یه فایل cert در ابتدای کار ، رمز نگاریش , و ارسالش به هر کی که به سرور دخواست بده )حالا میرسیم به تنظیمات مربوط که کلاینت ، یعنی سرویسی که سمت بک اند هست و ارسال درخواست میده (post / delete/ get / ... ) به سمت سرور میایم یه کلاس می سازیم و این تنظیماتو انجام میدیمnamespace ..... ;using System.Security.Cryptography.X509Certificates;using Microsoft.Extensions.Options;public class HttpclientService{ private readonly ILogger<HttpclientService> logger; private readonly IOptions<.......> options; public HttpclientService(ILogger<HttpclientService> logger, IOptions<......> options, ) { this.logger = logger; this.options = options; } public HttpClient HttpClient() { var handler = new HttpClientHandler(); var pss = CertificateUtilities.PfxPassword; var clientCertificate = ConvertFromPfx(this.options.Value.CertificatePath, pss); ّFile.TryWriteAllTextToFile("ThumbPrint.txt", clientCertificate.Thumbprint, CancellationToken.None); handler.ClientCertificates.Add(clientCertificate); handler.ClientCertificateOptions = ClientCertificateOption.Manual; handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { if (cert?.Thumbprint == this.options.Value.VishkadehServerCertThumbprint) { return true; } return false; }; var client = new HttpClient(handler) { }; var uriString = this.options.Value.VishkadehURL ?? ""; if (string.IsNullOrEmpty(uriString)) { throw new ArgumentException("......."); } if (!uriString.EndsWith("/", StringComparison.Ordinal)) { uriString += "/"; } client.BaseAddress = new Uri(uriString); return client; } private static X509Certificate2 ConvertFromPfx(string pfx, string password) { var clientCertificate = new X509Certificate2(pfx, password, X509KeyStorageFlags.Exportable); return clientCertificate; }}و در نهایت تو هر کلاسی که خواستیم ، خیلی راحت میتونیم درخواست رو ارسال کنیمawait this.client.PostAsync("path", content, token)