Skip to content

Instantly share code, notes, and snippets.

@achechulin
Created May 20, 2022 05:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save achechulin/fb785cf87040175707e666aa58377ef5 to your computer and use it in GitHub Desktop.
Save achechulin/fb785cf87040175707e666aa58377ef5 to your computer and use it in GitHub Desktop.
TLS support for THttpServer
--- a/httpServerRaw.dpr
+++ b/httpServerRaw.dpr
@@ -103 +103 @@ begin
- fHttpServer := THttpAsyncServer.Create(
+ fHttpServer := THttpServer.Create(
--- a/mormot.net.sock.pas
+++ b/mormot.net.sock.pas
@@ -3255,3 +3255 @@ begin
- aTLS and
- not doBind and
- ({%H-}PtrInt(aSock) <= 0) then
+ (PtrInt(aSock) > 0) then
--- a/mormot.net.sock.windows.inc
+++ b/mormot.net.sock.windows.inc
@@ -11 +11,3 @@ uses
- Windows;
+ Windows,
+ // ASC_REQ_*, TSChannel_Cred, CertFindCertificateInStore, etc
+ mormot.lib.sspi;
@@ -1045,0 +1048,5 @@ type
+ TCryptDataBlob = record
+ cbData: Cardinal;
+ pbData: Pointer;
+ end;
+
@@ -1092,0 +1100,3 @@ var
+ function PFXImportCertStore(pPFX: Pointer; szPassword: PWideChar;
+ dwFlags: Cardinal): HCERTSTORE; stdcall; external 'crypt32.dll';
+
@@ -1136 +1146,9 @@ const
-
+ // ASC_REQ_* const values differs from ISC_REQ_*
+ ASC_REQ_REPLAY_DETECT = $00000004;
+ ASC_REQ_SEQUENCE_DETECT = $00000008;
+ ASC_REQ_EXTENDED_ERROR = $00008000;
+ ASC_REQ_STREAM = $00010000;
+ ASC_REQ_FLAGS =
+ ASC_REQ_SEQUENCE_DETECT or ASC_REQ_REPLAY_DETECT or
+ ASC_REQ_CONFIDENTIALITY or ASC_REQ_EXTENDED_ERROR or
+ ASC_REQ_ALLOCATE_MEMORY or ASC_REQ_STREAM;
@@ -1294,0 +1313,5 @@ end;
+// Shared context
+var
+ gCertStore: HCERTSTORE;
+ gCert: PCCERT_CONTEXT;
+
@@ -1298,2 +1321,3 @@ var
- buf: THandshakeBuf;
- res, f: cardinal;
+ certblob: RawByteString;
+ blob: TCryptDataBlob;
+ cred: TSChannel_Cred;
@@ -1304,0 +1329,25 @@ begin
+ // Create shared TLS context. Not thread safe.
+ // Should be created before server start, i.e. in NewServerTlsContext
+ if gCert = nil then
+ begin
+ certblob := StringFromFile('mycert.pfx');
+ blob.cbData := Length(certblob);
+ blob.pbData := Pointer(certblob);
+ // We can load certificate from WIndows certificate store
+ // or from file. I use Let's Encrypt sertificate, converted to PFX:
+ // openssl pkcs12 -inkey privkey.pem -in cert.pem -export -out mycert.pfx
+ // Unfortunately, CertOpenStore(CERT_STORE_PROV_PKCS12) does not work.
+ // May be private key not loaded? But PFXImportCertStore works fine.
+ // Or use current user certificate store:
+ // gCertStore := CertOpenSystemStoreW(nil, 'MY');
+ gCertStore := PFXImportCertStore(@blob, nil, {PKCS12_INCLUDE_EXTENDED_PROPERTIES}$10);
+ // Find first certificate in store
+ gCert := CertFindCertificateInStore(gCertStore, 0, 0, {CERT_FIND_ANY}0, nil, nil);
+ if not Assigned(gCert) then
+ raise ESChannel.Create('Certificate not available');
+ // After shutdown call CertFreeCertificateContext and CertCloseStore
+ end;
+ FillCharFast(cred, SizeOf(cred), 0);
+ cred.dwVersion := {SCHANNEL_CRED_VERSION}4;
+ cred.cCreds := 1;
+ cred.paCred := @gCert;
@@ -1307,2 +1356,2 @@ begin
- CheckSEC_E_OK(AcquireCredentialsHandle(nil, UNISP_NAME, SECPKG_CRED_OUTBOUND,
- nil, nil, nil, nil, @fCred, nil));
+ CheckSEC_E_OK(AcquireCredentialsHandle(nil, UNISP_NAME, SECPKG_CRED_INBOUND,
+ nil, @cred, nil, nil, @fCred, nil));
@@ -1311,16 +1360 @@ begin
- fFlags := ISC_REQ_FLAGS;
- if Context.IgnoreCertificateErrors then
- // prevent SEC_E_UNTRUSTED_ROOT result in HandshakeLoop
- fFlags := fFlags or ISC_REQ_MANUAL_CRED_VALIDATION;
- // initiate a ClientHello TLS message and a new fCtxt
- HandshakeBufInit(buf);
- res := InitializeSecurityContext(@fCred, nil, pointer(ServerAddress),
- fFlags, 0, 0, nil, 0, @fCtxt, @buf.output, @f, nil);
- if res <> SEC_I_CONTINUE_NEEDED then
- ESChannelRaiseLastError(res);
- if (buf.buf[2].cbBuffer = 0) or
- (buf.buf[2].pvBuffer = nil) then
- raise ESChannel.CreateFmt('Void Hello answer to %s', [ServerAddress]);
- CheckSocket(mormot.net.sock.Send(
- fSocket.Socket, buf.buf[2].pvBuffer, buf.buf[2].cbBuffer, 0));
- CheckSEC_E_OK(FreeContextBuffer(buf.buf[2].pvBuffer));
+ fFlags := ASC_REQ_FLAGS;
@@ -1343,0 +1378 @@ procedure TSChannelClient.HandshakeLoop;
+ e: LARGE_INTEGER;
@@ -1344,0 +1380 @@ procedure TSChannelClient.HandshakeLoop;
+ LInCtxPtr: PSecHandle;
@@ -1350,2 +1386,6 @@ procedure TSChannelClient.HandshakeLoop;
- result := InitializeSecurityContext(@fCred, @fCtxt, pointer(fServerAddress),
- fFlags, 0, 0, @buf.input, 0, nil, @buf.output, @f, nil);
+ if (fCtxt.dwLower = nil) and (fCtxt.dwUpper = nil) then
+ LInCtxPtr := nil
+ else
+ LInCtxPtr := @fCtxt;
+ result := AcceptSecurityContext(@fCred, LInCtxPtr, @buf.input,
+ fFlags, 0, @fCtxt, @buf.output, f, e);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment