mohammad mhammadi
mohammad mhammadi
خواندن ۴ دقیقه·۲ سال پیش

certificate in httpclinet

  • برای ارسال درخواست HTTP ، با استفاده از یک URL استفاده می شود.HttpClient می تواند برای ساخت درخواست های Web API در برنامه Console ، برنامه Winform ، برنامه Web form ، Windows store Applicatio و... استفاده شود.
  • .سوال اصلی اینه که کاربردش چیه ؟

وقتی یه درخواست از کلاینت ( مثلا وقتی کاربر در صفحه مرورگر محصولیو انتخواب میکنه ، خب این درخواست میاد سمت سرور و خب مشخصا ، قبلش این کاربر log in کرده و هم authaticate شده و هم authorize شده )

اما وقتی دو تا سرویس داریم ( هر دو سمت بک اند هستند ) ، مثلا یک سرویس مسول اینه که درخواست کاربرو بگیره بعدش مثلا بیاد بریزتش تو یه سرویس دیگه برای ذخیره سازی مثل kafka بعد از این دوباره اون سرویس درخواستو میگیره و این بار میده به سرویس دیگه مثلا vdb . حالا وقتی درخواست بین دو تا سرویس هست که جفتشون هم بک اند مون هستند پس برای این که این مطمین بشیم این دو سرویس همو میشناسن و کسی نمیتونه خودشو جای یکی از اینا بزاره و هکمون کنه میایم از این قابلیت استفاده میکنیم

  • تنظیمات سمت سرور ؟

یکی از این سرویس ها میشه سرورمون تو این سناریو ، یعنی سرویسی که api اونجا قرار داره و در دات نت این تنظیماتو براش باید انجام بدیم

  1. میایم در 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)


سمت سرورسمت کلاینتhttpclient
شاید از این پست‌ها خوشتان بیاید