Page 1 of 1

TScSslClient: readbuffer, writebuffer and other unknown things

Posted: Mon 05 Mar 2018 22:42
by wheathoff
How do TScSslClient.ReadBuffer and TScSslClient.WriteBuffer work? And how does THttpOptions work alongside these buffers?

We have a vendor who requires we use an SSL client certificate to connect to their site. After we successfully authenticate, we can retrieve an OAuth token for subsequent requests. They provide an example of doing this, using curl and assuming we're sending them a PEM file:

Code: Select all

curl Example:
'curl -k -d "grant_type=client_cert" --basic -u "proxyUser:proxyPwd" -H "Content-Type: application/x-www-form-urlencoded" --cert "our_cert.pem:ourPwd" "https://portal.myvendor.com/token"'

  //    This translates to:
  //    -k: Allow insecure server connections (Really?!...)
  //    -d: HTTP POST data ("grant_type=client_cert")
  //    --basic: Use HTTP Basic Authentication
  //    -u: Proxy user and password
  //    -H: Header
  //    --cert: Client certificate and password
  //            (presumably the PEM file is replaced by MyScSslClient.Storage.Certificates.FindCertificate(MyScSslClient.CertName),
  //            but where would we pass 'ourPwd'?
  //    --url ("https://portal.myvendor.com/token")
  //

I *think* I have been able to establish a connection, using TScSslClient. But, I'm confused as to what to do next: is the certificate value for the '--cert' argument passed when we called TScSslClient.Connect? (See other code sample, below.) What about the password portion of that same value?

If they *were* passed, do we then just need to focus on crafting the POST request and providing the HTTP header? What about providing the proxyuser and proxyPwd values?

I have a hunch that perhaps we need to use THttpOptions for some (all?) of these values. But, the documentation only gives SSH Tunneling examples for using THttpOptions -- nothing for an SSL Client certificate situation.

Let me know if I need to clarify anything. Thank you!

Code: Select all

procedure TForm1.Button1Click(Sender: TObject);
var
  sndbxKey, sndbxSecrt, vendorConn: string;
begin
  // For our dev sandbox (the '-u' argument in the curl example)
  sndbxKey   := 'ourProxyUsername';
  sndbxSecrt := 'ourProxyPwd';

  vendorConn := 'https://myvendor.com/token';

  MyScSslClient.Connect;

  // Now that we've connected, what do we do?

  // The API documentation for the SecureBridge TScSSLClient
  // (https://www.devart.com/sbridge/docs/tscsslclient.htm) says,
  // "To exchange data, you should use the ReadBuffer and WriteBuffer methods."
  // The documentation for ReadBuffer and WriteBuffer does not contain very much.

end;


Re: TScSslClient: readbuffer, writebuffer and other unknown things

Posted: Wed 07 Mar 2018 12:30
by ViktorV
1. If the server requires the user certificate verification for client authentication, you just need to specify it in the TScSSLClient.CertName property, pre-importing it into TScStorage. You can do this either in design time using the Certificates tab in the TScStorage descendant property editor, or in runtime using the TScCertificate.ImportFrom method (Stream: TStream; const Password: string = ''). For example:

Code: Select all

var
Cert: TScCertificate;
MyCert: String;
SStream: TStringStream;
...
Cert := TScCertificate.Create(ScFileStorage.Certificates);
Cert.Name := 'CertName';
SStream:=TStringStream.Create(MyCert);
Cert.ImportFrom(SStream, aPwd);
ScSSLClient.CertName := 'CertName';
ScSSLClient.Connect;
In this case, you only need to call the TScSSLClient.Connect method to connect to the server and no additional action are required for authentication.
2. SecureBridge allows you to connect to a non-HTTP server using an HTTP tunnel, and the THttpOptions property is used to configure this connection. If you are working with an HTTP server, then you cannot use the HTTP tunnel, and correspondingly, THttpOptions.
3. To work with the data via the HTTP protocol, you can use the TScHttpWebRequest component: https://devart.com/sbridge/docs/index.h ... equest.htm
You can find the samples of using the TScHttpWebRequest component at our forum: https://forums.devart.com/viewtopic.php?f=27&t=36270
Note, maybe we did not fully understand the essence of your question. If you have any questions after reading our answer - please contact us and we will try to give you a detailed answer in the shortest possible time.

Re: TScSslClient: readbuffer, writebuffer and other unknown things

Posted: Fri 09 Mar 2018 20:19
by wheathoff
Thanks for the reply. There was a typo in your example: Cert.Name should actually be Cert.CertName.

More importantly, when I try the following code, I'm getting an exception:

Code: Select all

procedure TForm1.FormCreate(Sender: TObject);
var
  Cert:    TScCertificate;
  SStream: TStringStream;
begin
  Cert          := TScCertificate.Create(MyFileStorage.Certificates);
  Cert.CertName := 'MyCert';
  SStream       := TStringStream.Create('MyCert.PEM');
  Cert.ImportFrom(SStream, 'aPwd');

  MySslClient.CertName := Cert.CertName;

end;
When we try to run Cert.ImportFrom(SStream, 'aPwd'), the exception 'Wrong certificate context' is raised. Stepping into it, I see it's at line 9505 of TScCertificate.SetCertContext. But, I have no idea what it means. Can you shed some light?

Thanks.

Re: TScSslClient: readbuffer, writebuffer and other unknown things

Posted: Mon 12 Mar 2018 14:53
by wheathoff
Further information:

TscASN1Compiler.Parse has this block

Code: Select all

  if Stream <> nil then
    Result := ParseData(FLexemInfo)
  else
    Result := True;
When we get into TscASN1Compiler.ParseData, we hit this section (lines 1634-1638), which exits with a Result of False:

Code: Select all

      PrevPos := ParentLexemInfo.FCurPos;
      Identifier := ReadByte(ParentLexemInfo);
      Len := ReadLength(ParentLexemInfo);
      if (ParentLexemInfo.FLexemData.FSize - ParentLexemInfo.FCurPos) < Len then
        Exit;
Hopefully this can help isolate the issue?...

Re: TScSslClient: readbuffer, writebuffer and other unknown things

Posted: Mon 12 Mar 2018 15:14
by ViktorV
To solve the issue, try replacing in your sample the string:

Code: Select all

  SStream: = TStringStream.Create ('MyCert.PEM');
with

Code: Select all

  SStream       := TStringStream.Create;
  SStream.LoadFromFile('MyCert.PEM');

Re: TScSslClient: readbuffer, writebuffer and other unknown things

Posted: Tue 13 Mar 2018 19:39
by wheathoff
Thank you -- separating Create and LoadFromFile got around the issue.

Attempting to send data with the HTTP Request object, I'm getting an error (from THandshakeLayer.ProcessAlert(Message: TRecordMessage)):
First chance exception at $75B308C2. Exception class EScError with message 'The other side has sent a failure alert: [40]'. Is the description '40' from the SecureBridge library, or is it from "the other side"?...

Re: TScSslClient: readbuffer, writebuffer and other unknown things

Posted: Tue 13 Mar 2018 20:42
by wheathoff
Further details:

We're trying to use the SecureBridge SslClient and TCRHttpWebRequest objects to replace the curl example shown in the commented portion of the code below. What remains tricky at this point is the --cert parameter of the curl command. You see in the example that the --cert parameter will expect the certificate PEM file *and* the password.

I am wondering:
1. Would the PEM file be represented by assigning Buf: TBytes := SslCiient.Storage.Certificates.FindCertificate(SslClient.CertName).GetRawData and passing that to WriteBuffer?

2. How to include the password as shown in the curl example? Would I need to append ':certPwd' to Buf, before passing Buf to WriteBuffer? Or is that the wrong approach for matching the curl syntax?

Thank you!

Code: Select all

procedure TForm1.SslClientAfterConnect(Sender: TObject);
var
  myKey, mySecrt, TokenURL: string;
  Request: TCRHttpWebRequest;
  Resp:    TCRHttpWebResponse;
  Buf:     TBytes;
  Base64AuthStr: string;
begin
  myKey   := 'myKey';
  mySecrt := 'mySecrt';

  TokenURL := 'https://securesite.com/token';

  // Example curl request:
  //
  //    'curl -k -d "grant_type=client_cert" --basic -u "myKey:mySecrt" -H "Content-Type: application/x-www-form-urlencoded" --cert "MyCert.pem:certPwd" "https://securesite.com/token"'
  // 
  //    --cert: Client certificate and password
  //            (presumably SslClient.Storage.Certificates.FindCertificate(SslClient.CertName),
  //            but how to provide the password ('certPwd')?...

  Base64AuthStr := Base64Encode(myKey + ':' + mySecrt);
  Buf := SslClient.Storage.Certificates.FindCertificate(SslClient.CertName).GetRawData;

  Request := TCRHttpWebRequest.Create(TokenURL);
  Request.Method := rmPost;
  Request.ContentType := 'application/x-www-form-urlencoded';
  Request.ContentLength := Length(Buf);
  Request.Headers.Add('grant_type', 'client_cert');
  Request.Headers.Add('Authorization',  'Basic ' + Base64AuthStr );
  Request.WriteBuffer(Buf);

  SslClient.IsSecure := True;

  try
    Resp :=  Request.GetResponse;
  finally
    Request.Free;
    SslClient.Disconnect;
  end;

end;


Re: TScSslClient: readbuffer, writebuffer and other unknown things

Posted: Wed 14 Mar 2018 08:40
by ViktorV
Please provide more information about your task. Do you use a certificate to authenticate a client to a server? Then, why are you trying to pass it using TCRHttpWebRequest? How do you bind a client certificate with the certificate that you pass using the TCRHttpWebRequest component, because these are different connections?

Re: TScSslClient: readbuffer, writebuffer and other unknown things

Posted: Wed 14 Mar 2018 18:43
by wheathoff
Okay, let's start over.

I'm connecting to a server that requires a certificate from me. So, I'm using your SslClient component to connect. (I hit the AfterConnect event, so I'm assuming (correctly?...) that we authenticated. At least, the 'Connected' property is True.

Now that we're connected, what should we do to send and receive data? You pointed me to the ReadBuffer and WriteBuffer methods, but the documentation on those methods is sparse (to put it mildly). Are those all that we need to use?

For example: now that we're connected, let's say we want to send the following:
POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded

say=Hi&to=Mom
Using the SslClient connection, how would you send that and how would you read the server response?


Thank you.

Re: TScSslClient: readbuffer, writebuffer and other unknown things

Posted: Mon 19 Mar 2018 15:40
by wheathoff
*Bump*

Anyone?....

Re: TScSslClient: readbuffer, writebuffer and other unknown things

Posted: Mon 19 Mar 2018 16:21
by ViktorV
To solve the task, you can use the following code:

Code: Select all

var
  WriteBuf, ReadBuf: TBytes;
begin
  WriteBuf := TEncoding.UTF8.GetBytes('POST / HTTP/1.1' + #13#10 +
    'Content-Type: application/x-www-form-urlencoded' + #13#10 + 'say=Hi&to=Mom');
  ScSSLClient.WriteBuffer(WriteBuf, Length(WriteBuf));
  SetLength(ReadBuf, ReadBufSize);
  ScSSLClient.ReadBuffer(ReadBuf, Length(ReadBuf));
end;