Watch, Follow, &
Connect with Us

Please visit our new home
community.embarcadero.com.


Welcome, Guest
Guest Settings
Help

Thread: https -> HTTPClient requirement for Client Certificate


This question is not answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 3 - Last Post: Jan 26, 2016 4:38 PM Last Post By: wise info
Martin Waller

Posts: 12
Registered: 10/12/14
https -> HTTPClient requirement for Client Certificate  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 18, 2015 3:12 AM
Hi,

I'm trying to access a simple https site. In C# I can use the following code:

WebRequest request = WebRequest.Create("<my URL>");
WebResponse response = request.GetResponse();
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();

this all works as expected and returns me a page of data. Note that I do not provide a certificate.

In Delphi I'm using the following:

client := THTTPClient.Create;
request := client.GetRequest(sHTTPMethodGet,'Mmy URL>');
response := client.Execute(request);

and I get an unspecified certificate from client error. The error seems to be raised here:

procedure THTTPClient.DoNeedClientCertificate(const LRequest: THTTPRequest; const LClientCertificateList: TCertificateList);
var
LClientCertificateIndex: Integer;
begin
LClientCertificateIndex := -1;
if Assigned(FNeedClientCertificateCallback) or Assigned(FNeedClientCertificateEvent) then
begin
DoGetClientCertificates(LRequest, LClientCertificateList);
if LClientCertificateList.Count = 0 then
raise ENetHTTPCertificateException.CreateRes(@SNetHttpEmptyCertificateList)
else if Assigned(FNeedClientCertificateCallback) then
FNeedClientCertificateCallback(Self, LRequest, LClientCertificateList, LClientCertificateIndex)
else
FNeedClientCertificateEvent(Self, LRequest, LClientCertificateList, LClientCertificateIndex);
end;
if LClientCertificateIndex < 0 then
raise ENetHTTPCertificateException.CreateRes(@SNetHttpUnspecifiedCertificate) <-- ******* HERE *****
else
if DoClientCertificateAccepted(LRequest, LClientCertificateIndex) = False then
raise ENetHTTPCertificateException.CreateRes(@SNetHttpRejectedCertificate);
end;

If you look at https://tools.ietf.org/html/rfc5246#page-55 it says:

7.4.6. Client Certificate

When this message will be sent:

This is the first message the client can send after receiving a
ServerHelloDone message. This message is only sent if the server
requests a certificate. If no suitable certificate is available,
the client MUST send a certificate message containing no
certificates. That is, the certificate_list structure has a
length of zero. If the client does not send any certificates, the
server MAY at its discretion either continue the handshake without
client authentication, or respond with a fatal handshake_failure
alert. Also, if some aspect of the certificate chain was
unacceptable (e.g., it was not signed by a known, trusted CA), the
server MAY at its discretion either continue the handshake
(considering the client unauthenticated) or send a fatal alert.

So two questions I guess...

1) Why do I need to send a certificate using Delphi and not C#
2) Does anyone have a simple example of how to get this to work with no client certificate?

Many thanks

Martin

Eli M

Posts: 1,346
Registered: 11/9/13
Re: https -> HTTPClient requirement for Client Certificate  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 18, 2015 5:23 AM   in response to: Martin Waller in response to: Martin Waller
What platform is this on?

I assume this is an HTTPS URL?

Is there a flag you can set to tell the Delphi component to not check for a valid certificate?

Does the server HAVE a valid certificate?

Is that using Indy or TNetHTTPClient?
Martin Waller

Posts: 12
Registered: 10/12/14
Re: https -> HTTPClient requirement for Client Certificate  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 18, 2015 6:56 AM   in response to: Eli M in response to: Eli M
Digging deeper this is a difference between XE7 and XE8. Take the code below

RestClient := TRestClient.Create(DownloadUrl);

RestRequest := TRestRequest.Create(nil);
RestRequest.Client := RestClient;
RestRequest.Method := TRestRequestMethod.rmGet;
RestRequest.URLAlreadyEncoded := True;
RestRequest.Accept := 'text/plain, text/html';

RestRequest.Execute;

This works fine in XE7 but e complains about the client certificate being missing in XE8.

As an experiment I tried the OPENSSL tool set to see what if it gave any clues as to what was going on. The log looks like:

OpenSSL>
OpenSSL> s_client -connect cc2.co:443
Loading 'screen' into random state - done
CONNECTED(00000214)
depth=1 /C=US/O=thawte, Inc./CN=thawte EV SSL CA - G3
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
0 s:/1.3.6.1.4.1.311.60.2.1.3=GB/2.5.4.15=Private Organization/O=RedTitan Ltd/s
erialNumber=01698238/C=GB/ST=Buckinghamshire/L=High Wycombe/CN=cc2.co
i:/C=US/O=thawte, Inc./CN=thawte EV SSL CA - G3
1 s:/C=US/O=thawte, Inc./CN=thawte EV SSL CA - G3
i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte,
Inc. - For authorized use only/CN=thawte Primary Root CA
---
Server certificate
-----BEGIN CERTIFICATE-----
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
8SpHTA==
-----END CERTIFICATE-----
subject=/1.3.6.1.4.1.311.60.2.1.3=GB/2.5.4.15=Private Organization/O=RedTitan Lt
d/serialNumber=01698238/C=GB/ST=Buckinghamshire/L=High Wycombe/CN=cc2.co
issuer=/C=US/O=thawte, Inc./CN=thawte EV SSL CA - G3
---
No client certificate CA names sent
---
SSL handshake has read 2898 bytes and written 450 bytes
---
New, TLSv1/SSLv3, Cipher is AES256-SHA
Server public key is 2048 bit
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : AES256-SHA
Session-ID: 930200001FB6008D4B7A605F50CED23D80415D61E2CF2FD492E89C0BFC6AD30E

Session-ID-ctx:
Master-Key: 37D745E0CDC6A821F0ED14E0163B7F803D5A686D5A31AC8513175C3596026621
10DBC139B2CC190EDC268427F769F902
Key-Arg : None
Start Time: 1447856850
Timeout : 300 (sec)
Verify return code: 20 (unable to get local issuer certificate)
---
hello, world
HTTP/1.1 400 Bad Request
Content-Type: text/html; charset=us-ascii
Server: Microsoft-HTTPAPI/2.0
Date: Wed, 18 Nov 2015 14:27:37 GMT
Connection: close
Content-Length: 326

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/str
ict.dtd">
<HTML><HEAD><TITLE>Bad Request</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY>

Bad Request - Invalid Verb

<hr>

HTTP Error 400. The request verb is invalid.

</BODY></HTML>
read:errno=0
OpenSSL>

The interesting thing here is that once the server says:

"Verify return code: 20 (unable to get local issuer certificate)"

it's still open for business. If I type "Hello, World" at it it complains about a bad request - which can be expected.

With XE8 it brings me back to the following method:

procedure THTTPClient.DoNeedClientCertificate(const LRequest: THTTPRequest; const LClientCertificateList: TCertificateList);
var
LClientCertificateIndex: Integer;
begin
LClientCertificateIndex := -1;
if Assigned(FNeedClientCertificateCallback) or Assigned(FNeedClientCertificateEvent) then
begin
DoGetClientCertificates(LRequest, LClientCertificateList);
if LClientCertificateList.Count = 0 then
raise ENetHTTPCertificateException.CreateRes(@SNetHttpEmptyCertificateList)
else if Assigned(FNeedClientCertificateCallback) then
FNeedClientCertificateCallback(Self, LRequest, LClientCertificateList, LClientCertificateIndex)
else
FNeedClientCertificateEvent(Self, LRequest, LClientCertificateList, LClientCertificateIndex);
end;
if LClientCertificateIndex < 0 then
raise ENetHTTPCertificateException.CreateRes(@SNetHttpUnspecifiedCertificate)
else
if DoClientCertificateAccepted(LRequest, LClientCertificateIndex) = False then
raise ENetHTTPCertificateException.CreateRes(@SNetHttpRejectedCertificate);
end;

should it actually be raising an exception if no client certificate was presented. Is it not down to the server to reject the connection attempt completely if no certificate was presented when it needs one?

Again, in XE7 - which uses openssl - all is fine.

Martin
wise info

Posts: 1
Registered: 1/18/06
Re: https -> HTTPClient requirement for Client Certificate  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jan 26, 2016 4:38 PM   in response to: Martin Waller in response to: Martin Waller
use NetHTTPClient1.Post('https://api.mch.weixin.qq.com/pay/micropay'), exception "Unspecified certificate from client."

but use IdHTTP+OPENSSL OK no problem!!

Edited by: wise info on Jan 26, 2016 4:38 PM
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02