Thực hiện cuộc gọi Https bằng cách sử dụng HttpClient


157

Tôi đã và đang sử dụng HttpClientđể thực hiện các cuộc gọi WebApi bằng C #. Có vẻ gọn gàng & nhanh chóng so với WebClient. Tuy nhiên tôi bị kẹt trong khi thực hiện Httpscuộc gọi.

Làm thế nào tôi có thể thực hiện mã dưới đây để thực hiện Httpscuộc gọi?

HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://foobar.com/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/xml"));

var task = httpClient.PostAsXmlAsync<DeviceRequest>(
                "api/SaveData", request);

EDIT 1: Đoạn mã trên hoạt động tốt để thực hiện các cuộc gọi http. Nhưng khi tôi thay đổi lược đồ thành https thì nó không hoạt động. Đây là lỗi thu được:

Kết nối cơ bản đã bị đóng: Không thể thiết lập mối quan hệ tin cậy cho kênh bảo mật SSL / TLS.

EDIT 2: Thay đổi lược đồ thành https là: bước một.

Làm cách nào để cung cấp chứng chỉ & khóa chung / riêng cùng với yêu cầu C #.


2
bạn đang thực hiện các cuộc gọi https chỉ bằng cách chỉ địnhnew Uri("https://foobar.com/");
felickz

2
Tôi bối rối. Điều đó đã không làm việc? bạn đang nhận được một lỗi? (Chỉnh sửa: Được đăng trước khi OP thay đổi URI từ https thành http)
Tim

Câu trả lời:


214

Nếu máy chủ chỉ hỗ trợ phiên bản TLS cao hơn như TLS 1.2, thì nó vẫn sẽ bị lỗi trừ khi PC khách của bạn được cấu hình để sử dụng phiên bản TLS cao hơn theo mặc định. Để khắc phục vấn đề này, thêm vào sau đây trong mã của bạn.

System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

Sửa đổi mã ví dụ của bạn, nó sẽ là

HttpClient httpClient = new HttpClient();   

//specify to use TLS 1.2 as default connection
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

httpClient.BaseAddress = new Uri("https://foobar.com/");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));

var task = httpClient.PostAsXmlAsync<DeviceRequest>("api/SaveData", request);

@DerekS Rất vui và bạn được chào đón. Nếu vì lý do nào đó, bạn không thể sửa đổi mã trong cài đặt sản xuất nhưng có thể thực hiện một số quản trị viên trên máy chủ, tôi sử dụng tiện ích này để định cấu hình TLS 1.2 làm mặc định. nartac.com/ Products / IISCrypto
Ronald Ramos

SecurityProtocolType.Tls12không thể tìm thấy những giá trị enum mà bạn đã đề cập
JobaDiniz

1
@JobaDiniz sử dụng .NET 4.5 trở lên và bao gồm không gian tên System.Net.
Ronald Ramos

Có phải cài đặt SecurityProtocol chỉ một lần? Giống như lúc khởi động ứng dụng?
CamHart

Điều này làm việc tuyệt vời. Cảm ơn. Tôi cũng thấy rằng tôi không cần phải xóa bất kỳ tiêu đề nào đã sử dụng ứng dụng khách HTTPClient này = new HttpClient (); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtatioType.Tls11 | SecurityProtatioType.Tls; Phản hồi httpResponseMessage = đang chờ khách hàng.GetAsync ("https: // ...");
AtLeastTheresToast

127

Chỉ cần chỉ định HTTPS trong URI.

new Uri("https://foobar.com/");

Foobar.com sẽ cần phải có chứng chỉ SSL đáng tin cậy hoặc các cuộc gọi của bạn sẽ không thành công với lỗi không đáng tin cậy.

Trả lời EDIT: Chứng chỉ ứng dụng khách với httpClient

WebRequestHandler handler = new WebRequestHandler();
X509Certificate2 certificate = GetMyX509Certificate();
handler.ClientCertificates.Add(certificate);
HttpClient client = new HttpClient(handler);

EDIT Trả lời2: Nếu máy chủ bạn đang kết nối đã vô hiệu hóa SSL, TLS 1.0 và 1.1 và bạn vẫn đang chạy .NET framework 4.5 (hoặc bên dưới), bạn cần phải lựa chọn

  1. Nâng cấp lên .Net 4.6+ ( Hỗ trợ TLS 1.2 theo mặc định )
  2. Thêm những thay đổi registry để hướng dẫn 4.5 để kết nối qua TLS1.2 (Xem: Salesforce writeup cho compat và chìa khóa để thay đổi hoặc thanh toán IISCrypto thấy Ronald Ramos trả lời bình luận )
  3. Thêm mã ứng dụng để cấu hình thủ công .NET để kết nối qua TLS1.2 (xem câu trả lời của Ronald Ramos )

@felickz phản ứng tuyệt vời. Có thư viện nào tương đương với thư viện WebRequestHandler cho windows phone 8 không?
Billatron

@Billatron các thư viện khác nhau cho WP / Win8 .. xem
felickz

5
Khi phát triển hoặc xử lý các certs tự ký, bạn có thể bỏ qua các lỗi chứng chỉ không đáng tin cậy bằng cách sau: ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
nhà phát triển

6
GetMyX509Certificate
Fandi Susanto

6
@developer cho phép chỉ hy vọng rằng mã không rơi vào bản dựng sản xuất của bạn :)
felickz

15

Mã của bạn nên được sửa đổi theo cách này:

httpClient.BaseAddress = new Uri("https://foobar.com/");

Bạn chỉ cần sử dụng https:lược đồ URI. Có một trang hữu ích ở đây trên MSDN về các kết nối HTTP an toàn. Thật:

Sử dụng lược đồ https: URI

Giao thức HTTP xác định hai sơ đồ URI:

http: Được sử dụng cho các kết nối không được mã hóa.

https: Được sử dụng cho các kết nối an toàn cần được mã hóa. Tùy chọn này cũng sử dụng chứng chỉ kỹ thuật số và cơ quan cấp chứng chỉ để xác minh rằng máy chủ là người mà nó tuyên bố là.

Hơn nữa, hãy xem xét rằng các kết nối HTTPS sử dụng chứng chỉ SSL. Đảm bảo kết nối an toàn của bạn có chứng chỉ này nếu không các yêu cầu sẽ thất bại.

BIÊN TẬP:

Mã trên hoạt động tốt để thực hiện cuộc gọi http. Nhưng khi tôi thay đổi lược đồ thành https thì nó không hoạt động, hãy để tôi đăng lỗi.

Điều đó có nghĩa là gì không hoạt động? Yêu cầu thất bại? Một ngoại lệ được ném? Làm rõ câu hỏi của bạn.

Nếu các yêu cầu không thành công, thì vấn đề sẽ là chứng chỉ SSL.

Để khắc phục sự cố, bạn có thể sử dụng lớp HttpWebRequestvà thuộc tính của nó ClientCertificate. Hơn nữa, bạn có thể tìm thấy ở đây một mẫu hữu ích về cách thực hiện yêu cầu HTTPS bằng chứng chỉ.

Một ví dụ là như sau (như được hiển thị trong trang MSDN được liên kết trước đó):

//You must change the path to point to your .cer file location. 
X509Certificate Cert = X509Certificate.CreateFromCertFile("C:\\mycert.cer");
// Handle any certificate errors on the certificate from the server.
ServicePointManager.CertificatePolicy = new CertPolicy();
// You must change the URL to point to your Web server.
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://YourServer/sample.asp");
Request.ClientCertificates.Add(Cert);
Request.UserAgent = "Client Cert Sample";
Request.Method = "GET";
HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();

Chính xác, lừa nằm trong việc gửi chứng chỉ cùng với yêu cầu. làm thế nào để làm điều đó?
Abhijeet

Nơi nhận "C: \\ mycert.cer"? Làm cách nào để tạo tập tin mycert.cer?
masiboo

@masiboo Những gì tôi đã viết câu trả lời làm thế nào để thực hiện cuộc gọi HTTPS. Hỏi google về cách tạo tệp * .cer và bạn sẽ tự tìm thấy câu trả lời.
Alberto Solano

9

Khi kết nối với httpstôi cũng gặp lỗi này, tôi đã thêm dòng này trước HttpClient httpClient = new HttpClient();và kết nối thành công:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

Tôi biết nó từ câu trả lời nàymột Anwser tương tự khác và bình luận đề cập:

Đây là một hack hữu ích trong quá trình phát triển, vì vậy, đưa ra tuyên bố #if DEBUG #endif xung quanh nó là điều tối thiểu bạn nên làm để làm cho điều này an toàn hơn và ngăn chặn kết thúc này trong sản xuất

Ngoài ra, tôi đã không thử phương pháp trong Câu trả lời khác sử dụng new X509Certificate()hoặc new X509Certificate2()để tạo Chứng chỉ, tôi không chắc chắn chỉ đơn giản là tạo bởi new()sẽ hoạt động hay không.

EDIT: Một số tài liệu tham khảo:

Tạo chứng chỉ máy chủ tự ký trong IIS 7

Nhập và xuất chứng chỉ SSL trong IIS 7

Chuyển đổi .pfx sang .cer

Thực tiễn tốt nhất để sử dụng ServerCertertValidationCallback

Tôi thấy giá trị của Thumbprint bằng x509certificate.GetCertHashString():

Lấy vân tay của chứng chỉ


6

Tôi gặp vấn đề tương tự khi kết nối với GitHub, đòi hỏi phải có tác nhân người dùng. Do đó, nó là đủ để cung cấp điều này hơn là tạo ra một chứng chỉ

var client = new HttpClient();

client.BaseAddress = new Uri("https://api.github.com");
client.DefaultRequestHeaders.Add(
    "Authorization",
    "token 123456789307d8c1d138ddb0848ede028ed30567");
client.DefaultRequestHeaders.Accept.Add(
    new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add(
    "User-Agent",
    "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");

2
bạn nên thu hồi mã thông báo mà bạn đã chia sẻ với internet, tôi nghĩ GitHub có thể đã tự động làm điều đó.
Amias

21
bạn có thực sự nghĩ rằng mã thông báo của tôi bắt đầu bằng 123456789??
Carlo V. Dango

5

Có một thiết lập phi toàn cầu ở cấp độ HttpClientHandler:

var handler = new HttpClientHandler()
{
    SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls
};

var client = new HttpClient(handler);

Do đó, một cho phép các phiên bản TLS mới nhất.

Lưu ý rằng giá trị mặc định SslProtocols.Defaultthực sự SslProtocols.Ssl3 | SslProtocols.Tls(được kiểm tra .Net Core 2.1 và .Net Framework 4.7.1).


Cảm ơn câu trả lời. Một lưu ý nhỏ: theo tài liệu này: docs.microsoft.com/en-us/dotnet/api/NH Nó yêu cầu ít nhất là khung 4.7.1.
GELR

@GELR vâng, bạn nói đúng. Đoạn mã trên mang lại lỗi thời gian chạy trong .Net 4.6.1. Đã sửa câu trả lời.
stop-cran

Trong các phiên bản trước của .NET Framework, thuộc tính này là riêng tư
JotaBe

Trên Net Core 3.1, đây là tất cả những gì hiệu quả với tôi. Đặt System.Net.ServicePointManager.SecurityProtocol = xxx toàn cầu - hoàn toàn không có tác dụng trong theo dõi gói tin.
Rowan Smith

3

Chỉ cần xác định HTTPS trong URI sẽ thực hiện thủ thuật.

httpClient.BaseAddress = new Uri("https://foobar.com/");

Nếu yêu cầu hoạt động với HTTP nhưng không thành công với HTTPS thì đây chắc chắn là sự cố chứng chỉ . Hãy chắc chắn rằng người gọi tin tưởng nhà phát hành chứng chỉ và chứng chỉ chưa hết hạn. Một cách nhanh chóng và dễ dàng để kiểm tra đó là thử thực hiện truy vấn trong trình duyệt.

Bạn cũng có thể muốn kiểm tra trên máy chủ (nếu là của bạn và / hoặc nếu bạn có thể) rằng nó được đặt để phục vụ các yêu cầu HTTPS đúng cách.


: Tôi có một vấn đề tương tự: Tôi đã thực hiện truy vấn trong trình duyệt và chứng chỉ hợp lệ (cuộc gọi đi qua, được ủy quyền là cấp độ dịch vụ và tất cả đều hoạt động như một bùa mê) nhưng lập trình không hoạt động. Những gì khác có thể được? nếu tôi sử dụng chính xác cùng một chứng chỉ từ trình duyệt "gọi dịch vụ theo cách thủ công" so với từ Ứng dụng web gọi srvice theo chương trình?
Carlos

2

Tôi cũng đã nhận được lỗi:

Kết nối cơ bản đã bị đóng: Không thể thiết lập mối quan hệ tin cậy cho kênh bảo mật SSL / TLS.

... Với ứng dụng nhắm mục tiêu Android Xamarin Forms cố gắng yêu cầu tài nguyên từ nhà cung cấp API yêu cầu TLS 1.3.

Giải pháp là cập nhật cấu hình dự án để trao đổi máy khách http "được quản lý" (.NET) của Xamarin (không hỗ trợ TLS 1.3 như Xamarin Forms v2.5), và thay vào đó sử dụng máy khách gốc Android.

Đó là một dự án đơn giản chuyển đổi trong studio hình ảnh. Xem ảnh chụp màn hình bên dưới.

  • Thuộc tính dự án
  • Tùy chọn Android
  • Nâng cao
  • Danh sách mục
  • Thay đổi "triển khai httpClient" thành "Android"
  • Thay đổi triển khai SSL / TLS thành "TLS gốc 1.2+"

nhập mô tả hình ảnh ở đây


1

Thêm các khai báo dưới đây vào lớp của bạn:

public const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
public const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;

Sau:

var client = new HttpClient();

Và:

ServicePointManager.SecurityProtocol = Tls12;
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 /*| SecurityProtocolType.Tls */| Tls12;

Vui mừng? :)


1

Tôi gặp vấn đề này và trong trường hợp của tôi, giải pháp rất đơn giản: mở Visual Studio với quyền Quản trị viên. Tôi đã thử tất cả các giải pháp trên và nó không hoạt động cho đến khi tôi làm điều này. Hy vọng nó sẽ giúp ai đó tiết kiệm thời gian quý báu.


Vì bạn chưa quen với trang web, bạn có thể không biết rằng nếu câu trả lời có kiểm tra màu xanh lá cây bên cạnh, điều đó có nghĩa là nó đã được OP chấp nhận là giải pháp chính xác cho vấn đề của anh ấy / cô ấy. Trừ khi bạn có thể đưa ra một câu trả lời tốt hơn, đầy đủ hơn, tốt nhất là không nên gửi câu trả lời khác, đặc biệt là về một câu hỏi cũ như vậy.
Sam W

4
Chà, tôi cũng có vấn đề, và câu trả lời được kiểm tra màu xanh lá cây không giúp được gì cả. Nghĩ rằng tôi đưa ra một giải pháp thay thế giúp tôi, nhưng này, tôi sẽ không làm điều đó vào lần tới. Cảm ơn các điểm trừ;) có một ngày tuyệt vời.
Lucian Satmarean

0

Bạn có thể thử bằng cách sử dụng ModernHttpClient NuGet Package: Sau khi tải về gói, bạn có thể thực hiện nó như thế này:

 var handler = new ModernHttpClient.NativeMessageHandler()
 {
     UseProxy = true,
 };


 handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
 handler.PreAuthenticate = true;
 HttpClient client = new HttpClient(handler);

Thật không may, điều này đã không làm việc với tôi. Giải pháp là kích hoạt TLS 1.3 bằng cách chuyển từ máy khách http được quản lý xamarin sang máy khách http gốc Android. Xem câu trả lời của tôi dưới đây.
MSC

0

Tôi đồng ý với felickz nhưng tôi cũng muốn thêm một ví dụ để làm rõ việc sử dụng trong c #. Tôi sử dụng SSL trong dịch vụ windows như sau.

    var certificatePath = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "bin");
    gateway = new GatewayService();
    gateway.PreAuthenticate = true;


    X509Certificate2 cert = new X509Certificate2(certificatePath + @"\Attached\my_certificate.pfx","certificate_password");
    gateway.ClientCertificates.Add(cert);

    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    gateway.UserAgent = Guid.NewGuid().ToString();
    gateway.Timeout = int.MaxValue;

Nếu tôi sẽ sử dụng nó trong một ứng dụng web, tôi chỉ thay đổi việc triển khai ở phía proxy như thế này:

public partial class GatewayService : System.Web.Services.Protocols.SoapHttpClientProtocol // to => Microsoft.Web.Services2.WebServicesClientProtocol

-4

Đối với lỗi:

Kết nối cơ bản đã bị đóng: Không thể thiết lập mối quan hệ tin cậy cho kênh bảo mật SSL / TLS.

Tôi nghĩ rằng bạn cần chấp nhận chứng chỉ vô điều kiện với mã sau

ServicePointManager.ServerCertificateValidationCallback += 
    (sender, cert, chain, sslPolicyErrors) => true;

như Đối lập đã viết trong câu trả lời của mình cho câu hỏi .NET client kết nối với API Web ssl .


4
Đây là bỏ qua xác nhận chứng nhận. Đừng bao giờ làm điều này trong sản xuất.
John Korsnes

Chà @JohnKorsnes, nếu Abhijeet đang truy cập một số máy chủ WebAPI công cộng, tôi không nghĩ rằng anh ta có thể làm được gì nhiều về nó. Đây là trường hợp của tôi.
Nemanja Simović
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.