โทร Https โดยใช้ HttpClient


157

ฉันใช้HttpClientสำหรับการโทรผ่าน WebApi ด้วย C # WebClientดูเหมือนว่าวิธีที่ประณีตและรวดเร็วเมื่อเทียบกับ อย่างไรก็ตามฉันติดขัดในขณะที่Httpsโทร

ฉันจะHttpsโทรด้านล่างเพื่อโทรออกได้อย่างไร?

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);

แก้ไข 1: รหัสข้างต้นใช้งานได้ดีสำหรับการโทร http แต่เมื่อฉันเปลี่ยนรูปแบบเป็น https มันไม่ทำงาน นี่คือข้อผิดพลาดที่ได้รับ:

การเชื่อมต่อพื้นฐานถูกปิด: ไม่สามารถสร้างความสัมพันธ์ที่เชื่อถือได้สำหรับช่องทางที่ปลอดภัยของ SSL / TLS

แก้ไข 2: การเปลี่ยนรูปแบบเป็น https คือ: ขั้นตอนที่หนึ่ง

ฉันจะจัดหาใบรับรอง & คีย์สาธารณะ / ส่วนตัวพร้อมกับคำขอ C # ได้อย่างไร


2
คุณกำลังโทรหา https เพียงระบุnew Uri("https://foobar.com/");
felickz

2
ฉันสับสน นั่นยังไม่ได้ผลใช่ไหม คุณได้รับข้อผิดพลาดหรือไม่? (แก้ไข: โพสต์ก่อนที่ OP จะเปลี่ยน URI จาก https เป็น http)
Tim

คำตอบ:


215

หากเซิร์ฟเวอร์รองรับเฉพาะรุ่น TLS ที่สูงกว่าเช่น TLS 1.2 เท่านั้นจะยังคงล้มเหลวเว้นแต่ว่าไคลเอ็นต์พีซีของคุณได้รับการกำหนดค่าให้ใช้รุ่น TLS ที่สูงกว่าโดยค่าเริ่มต้น เพื่อเอาชนะปัญหานี้เพิ่มรหัสต่อไปนี้ในของคุณ

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

การปรับเปลี่ยนรหัสตัวอย่างของคุณมันจะเป็น

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);

@ ดีเร็กดีใจและยินดีต้อนรับ หากด้วยเหตุผลบางอย่างคุณไม่สามารถแก้ไขรหัสในการตั้งค่าการผลิต แต่สามารถทำผู้ดูแลระบบบางอย่างบนเซิร์ฟเวอร์ฉันใช้โปรแกรมอรรถประโยชน์นี้เพื่อกำหนดค่า TLS 1.2 เป็นค่าเริ่มต้น nartac.com/Products/IISCrypto
Ronald Ramos

SecurityProtocolType.Tls12ไม่สามารถหาค่า enum ที่คุณพูดถึงได้
JobaDiniz

1
@JobaDiniz ใช้. NET 4.5 ขึ้นไปและรวมเนมสเปซ System.Net
Ronald Ramos

การตั้งค่า SecurityProtocol ต้องเกิดขึ้นเพียงครั้งเดียวหรือไม่ ชอบตอนเริ่มต้นแอปพลิเคชันหรือไม่
CamHart

มันใช้งานได้ดี ขอบคุณ ฉันยังพบว่าฉันไม่จำเป็นต้องล้างส่วนหัวใด ๆ ที่ใช้ไคลเอนต์ HttpClient นี้ = new HttpClient (); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; HttpResponseMessage response = รอ client.GetAsync ("https: // ... ");
AtLeastTheresToast

127

เพียงระบุ HTTPS ใน URI

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

Foobar.com จะต้องมีใบรับรอง SSL ที่เชื่อถือได้หรือการโทรของคุณจะล้มเหลวโดยมีข้อผิดพลาดที่ไม่น่าเชื่อถือ

คำตอบแก้ไข: ใบรับรองลูกค้าด้วย HttpClient

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

แก้ไขคำตอบ 2: หากเซิร์ฟเวอร์ที่คุณกำลังเชื่อมต่ออยู่ได้ปิดการใช้งาน SSL, TLS 1.0 และ 1.1 และคุณยังคงใช้งาน. NET Framework 4.5 (หรือด้านล่าง) คุณต้องเลือก

  1. อัปเกรดเป็น. Net 4.6+ ( รองรับ TLS 1.2 โดยค่าเริ่มต้น )
  2. เพิ่มการเปลี่ยนแปลงรีจิสตรีเพื่อสั่ง 4.5 เพื่อเชื่อมต่อผ่าน TLS1.2 (ดู: การเขียนของ salesforceสำหรับ compat และคีย์เพื่อเปลี่ยนหรือเช็คเอาท์IISCryptoดู Ronald Ramos ตอบความคิดเห็น )
  3. เพิ่มรหัสแอปพลิเคชันเพื่อกำหนดค่า. NET เพื่อเชื่อมต่อผ่าน TLS1.2 เอง (ดูคำตอบ Ronald Ramos )

@felickz การตอบสนองที่ดี มีเทียบเท่ากับ WebRequestHandler library สำหรับ windows phone 8 หรือไม่?
Billatron

@Billatron ไลบรารีที่แตกต่างกันสำหรับ WP / Win8 .. ดู
felickz

6
เมื่อพัฒนาหรือจัดการกับใบรับรองที่เซ็นชื่อด้วยตนเองคุณสามารถเพิกเฉยต่อข้อผิดพลาดใบรับรองที่ไม่น่าเชื่อถือได้ดังต่อไปนี้: ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
นักพัฒนา

6
คือGetMyX509Certificateอะไร
Fandi Susanto

6
@developer ช่วยให้แค่หวังว่ารหัสที่ไม่ลื่นหลุดเข้าไปในของคุณสร้างการผลิต :)
felickz

15

ควรแก้ไขรหัสของคุณด้วยวิธีนี้:

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

คุณต้องใช้https:รูปแบบ URI มีหน้าที่เป็นประโยชน์ที่นี่ใน MSDN เกี่ยวกับการเชื่อมต่อ HTTP ที่ปลอดภัย อันที่จริง:

ใช้รูปแบบ https: URI

โปรโตคอล HTTP กำหนดสองรูปแบบ URI:

http: ใช้สำหรับการเชื่อมต่อที่ไม่ได้เข้ารหัส

https: ใช้สำหรับการเชื่อมต่อที่ปลอดภัยที่ควรได้รับการเข้ารหัส ตัวเลือกนี้ยังใช้ใบรับรองดิจิทัลและผู้ออกใบรับรองเพื่อตรวจสอบว่าเซิร์ฟเวอร์เป็นใคร

นอกจากนี้ให้พิจารณาว่าการเชื่อมต่อ HTTPS ใช้ใบรับรอง SSL ตรวจสอบให้แน่ใจว่าการเชื่อมต่อที่ปลอดภัยของคุณมีใบรับรองนี้มิฉะนั้นการร้องขอจะล้มเหลว

แก้ไข:

รหัสด้านบนทำงานได้ดีสำหรับการโทร http แต่เมื่อฉันเปลี่ยนรูปแบบเป็น https มันไม่ทำงานให้ฉันโพสต์ข้อผิดพลาด

มันหมายความว่าอะไรไม่ทำงาน? คำขอล้มเหลว? มีข้อยกเว้นโยน? ชี้แจงคำถามของคุณ

หากการร้องขอล้มเหลวปัญหาควรเป็นใบรับรอง SSL

ในการแก้ไขปัญหาคุณสามารถใช้คลาสHttpWebRequestและคุณสมบัติClientCertificateได้ นอกจากนี้คุณสามารถหาได้ที่นี่ตัวอย่างที่มีประโยชน์เกี่ยวกับวิธีการที่จะทำให้คำขอ HTTPS โดยใช้ใบรับรอง

ตัวอย่างมีดังต่อไปนี้ (ดังแสดงในหน้า MSDN ที่เชื่อมโยงมาก่อน):

//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();

เคล็ดลับอยู่ที่การส่งใบรับรองพร้อมกับคำขอ ทำอย่างไร
Abhijeet

จะได้รับ "C: \\ mycert.cer" ได้ที่ไหน จะสร้างไฟล์ mycert.cer ได้อย่างไร?
masiboo

@masiboo สิ่งที่ฉันเขียนตอบวิธีการโทร HTTPS ถาม google เกี่ยวกับวิธีสร้างไฟล์ * .cer และคุณจะพบคำตอบด้วยตัวเอง
Alberto Solano

9

เมื่อเชื่อมต่อกับhttpsฉันได้รับข้อผิดพลาดนี้ด้วยฉันเพิ่มบรรทัดนี้มาก่อนHttpClient httpClient = new HttpClient();และเชื่อมต่อได้สำเร็จ:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

ฉันรู้ว่ามันจากคำตอบนี้และอีกอันที่คล้ายกัน Anwserและความคิดเห็นที่กล่าวถึง:

นี่คือแฮ็คที่มีประโยชน์ในการพัฒนาดังนั้นการวางคำสั่ง #if DEBUG #endif ไว้รอบ ๆ เป็นสิ่งที่คุณควรทำเพื่อให้ปลอดภัยและหยุดการสิ้นสุดของการผลิต

นอกจากนี้ฉันไม่ได้ลองวิธีในคำตอบอื่นที่ใช้new X509Certificate()หรือnew X509Certificate2()สร้างใบรับรองฉันไม่แน่ใจเพียงแค่สร้างโดยnew()จะทำงานหรือไม่

แก้ไข: การอ้างอิงบางส่วน:

สร้างใบรับรองเซิร์ฟเวอร์ที่ลงชื่อด้วยตนเองใน IIS 7

นำเข้าและส่งออกใบรับรอง SSL ใน IIS 7

แปลง. pfx เป็น .cer

แนวทางปฏิบัติที่ดีที่สุดสำหรับการใช้ ServerCertificateValidationCallback

ฉันพบว่ามูลค่าของ Thumbprint เท่ากับx509certificate.GetCertHashString():

ดึงรหัสประจำตัวของใบรับรอง


6

ฉันมีปัญหาเดียวกันเมื่อเชื่อมต่อกับ GitHub ซึ่งต้องการตัวแทนผู้ใช้ ดังนั้นจึงเพียงพอที่จะให้สิ่งนี้แทนที่จะสร้างใบรับรอง

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
คุณควรยกเลิกโทเค็นที่คุณแชร์กับอินเทอร์เน็ตฉันคิดว่า GitHub อาจทำเช่นนั้นโดยอัตโนมัติ
Amias

21
คุณคิดว่าโทเค็นของฉันเริ่มต้นด้วยจริงหรือ123456789?
Carlo V. Dango

5

มีการตั้งค่าที่ไม่ใช่ทั่วโลกในระดับHttpClientHandler:

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

var client = new HttpClient(handler);

ดังนั้นจึงเปิดใช้งาน TLS เวอร์ชันล่าสุด

โปรดทราบว่าค่าเริ่มต้นSslProtocols.Defaultเป็นจริงSslProtocols.Ssl3 | SslProtocols.Tls(ตรวจสอบ. Net Core 2.1 และ. Net Framework 4.7.1)


ขอบคุณสำหรับคำตอบ. ข้อควรทราบเล็กน้อย: ตามเอกสารนี้: docs.microsoft.com/en-us/dotnet/api/ ......มันต้องมีกรอบงานอย่างน้อย 4.7.1
GELR

@GELR ใช่คุณพูดถูก รหัสข้างต้นทำให้เกิดข้อผิดพลาดในการทำงานใน. Net 4.6.1 แก้ไขคำตอบ
หยุด

ใน. NET Framework เวอร์ชันก่อนหน้าคุณสมบัตินี้เป็นแบบส่วนตัว
JotaBe

บน Net Core 3.1 นี่คือทั้งหมดที่ใช้ได้สำหรับฉัน การตั้งค่าโกลบอล System.Net.ServicePointManager.SecurityProtocol = xxx - มีผลกระทบเป็นศูนย์ในการติดตามแพ็กเก็ต
Rowan Smith

3

เพียงระบุ HTTPS ใน URI ควรทำการหลอกลวง

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

หากคำขอทำงานร่วมกับ HTTP แต่ล้มเหลวด้วย HTTPS แล้วเป็นส่วนใหญ่แน่นอนปัญหาใบรับรอง ตรวจสอบให้แน่ใจว่าผู้โทรเชื่อถือผู้ออกใบรับรองและใบรับรองนั้นยังไม่หมดอายุ วิธีที่ง่ายและรวดเร็วในการตรวจสอบคือลองทำแบบสอบถามในเบราว์เซอร์

คุณอาจต้องการตรวจสอบเซิร์ฟเวอร์ (ถ้าเป็นของคุณและ / หรือคุณสามารถทำได้) ว่ามีการตั้งค่าให้บริการคำขอ HTTPS อย่างถูกต้อง


: ฉันมีปัญหาที่คล้ายกัน: ฉันทำแบบสอบถามในเบราว์เซอร์และใบรับรองถูกต้อง (โทรผ่านผ่านได้รับอนุญาตเป็นระดับการให้บริการและใช้งานได้อย่างมีเสน่ห์) แต่โปรแกรมไม่ทำงาน มีอะไรอีกบ้าง ถ้าฉันใช้ใบรับรองเดียวกันทั้งหมดจากเบราว์เซอร์ "เรียกใช้บริการด้วยตนเอง" กว่าจากเว็บแอปพลิเคชันที่เรียกใช้ srvice โดยทางโปรแกรมหรือไม่
Carlos

2

ฉันยังได้รับข้อผิดพลาด:

การเชื่อมต่อพื้นฐานถูกปิด: ไม่สามารถสร้างความสัมพันธ์ที่เชื่อถือได้สำหรับช่องทางที่ปลอดภัยของ SSL / TLS

... ด้วยแอปพลิเคชันการกำหนดเป้าหมายAndroid ของ Xamarin Forms Android ที่พยายามขอทรัพยากรจากผู้ให้บริการ API ที่จำเป็นต้องใช้TLS 1.3

วิธีแก้ไขคือการอัพเดทการกำหนดค่าโครงการเพื่อแลกเปลี่ยนไคลเอนต์ http http "XAMININ" ที่ได้รับการจัดการ "(ที่ไม่รองรับ TLS 1.3 ตั้งแต่ Xamarin Forms v2.5) และใช้ไคลเอ็นต์เนทีฟของ Android แทน

มันเป็นโครงการที่เรียบง่ายสลับใน Visual Studio ดูภาพหน้าจอด้านล่าง

  • คุณสมบัติโครงการ
  • ตัวเลือก Android
  • สูง
  • รายการสินค้า
  • เปลี่ยน "การใช้ HttpClient" เป็น "Android"
  • เปลี่ยนการใช้งาน SSL / TLS เป็น "Native TLS 1.2+"

ป้อนคำอธิบายรูปภาพที่นี่


1

เพิ่มการประกาศด้านล่างให้กับชั้นเรียนของคุณ:

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

หลังจาก:

var client = new HttpClient();

และ:

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

มีความสุข? :)


1

ฉันมีปัญหานี้และในกรณีของฉันการแก้ปัญหานั้นง่ายมาก: เปิด Visual Studio ที่มีสิทธิ์ผู้ดูแลระบบ ฉันลองวิธีแก้ปัญหาทั้งหมดข้างต้นแล้ว แต่ก็ไม่ได้ผลจนกว่าฉันจะทำสิ่งนี้ หวังว่ามันจะช่วยประหยัดเวลาอันมีค่าของใครบางคน


เนื่องจากคุณยังใหม่ต่อไซต์คุณอาจไม่ทราบว่าหากคำตอบมีการตรวจสอบสีเขียวข้างๆนั่นหมายความว่า OP ยอมรับว่าเป็นวิธีการแก้ไขปัญหาที่ถูกต้องสำหรับเขา / เธอ ถ้าคุณไม่สามารถให้คำตอบที่ดีกว่าและสมบูรณ์กว่านี้ได้ก็ควรที่จะไม่ส่งคำตอบอื่นโดยเฉพาะคำถามเก่า
Sam W

4
ฉันก็มีปัญหาด้วยเช่นกันและคำตอบตรวจสอบสีเขียวก็ไม่ได้ช่วยอะไรเลย คิดว่าฉันจะให้ทางเลือกอื่นซึ่งช่วยฉัน แต่เดี๋ยวก่อนฉันจะไม่ทำมันในครั้งต่อไป ขอบคุณสำหรับคะแนนลบ;) ขอให้มีความสุขมาก ๆ ในวันนี้
Lucian Satmarean

0

คุณสามารถลองใช้ModernHttpClient Nuget แพคเกจ: หลังจากดาวน์โหลดแพคเกจที่คุณสามารถใช้มันเช่นนี้

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


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

น่าเสียดายที่นี่ใช้งานไม่ได้กับฉัน วิธีแก้ปัญหาคือการเปิดใช้งาน TLS 1.3 โดยการเปลี่ยนจากไคลเอนต์ http ของ xamarin ที่มีการจัดการเป็นไคลเอนต์ http ดั้งเดิมของ Android ดูคำตอบของฉันด้านล่าง
MSC

0

ฉันเห็นด้วยกับ felickz แต่ฉันต้องการเพิ่มตัวอย่างเพื่อชี้แจงการใช้ใน c # ฉันใช้ SSL ในบริการ windows ดังนี้

    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;

ถ้าฉันจะใช้มันในเว็บแอปพลิเคชันฉันแค่เปลี่ยนการใช้งานในด้านพร็อกซีดังนี้:

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

-4

สำหรับข้อผิดพลาด:

การเชื่อมต่อพื้นฐานถูกปิด: ไม่สามารถสร้างความสัมพันธ์ที่เชื่อถือได้สำหรับช่องทางที่ปลอดภัยของ SSL / TLS

ฉันคิดว่าคุณต้องยอมรับใบรับรองโดยไม่มีเงื่อนไขด้วยรหัสต่อไปนี้

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

เป็นOppositionalเขียนไว้ในคำตอบของเขากับคำถามลูกค้า .NET เชื่อมต่อกับ SSL Web API


4
นี่คือการข้ามการตรวจสอบใบรับรอง ไม่เคยทำเช่นนี้ในการผลิต
John Korsnes

@JohnKorsnes ดีถ้า Abhijeet กำลังเข้าถึงเซิร์ฟเวอร์ WebAPI สาธารณะฉันไม่คิดว่าเขาจะทำอะไรได้มากนัก นี่เป็นกรณีของฉัน
Nemanja Simović
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.