Próbuję napisać kod C#, który wysyła żądanie WWW do punktu końcowego usługi REST używanego do obliczania podatku obrotowego w aplikacji internetowej. Jest to usługa stron trzecich i jest zabezpieczona przy użyciu protokołu SSL. Istnieją dwa środowiska, UAT i produkcji. Kod uruchamiający webrequest wygląda następująco:Błąd usługi WebRequest - Nie można utworzyć bezpiecznego kanału SSL/TLS
...
var req = WebRequest.Create(url) as HttpWebRequest;
req.Method = "POST";
req.ContentType = "application/json";
...
using (var webresponse = req.GetResponse())
{
using (var responseStream = new StreamReader(webresponse.GetResponseStream()))
{
var respJson = responseStream.ReadToEnd();
calcResult = BuildResponse(calcRequest, respJson, consoleWriteRawReqResponse);
}
}
return calcResult;
Działa to dobrze w środowisku UAT. Ale kiedy uruchomić ten sam kod przeciwko środowisku produkcyjnym, pojawia się błąd:
„Nie można utworzyć SSL/TLS bezpiecznego kanału”
I am stanie wykonać oba wnioski z Postman bez problemu, bez wszelkie specjalne modyfikacje.
To doprowadziło mnie na ścieżkę bada ten błąd, i znalazłem wiele pomocni, więc posty omawiania tematu, w tym:
The request was aborted: Could not create SSL/TLS secure channel
Could not create SSL/TLS secure channel, despite setting ServerCertificateValidationCallback
te pomogły wskazując mnie w prawo kierunek, który miał przyjrzeć się ustawieniu ustawienia ServicePointManager.SecurityProtocol na inną wartość i użyciu ServicePointManager.ServerCertificateValidationCallback w celu zbadania błędów.
Co znalazłem po graniu z nich jest następujący:
- Wezwanie środowisko UAT będzie działać z ustawieniem domyślnym SSL3 | Tls (domyślnie dla środowiska .NET 4.5.2), podczas gdy środowisko produkcyjne nie.
- Połączenie produkcyjne będzie działać TYLKO po ustawieniu tego ustawienia jawnie na Ssl3.
Ten kod wygląda następująco:
...
var req = WebRequest.Create(url) as HttpWebRequest;
req.Method = "POST";
req.ContentType = "application/json";
...
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CertValidationCallback);
using (var webresponse = req.GetResponse())
{
using (var responseStream = new StreamReader(webresponse.GetResponseStream()))
{
var respJson = responseStream.ReadToEnd();
calcResult = BuildResponse(calcRequest, respJson, consoleWriteRawReqResponse);
}
}
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
return calcResult;
Jest to szczególnie kłopotliwe, ponieważ patrząc na punkty końcowe w przeglądarce internetowej, widzę, że obie są zabezpieczone przez tego samego certyfikatu wieloznacznego i to zarówno używając TLS 1.0.
Spodziewam się, że ustawienie ServicePointManager.SecurityProtocol na TLS będzie działało, ale nie działa.
Naprawdę chcę uniknąć bezpośredniego ustawienia ServicePointManager.SecurityProtocol na SSL3, ponieważ nasza aplikacja jest aplikacją internetową i ma wiele innych punktów integracji, które komunikują się przez SSL. Wszystko działa dobrze i nie chcę wpływać negatywnie na ich funkcjonalność. Nawet jeśli ustawię to ustawienie tuż przed wywołaniem, a następnie zmienię je zaraz po, uruchamiam ryzyko trafienia problemów współbieżności, ponieważ ServicePointManager.SecurityProtocol jest statyczny.
Zbadałem również ten temat i nie spodobało mi się to, co przeczytałem.Istnieją wzmianki o użyciu różnych domen aplikacji:
.NET https requests with different security protocols across threads
How to use SSL3 instead of TLS in a particular HttpWebRequest?
Ale to wydaje się zbyt skomplikowane/hacky do mnie. Czy zajmowanie się tworzeniem domeny aplikacji jest naprawdę jedynym rozwiązaniem? Czy jest to coś, czego po prostu nie powinienem próbować rozwiązywać i zamiast tego podchodzić do właściciela danej usługi? Jest bardzo ciekawy, że zadziała z TLS na jednym środowisku/serwerze, ale nie na drugim.
EDYTOWANIE Zrobiłem trochę więcej zabawy z tym. Zmieniłem mojego klienta do korzystania z podejścia przedstawionego całkiem dobrze w tym blogu (przy użyciu innej aplikacji domenę do izolowania kodu, który zmienia ServicePointManager.SecurityProtocol):
https://bitlush.com/blog/executing-code-in-a-separate-application-domain-using-c-sharp
To rzeczywiście pracował całkiem dobrze, a może być rozwiązaniem z powrotem. Ale dowiedziałem się również, że dostawca danej usługi ma inny punkt końcowy (ten sam URL, inny port), który jest zabezpieczony przy użyciu TLS 1.2. Na szczęście, poprzez rozszerzenie ustawienie moja SecurityProtocol podobnie jak we wniosku Global.asax.cs rozpocząć wydarzenie:
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
jestem w stanie komunikować się z grzywny usług we wszystkich środowiskach. Nie ma to również wpływu na istniejące integracje z innymi usługami (na przykład CyberSource).
Jednak - teraz pojawia się nowe, ale pokrewne pytanie. Dlaczego jest to, że to połączenie działa TYLKO gdy rozszerzam SecurityProtocolType tak jak powyżej? Inne moje integracje, takie jak CyberSource, nie wymagały tego. A jednak ten. Wszystkie wydają się być zabezpieczone przy użyciu TLS 1.2 z tego, co widziałem w przeglądarce.
co jest najlepszym sposobem, aby skontaktować się z Tobą? Czekam na twoją odpowiedź. Dzięki – seoppc