Secure connections
A server that is reachable from the internet should serve wss:// rather than
ws://: TLS encrypts the traffic, proves the server's identity to clients, and
is the only option for browser pages served over https and for the WhatsApp
webhook. TTMSFNCWebSocketServer supports TLS through an Indy server IO
handler. By default it creates an OpenSSL-based handler from three certificate
file properties; when you need OpenSSL 3.x or another TLS library, it lets you
supply the handler yourself. This chapter covers enabling TLS, the certificate
files and their format, reusing an existing Windows or IIS certificate,
deploying OpenSSL, and plugging in a custom IO handler.
Enabling TLS
Set UseSSL to True and point
the server at its certificate files before activating it:
| Property | Contents |
|---|---|
CertificateFile |
The server certificate (PEM). |
CertificateKeyFile |
The private key that belongs to the certificate (PEM). |
RootCertificateFile |
The issuing and intermediate certificates, when the certificate authority provides a chain. |
If the private key is protected by a pass phrase, supply it from
OnGetSSLPassword.
Clients then connect with wss:// and their own TLS setting enabled — on
TTMSFNCWebsocketClient, UseSSL := True.
procedure TForm1.StartSecureServer;
var
CertDir: string;
begin
CertDir := TPath.Combine(ExtractFilePath(ParamStr(0)), 'certs');
// Enable TLS and set the certificate paths before the first activation.
TMSFNCWebSocketServer1.UseSSL := True;
TMSFNCWebSocketServer1.CertificateFile := TPath.Combine(CertDir, 'cert.pem');
TMSFNCWebSocketServer1.CertificateKeyFile := TPath.Combine(CertDir, 'key.pem');
TMSFNCWebSocketServer1.RootCertificateFile := TPath.Combine(CertDir, 'chain.pem');
TMSFNCWebSocketServer1.OnGetSSLPassword := ServerGetSSLPassword;
TMSFNCWebSocketServer1.Port := 443;
TMSFNCWebSocketServer1.Active := True; // clients connect to wss://<host>/
end;
procedure TForm1.ServerGetSSLPassword(Sender: TObject; var APassword: string);
begin
// The pass phrase that protects the private key file.
APassword := FKeyPassPhrase;
end;
Deploying OpenSSL for the default handler
The default handler is Indy's OpenSSL server IO handler, which negotiates up
to TLS 1.2 and loads the OpenSSL 1.0.2 libraries at runtime. On Windows, place
libeay32.dll and ssleay32.dll, in the bitness of the application, next to
the executable. Sources and binaries are available from the
OpenSSL 1.0.2 source archive and the
Indy OpenSSL binaries repository.
To keep the libraries in a subfolder, set the library path before the server is
activated:
uses
IdSSLOpenSSLHeaders;
procedure TForm1.FormCreate(Sender: TObject);
begin
// Must run before the first secure connection loads the libraries.
// The folder contains libeay32.dll and ssleay32.dll in the application's bitness.
IdOpenSSLSetLibPath(TPath.Combine(ExtractFilePath(ParamStr(0)), 'openssl'));
end;
Converting a PFX certificate to PEM
The certificate properties expect PEM files. A certificate exported as a PFX (PKCS #12) file converts to a single PEM file that contains both the certificate and the private key with the OpenSSL command line:
openssl pkcs12 -in filename.pfx -out cert.pem -nodes
Point both CertificateFile and CertificateKeyFile at the resulting file, or
split the certificate and key blocks into two files. -nodes writes the key
unencrypted; protect the file accordingly, or leave out -nodes to encrypt the
key and supply the pass phrase from OnGetSSLPassword.
Exporting an existing certificate from Windows Server
A certificate already installed on a Windows server, for example for IIS, can be exported to a PFX file and then converted as shown above:
- Press Win+R, type
mmc, and select OK to open the Microsoft Management Console. - Select File > Add/Remove Snap-in.
- Select Certificates under Available snap-ins and select Add.
- Choose Computer account and select Next.
- Select Local computer and select Finish.
- Select OK to add the snap-in to the console.
- Open the Personal > Certificates store in the left pane, right-click the certificate to export, and select All Tasks > Export.
- In the Certificate Export Wizard, select Next.
- Select Yes, export the private key and select Next.
- Select Personal Information Exchange - PKCS #12 (.PFX) as the format. Optionally check Include all certificates in the certification path if possible to include the intermediate certificates. Select Next.
- Enter a password for the PFX file and select Next.
- Choose the location and name of the PFX file and select Next.
- Select Finish. The message The export was successful. confirms that the certificate and its private key were exported.
Then convert the exported file to PEM, entering the PFX password when asked:
openssl pkcs12 -in exported.pfx -out cert.pem -nodes
Using OpenSSL 3.x or another TLS library
To use OpenSSL 3.x, supply your own server IO handler from
OnCreateIOHandler.
The event fires once, when the server first needs a TLS handler, and passes the
handler by reference in AIOHandler; a handler you assign replaces the
default. Pick a library that builds on Indy's TLS support — for example
TaurusTLS — create its
server IO handler class, and configure it completely.
uses
IdServerIOHandler, IdSSLOpenSSL;
procedure TForm1.TMSFNCWebSocketServer1CreateIOHandler(Sender: TObject;
var AIOHandler: TIdServerIOHandler);
var
Handler: TIdServerIOHandlerSSLOpenSSL;
begin
// For OpenSSL 3.x, create the server IO handler class of an Indy TLS
// library such as TaurusTLS here instead, and configure it the same way.
// The form owns the handler, so it is freed with the form.
Handler := TIdServerIOHandlerSSLOpenSSL.Create(Self);
// Nothing is copied from the server's certificate properties: configure all of it.
Handler.SSLOptions.CertFile := 'certs\cert.pem';
Handler.SSLOptions.KeyFile := 'certs\key.pem';
Handler.SSLOptions.RootCertFile := 'certs\chain.pem';
Handler.SSLOptions.Mode := sslmServer;
Handler.SSLOptions.SSLVersions := [sslvTLSv1_2];
Handler.OnGetPassword := HandlerGetPassword;
AIOHandler := Handler;
end;
procedure TForm1.HandlerGetPassword(var Password: string);
begin
Password := FKeyPassPhrase;
end;
procedure TForm1.StartBtnClick(Sender: TObject);
begin
// Assign the event before UseSSL; the handler is requested on first use.
TMSFNCWebSocketServer1.OnCreateIOHandler := TMSFNCWebSocketServer1CreateIOHandler;
TMSFNCWebSocketServer1.UseSSL := True;
TMSFNCWebSocketServer1.Port := 443;
TMSFNCWebSocketServer1.Active := True;
end;
Warning
A custom IO handler is used exactly as you configure it. The server does not
copy CertificateFile, CertificateKeyFile, or RootCertificateFile into
it and does not hook OnGetSSLPassword to it. Set the certificate paths,
pass phrase handling, TLS versions, and any events the handler needs inside
OnCreateIOHandler.
Combining certificate setup with a custom handler
A deployment often supports both setups — the default handler with PEM
certificate properties on one machine, an OpenSSL 3.x handler on another — and
picks one from configuration. The example does that and keeps the activation
order right: the handler event or the certificate properties first, then
UseSSL, then Active. The WhatsApp webhook host,
TTMSFNCWhatsAppServer, uses the same
certificate properties with TLS always enabled.
procedure TForm1.StartFromConfig(AUseCustomHandler: Boolean; const ACertDir: string);
begin
if AUseCustomHandler then
// The custom handler configures certificates and pass phrase itself
// (see TMSFNCWebSocketServer1CreateIOHandler); assign it before UseSSL.
TMSFNCWebSocketServer1.OnCreateIOHandler := TMSFNCWebSocketServer1CreateIOHandler
else
begin
// Default OpenSSL handler: the server applies these properties to it.
TMSFNCWebSocketServer1.CertificateFile := TPath.Combine(ACertDir, 'cert.pem');
TMSFNCWebSocketServer1.CertificateKeyFile := TPath.Combine(ACertDir, 'key.pem');
TMSFNCWebSocketServer1.RootCertificateFile := TPath.Combine(ACertDir, 'chain.pem');
TMSFNCWebSocketServer1.OnGetSSLPassword := ServerGetSSLPassword;
end;
TMSFNCWebSocketServer1.UseSSL := True;
TMSFNCWebSocketServer1.Port := 443;
try
TMSFNCWebSocketServer1.Active := True;
except
on E: Exception do
Log('Secure server could not start: ' + E.Message);
end;
end;
Pitfalls
- Configure TLS before the first activation. The server creates its
internal HTTP server, with or without TLS, the first time it becomes active;
set
UseSSL, the certificate files, andOnCreateIOHandlerbefore that. - Assign
OnCreateIOHandlerbefore enablingUseSSLin code. Setting a certificate property whileUseSSLisTruealready creates the handler. - Use PEM files. Convert PFX certificates first; the default handler cannot read them.
- Include the chain. Without the intermediate certificates in
RootCertificateFile, some clients cannot verify the server. - Missing libraries fail at runtime. Ship the OpenSSL libraries with the application and match its bitness.
UseSSLcannot change while active. Stop the server first.
Related API
UseSSL,CertificateFile,CertificateKeyFile,RootCertificateFileOnGetSSLPassword,TTMSFNCWebSocketServerGetSSLPasswordEventOnCreateIOHandler,TTMSFNCWebSocketServerCreateIOHandlerEvent
See also
- Running a server — start the server and handle clients.
- Client secure connections — connecting to a
wss://server. - WhatsApp webhook setup — a server that always runs over TLS.