Indy 10 and sslvTLSv1_2 - delphi

I have a website I post to that currently supports TLS v1.1 and TLS 1.2. They will soon only allow TLS ver 1.2 connections. I upgraded Delphi 5 to Indy 10 for this reason.
Currently, I create my components in code and everything works great running 3 threads at a time:
HTTp := TIdHttp.Create(nil);
HTTP.OnSelectAuthorization := HTTPSelectAuthorization;
HTTP.HTTPOptions := [hoInProcessAuth,hoForceEncodeParams,hoKeepOrigProtocol];
HTTP.OnStatus := HTTPStatus;
HTTP.OnWorkEnd := HTTPWorkEnd;
HTTP.Request.ContentType := 'application/x-www-form-urlencoded';
HTTP.ProxyParams.ProxyPort := ProxyPort;
HTTP.ProxyParams.ProxyUsername := ProxyUserName;
HTTP.ProxyParams.ProxyPassword := ProxyPassword;
HTTP.ProxyParams.BasicAuthentication := ProxyBasicAuth;
end;
If UseSSL and (SSL = nil) then
Begin
SSL := TIDSSLIOHandlerSocketOpenSSL.Create(nil);
SSL.SSLOptions.Mode := sslmClient;
SSL.OnGetPassword := SSLGetPassword;
SSL.SSLOptions.Method := sslvTLSv1_2;
HTTP.IOHandler := SSL;
end;
Is there an event that I would tell me exactly what TLS version I am current actually connecting with when sending a post? I don't want there to be a surprise when they finally stop accepting TLS v1.1 connections.
Thanks.

There is no event specifically for that purpose. You would have to query the underlying SSL object directly, such as in the OnStatus event, using the SSL_get_version() function.
However, you are setting the Method to TLS 1.2 exclusively, so that is all Indy will use (as long as you use a version of OpenSSL that supports 1.2, otherwise Indy will silently fallback to 1.0).
On a side note, your UseSSL if block should look more like this:
If UseSSL then
Begin
If (SSL = nil) then
Begin
SSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
SSL.SSLOptions.Mode := sslmClient;
SSL.OnGetPassword := SSLGetPassword;
SSL.SSLOptions.Method := sslvTLSv1_2;
End;
HTTP.IOHandler := SSL;
end;

Here is an example how you can get info about SSL version.
(may need some update as I don't use latest Indy)
Declaration
procedure IdSSLIOHandlerSocketOpenSSLStatusInfoEx(ASender: TObject;
const AsslSocket: PSSL; const AWhere, Aret: Integer; const AType,
AMsg: string);
Assign
SSL.OnStatusInfoEx:=IdSSLIOHandlerSocketOpenSSLStatusInfoEx;
Usage
procedure THttpThread.IdSSLIOHandlerSocketOpenSSLStatusInfoEx(ASender: TObject;
const AsslSocket: PSSL; const AWhere, Aret: Integer; const AType,
AMsg: string);
begin
if AsslSocket.version = TLS1_VERSION then
...
end;

Related

Delphi EIdOSSLUnderlyingCryptoError Exception - SSL3_GET_RECORD Wrong Version Number

I tried using TRESTClient to connect to an HTTPS web service using TLS 1.2. But NO LUCK with sending multipart/form-data.
So now I am trying with Indy. I got this "Wrong Version Number" error.
I think there is nothing wrong with the code since it worked with HTTP.
Probably my Delphi is missing something. What should I install and how?
procedure TForm10.Button2Click(Sender: TObject);
var
HTTP: TIdHTTP;
RequestBody: TStream;
ResponseBody: string;
myssl: TIdSSLIOHandlerSocketOpenSSL;
Input: TIdMultipartFormDataStream;
begin
ResponseBody := '';
try
try
HTTP := TIdHTTP.Create;
try
Input := TIdMultipartFormDataStream.Create;
try
Input.Clear;
Input.AddFormField('Email', 'xx#xx.com.tr');
Input.AddFormField('Password', 'xx');
myssl := TIdSSLIOHandlerSocketOpenSSL.Create(HTTP);
HTTP.IOHandler := myssl;
myssl.SSLOptions.Mode := sslmUnassigned;
myssl.SSLOptions.Method := sslvTLSv1_2;
myssl.SSLOptions.SSLVersions := [sslvTLSv1_2];
HTTP.HTTPOptions := [hoForceEncodeParams];
HTTP.Request.CustomHeaders.FoldLines := False;
ResponseBody := HTTP.Post('https://xxx.com.tr/api/Mobile/MobileLoginControl', Input);
finally
Input.Free;
end;
finally
HTTP.Free;
end;
finally
end;
except
ResponseBody := '"-20"';
end;
end;
The code is fine, though you are enabling only TLS 1.2 on the SSLIOHandler. Maybe the website in question doesn't support TLS 1.2? Try enabling TLS 1.0 and 1.1 as well:
myssl.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
And don't set the SSLOptions.Method at all. Setting the SSLVersions updates the Method and vice versa. So set one or the other, not both.

TIdhttp HTTPS handshake Failed

I am using Indy 10 to connect to a SOAP Web Service. When I run the code with Fiddler running I find the webserver is not negotiating our Handshake. I have attached a screen shot of the Fiddler Inspectors.
We have been able to successfully connect to the server using the shareware application "SoapUI". I have attached a screen shot of the inspectors when using SoapUI.
One thing I notice is SoapUI is using a different version of SSL/TLS. How do I configure Indy to use the same version?
Any other insight as to why my call is failing?
procedure TForm6.Button1Click(Sender: TObject);
var
idhttp: TIdhttp;
IdSSLIOHandlerSocket1 : TIdSSLIOHandlerSocketOpenSSL;
Params : TStringStream;
begin
idhttp := TIdhttp.Create(nil);
IdSSLIOHandlerSocket1 := TIdSSLIOHandlerSocketOpenSSL.create(nil);
idhttp.ProxyParams.ProxyServer := '127.0.0.1';
idhttp.ProxyParams.ProxyPort := 8888 ;
idhttp.Request.ContentType := 'text/xml';
idhttp.Request.CharSet:='utf-8';
idhttp.request.connection := 'keep-alive';
idhttp.Request.CustomHeaders.AddValue('Content-Type','text/xml;charset=UTF-8');
idhttp.Request.CustomHeaders.AddValue('SOAPAction','"http://ws.nemsis.org/SubmitData"');
idhttp.Request.CustomHeaders.AddValue('Host','nemsis.org:443');
idhttp.Request.BasicAuthentication := True;
idhttp.request.username := 'xxxxx';
idhttp.request.password := 'yyyyy';
with IdSSLIOHandlerSocket1 do begin
SSLOptions.Method := sslvTLSv1_2;
SSLOptions.SSLVersions := [sslvTLSv1_2];
SSLOptions.VerifyMode := [];
SSLOptions.VerifyDepth := 2;
end;
with IdHTTP do begin
IOHandler := IdSSLIOHandlerSocket1;
end;
Params := TStringStream.Create(filetostring(ExtractFileDir(ParamStr(0))+'\sample.xml'));
idhttp.Post('https://nemsis.org:443/NemsisV3Validator4/NemsisWsService',Params);
end;
Failed Call:
Successful Call using SoapUI:

Error connecting with SSL using IdHttp Indy

I have a problem in IdHttp using Indy (Delphi).
I try to using IdHttp to post XML in web service SOAP, but dont work. Return "Error connecting with SSL." in IdSSLOpenSSL.Connect#1437 from indy.
My code is simple:
procedure TForm1.Button1Click(Sender: TObject);
var
vRequest : TStringStream;
s : String;
begin
vRequest := TStringStream.Create((Memo1.Lines.Text));
try
IdHTTP1.Host := edHost.Text;
IdHTTP1.Request.ContentLength := length(Memo1.Lines.Text);
IdHTTP1.Request.ContentType := edContentType.Text;
IdHTTP1.Request.CustomHeaders.Text := 'SOAPAction: "removed for safe"'#13#10;
IdHTTP1.request.CacheControl := 'no-cache';
IdHTTP1.Request.AcceptEncoding := edAccept.Text;
IdHTTP1.HTTPOptions := [hoKeepOrigProtocol];
IdHTTP1.ProtocolVersion := pv1_1;
Memo2.Clear;
try
s := IdHTTP1.post(Edit1.Text, vRequest);
Memo2.Lines.Text := s;
except
on e: EIdHTTPProtocolException do begin
Label1.Caption := e.Message;
MEMO2.LINES.TEXT := e.Message;
end;
on e:Exception do begin
Label1.Caption := e.Message;
MEMO2.LINES.TEXT := e.Message;
end;
end;
requestHeaders.Lines.Text := IdHTTP1.Request.RawHeaders.Text;
responseHeaders.Lines.Text := IdHTTP1.Response.RawHeaders.Text;
finally
vRequest.Free;
end;
end;
In exe folder contain libeay32.dll and ssleay32.dll. Any sugestion?
I Used delphi 5, but in delphi 7 is the same problem.
By default, the SSLVersions property of TIdSSLIOHandlerSockOpenSSL is set to enable only TLS 1.0 1. But many websites are starting to phase out TLS 1.0 and only accept TLS 1.1+. So try enabling TLS 1.1 and TLS 1.2 in the SSLVersions property and see if it helps.
1: there is an open ticket in Indy's issue tracker to also enable TLS 1.1 and TLS 1.2 by default.
On a side note, there are some further tweaks you should make to your code:
do not assign any values to TIdHTTP's Host or ContentLength properties. They are populated automatically by TIdHTTP.
if you set the AcceptEncoding property manually, make sure NOT to include deflate or gzip unless you have a Compressor assigned to TIdHTTP, otherwise it will fail to decode a compressed response. You really should not assign anything to AcceptEncoding unless you are prepared to handle custom encodings. The Compressor handles deflate/gzip and TIdHTTP will update AcceptEncoding accordingly if a Compressor is assigned and ready for use.
use the CustomHeaders.Values property to set individual headers, not the CustomHeaders.Text property.
you do not need to catch EIdHTTPProtocolException explicitly, since that exception handler is not doing anything extra that the more generic Exception handler is not doing.
the RawHeaders property is a TStringList descendant, so it is more efficient to use Lines.Assign(RawHeaders) instead of Lines.Text := RawHeaders.Text.
Try this:
procedure TForm1.Button1Click(Sender: TObject);
var
vRequest : TStringStream;
s : String;
begin
IdHTTP1.Request.ContentType := edContentType.Text;
IdHTTP1.Request.CustomHeaders.Values['SOAPAction'] := 'removed for safe';
IdHTTP1.Request.CacheControl := 'no-cache';
IdHTTP1.HTTPOptions := [hoKeepOrigProtocol];
IdHTTP1.ProtocolVersion := pv1_1;
Memo2.Clear;
try
vRequest := TStringStream.Create(Memo1.Lines.Text);
try
s := IdHTTP1.Post(Edit1.Text, vRequest);
finally
vRequest.Free;
end;
Memo2.Lines.Text := s;
except
on e: Exception do begin
Label1.Caption := e.Message;
Memo2.Lines.Text := e.Message;
end;
end;
RequestHeaders.Lines.Assign(IdHTTP1.Request.RawHeaders);
ResponseHeaders.Lines.Assign(IdHTTP1.Response.RawHeaders);
end;

Indy 10 + SSL = works in Windows 7, does not work on XP

I'm using the Indy 10 Http Client (latest SVN build) and a SSL Handler (Delphi 7) to get the content of the https://www.webtide.com/choose/jetty.jsp website.
It works fine on Windows 7 x64 (tested on two systems), but on WindowsXP x86 (tested on 3 systems) the test app simply hangs on TIdHTTP.Get() without the possibility of a recovery (meaning even disconnecting in a worker-procedure/thread does not work!). The test app cannot be recovered and must be closed with the task manager.
The SSL libraries (32bit x86!) are from here: http://slproweb.com/products/Win32OpenSSL.html
but I've tried 5 other versions from different sites, with the same results.
Here is a zip package with source code, compiled executable, and the SSL libraries:
https://www.dropbox.com/s/pd5soxon0qbnnl0/IndyTest.zip
And here is the source code (the form has a button and two memos):
procedure TForm1.Button1Click(Sender: TObject);
var IdHTTP1: TIdHTTP;
sl : TStringList;
SSL1: TIdSSLIOHandlerSocketOpenSSL;
begin
try
try
IdHTTP1 := TIdHTTP.Create(nil);
sl := TStringList.Create;
SSL1 := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
SSL1.SSLOptions.Method := sslvSSLv23;
with IdHTTP1 do
begin
ConnectTimeout := 10 * 1000;
ReadTimeout := 10 * 1000;
IOHandler := SSL1;
Request.UserAgent := 'Mozilla/5.0 (X11; U; Linux i586; en-US; rv:1.7.3) Gecko/20040924 Epiphany/1.4.4 (Ubuntu)';
Memo2.Text := 'connecting...';
Application.ProcessMessages;
Memo1.Text := Get('https://www.webtide.com/choose/jetty.jsp');
Memo1.Lines.Add ('response: '+ResponseText);
Memo2.Text := 'connected or timeout...';
end;
except
On e: Exception do
Memo2.Text := 'Exception: '+e.Message;
end;
finally
IdHTTP1.Free;
SSL1.Free;
sl.Free;
end;
end;
Why does it crash/hang on WindowsXP?
Indy's ConnectTimeout property only applies to the socket API connect() function when establishing the underlying TCP/IP connection. SSL_connect() is called at a later time to initiate the SSL handshake, which is application data and thus is not subject to the ConnectTimeout.
Indy does use its ReadTimeout property to assign socket level read/write timeouts on OpenSSL connections, but only on Vista+ as a workaround for an OpenSSL bug. On XP and earlier, default socket read/write timeouts apply. The ReadTimeout only tells Indy how long to wait when reading data, but it is not applied to the socket itself. If you want to do that, you can do it manually by calling the TIdSocketHandle.SetSockOpt() method after establishing the TCP/IP connection but before beginning the SSL handshake, for example:
procedure TForm1.Button1Click(Sender: TObject);
var
IdHTTP1: TIdHTTP;
SSL1: TIdSSLIOHandlerSocketOpenSSL;
begin
try
IdHTTP1 := TIdHTTP.Create(nil);
try
SSL1 := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP1);
SSL1.SSLOptions.Method := sslvSSLv23;
with IdHTTP1 do
begin
ConnectTimeout := 10 * 1000;
ReadTimeout := 10 * 1000;
IOHandler := SSL1;
OnConnected := IdHTTPConnected;
OnStatus := IdHTTPStatus;
Request.UserAgent := 'Mozilla/5.0 (X11; U; Linux i586; en-US; rv:1.7.3) Gecko/20040924 Epiphany/1.4.4 (Ubuntu)';
Memo1.Text := Get('https://www.webtide.com/choose/jetty.jsp');
Memo1.Lines.Add('response: '+ ResponseText);
Memo2.Text := 'finished...';
end;
finally
IdHTTP1.Free;
end;
except
on e: Exception do
Memo2.Text := 'Exception: ' + e.Message;
end;
end;
procedure TForm1.IdHTTPStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string);
begin
case AStatus of
hsResolving: Memo2.Text := 'resolving...';
hsConnecting: Memo2.Text := 'connecting...';
hsConnected: Memo2.Text := 'connected...';
hsDisconnecting: Memo2.Text := 'disconnecting...';
hsDisconnected: Memo2.Text := 'disconnected...';
end;
Update;
end;
procedure TForm1.IdHTTPConnected(Sender: TObject);
begin
with TIdHTTP(Sender).Socket.Binding do
begin
SetSockOpt(Id_SOL_SOCKET, Id_SO_RCVTIMEO, 10 * 1000);
SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, 10 * 1000);
end;
end;

Seeing Indy Traffic in Fiddler

I think this is an easy question for someone familiar with Indy. I'm using Delphi 2010 and Indy 10. I am trying to get off the ground accessing an SSL web service. I think it will be a lot easier if I can get Fiddler to see my HTTP traffic. I have seen posts on StackOverflow that indicate it's no big thing to get Fiddler to see your Indy traffic, that you just have to configure the port to make it work. My question is how do you do that?
Here is my code so far:
procedure TForm1.Button1Click(Sender: TObject);
var slRequest: TStringList;
sResponse,
sFileName: String;
lHTTP: TIdHTTP;
lIOHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
sFileName := 'Ping.xml';
slRequest := TStringList.Create;
try
slRequest.LoadFromFile(sFileName);
lHTTP := TIdHTTP.Create(nil);
lHTTP.Intercept := IdLogDebug1;
lIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
try
lHTTP.IOHandler := lIOHandler;
sResponse := lHTTP.Post('https://FSETTESTPROD.EDD.CA.GOV/fsetservice', slRequest);
Memo1.Lines.Text := sResponse;
finally
lIOHandler.Free;
end;
finally
slRequest.Free;
end;
end;
Edit: If I don't use the proxy for Fiddler and click the button while Wireshark is running, I get this traffic in Wireshark.
You can set Indy to use the proxy fiddler provides easily by setting the ProxyParams:
try
lHTTP.IOHandler := lIOHandler;
lHTTP.ProxyParams.ProxyServer := '127.0.0.1';
lHTTP.ProxyParams.ProxyPort := 8888;
sResponse := lHTTP.Post('<URL>', slRequest);
Memo1.Lines.Text := sResponse;
finally
lIOHandler.Free;
end;
You should be able to see all traffic in Fiddler then.
Edit: If that does not work you can add a TIdLogDebug component and add it as interceptor (like you did in your question).
The OnReceive and OnSend events contain the complete headers sent and received aswell as the reply data:
procedure TForm10.captureTraffic(ASender: TIdConnectionIntercept;
var ABuffer: TArray<Byte>);
var
i: Integer;
s: String;
begin
s := '';
for i := Low(ABuffer) to High(ABuffer) do
s := s + chr(ABuffer[i]);
Memo1.Lines.Add(s);
end;

Resources