You are not logged in.
Pages: 1
procedure ACMEStart;
var
F: TFileName;
ES: TAcmeLetsEncryptServer;
I: integer;
Status: TAcmeStatus;
Cert, PrivateKey: RawUtf8;
begin
RegisterOpenSsl;
F := IncludeTrailingPathDelimiter(GetCurrentDir) + 'sslFolder';
ES := TAcmeLetsEncryptServer.Create(TSynLog, F,
ACME_LETSENCRYPT_DEBUG_URL,
'x509-es256', 'jf93v83');
try
ES.LoadFromKeyStoreFolder;
for I := Low(ES.Client) to High(ES.Client) do
with ES.Client[I] do
begin
RegisterAndWaitFolder(F, F, F, 'jf93v83', 3);
repeat
Status := CheckChallengesStatus;
Sleep(1000);
until Status <> asPending;
if Status = asValid then
begin
Status := ES.Client[I].CompleteDomainRegistration(Cert, PrivateKey, 'jf93v83');
end;
end;
ES.CheckCertificatesBackground;
ES.Redirect('xxx.com', 'https://xxx.com');
ES.Redirect('www.xxx.com', 'https://www.xxx.com');
finally
ES.Free;
end;
end;
Below is the JSON file, to be used by the LoadFromKeyStoreFolder method.
{
"contact": "mailto:admin@xxx.com",
"subjects": [
"xxx.com"
"www.com"
]
}
1.Since I don't have a domain name or a public IP, I cannot perform testing. I’m not sure if what I wrote is correct. Is there anything else I should pay attention to?
2.How does ACME integrate with TMvcApplication? Should I directly call RegisterAndWaitFolder to save the certificate files, or how should it be handled?
HttpServer := TRestHttpServer.Create([RestServerDB], '443', 32, secTLS, HTTPSERVER_DEFAULT_OPTIONS,
'C:\Users\FBI\Desktop\BOOK\1\ssl\mycert.pfx',
'C:\Users\FBI\Desktop\BOOK\1\ssl\privkey.pem',
'|&VwVx;2S',
'C:\Users\FBI\Desktop\BOOK\1\ssl\cert.pem'
);
FRestHttpServer.DomainHostRedirect('xxx.com', 'root');
FRestHttpServer.RootRedirectToURI('xxx.com', 'blog/default', true, true);
Last edited by testgary (2025-06-06 09:53:52)
Offline
Sorry, I forgot to check before sending the content.
Offline
I created pull request to fix minor ACME client bug:
https://github.com/synopse/mORMot2/pull/367
MVC Blog example with HTTPS and Let's Encrypt certificate:
https://gist.github.com/achechulin/f423 … f5ed261b89
Check it out in real life:
https://mvc-blog.fun/
Offline
I created pull request to fix minor ACME client bug:
https://github.com/synopse/mORMot2/pull/367
First of all, thank you very much for sharing
I have reviewed your code and noticed that you associate tmvcapplication and acme through reading and writing local certificate files, rather than linking them directly in the code. Could this cause any issues, such as file access conflicts?
Also, it seems you did not use the RegisterAndWaitFolder CompleteDomainRegistration method. Have you tested your code to ensure it runs completely without any problems?
Offline
There is low level TAcmeClient class that implements the ACME V2 client, and high level TAcmeLetsEncrypt/TAcmeLetsEncryptClient/TAcmeLetsEncryptServer classes that implements Let's Encrypt domains certificates management.
Also, it seems you did not use the RegisterAndWaitFolder CompleteDomainRegistration method.
TAcmeLetsEncrypt.CheckCertificates check certificates expiration and renew if needed.
I have reviewed your code and noticed that you associate tmvcapplication and acme through reading and writing local certificate files, rather than linking them directly in the code. Could this cause any issues, such as file access conflicts?
In TAcmeLetsEncrypt.LoadFromKeyStoreFolder we set callback mormot.net.sock.OnNetTlsAcceptServerName to point to TAcmeLetsEncrypt implementation, that returns certfifcate matched to requested server name, and after certificate renewed it's reread it from file.
Offline
type
Tzerossl = class
private
FAcmeClient: TAcmeClient;
FICryptCert: ICryptCert;
FChallengeRec: ^TAcmeChallenge;
protected
procedure DoChallenges(Sender: TObject; const Domain, Key, Token: RawUtf8);
public
constructor Create;
destructor Destroy; override;
procedure start;
procedure CheckCertificate(Sender: TObject);
procedure CheckCertificatesBackground;
published
end;
implementation
const
ACME_ZEROSSL_URL = 'https://acme.zerossl.com/v2/DV90';
ACME_CHALLENGE_PATH = '/.well-known/pki-validation/';
procedure Tzerossl.DoChallenges(Sender: TObject; const Domain, Key, Token: RawUtf8);
begin
if Key <> '' then
begin
FChallengeRec^.Token := Token;
FChallengeRec^.Key := Key;
end;
end;
constructor Tzerossl.Create;
begin
inherited Create;
New(FChallengeRec);
FChallengeRec^ := Default(TAcmeChallenge);
FICryptCert := Cert('x509-es256');
FAcmeClient := TAcmeClient.Create(nil, FICryptCert, ACME_ZEROSSL_URL, 'tempEmail@temp.com', 'www.temp.com');
// FAcmeClient.OnChallenges := @DoChallenges;
end;
destructor Tzerossl.Destroy;
begin
if Assigned(FChallengeRec) then
begin
Dispose(FChallengeRec);
FChallengeRec := nil;
end;
FAcmeClient.Free;
inherited Destroy;
end;
procedure Tzerossl.start;
var
Terminated: boolean = False;
SSLDir: TFileName;
const
WaitForSec = 30;
PrivateKeyPassword = 'tempPwd';
begin
SSLDir := IncludeTrailingPathDelimiter(GetCurrentDir) + 'ssl' + PathDelim;
if not DirectoryExists(SSLDir) then
ForceDirectories(SSLDir);
if FAcmeClient.RegisterAndWait(@DoChallenges, SSLDir + 'mycert.pen', SSLDir + 'privkey.pem', PrivateKeyPassword, WaitForSec, @Terminated) = TAcmeStatus.asValid then
begin
// ok
end;
end;
procedure Tzerossl.CheckCertificate(Sender: TObject);
var
ExpiryDate: TDateTime;
SSLDIR: TFileName;
CertFile: TFileName;
const
RenewBeforeDays = 30;
begin
SSLDIR := IncludeTrailingPathDelimiter(GetCurrentDir) + 'ssl' + PathDelim;
CertFile := SSLDIR + 'mycert.pen';
if not FileExists(CertFile) then Exit;
if not FICryptCert.LoadFromFile(CertFile) then Exit;
ExpiryDate := FICryptCert.GetNotAfter;
if ExpiryDate < (NowUtc + RenewBeforeDays) then
start;
end;
procedure Tzerossl.CheckCertificatesBackground;
begin
TLoggedWorkThread.Create(TSynLog, 'CheckCertificate', self, @CheckCertificate, nil, False);
end;
Right now I’m using a ZeroSSL certificate instead of a Let’s Encrypt one. The snippets above show some code, but there’s no documentation and I don’t know how to write it. I’m using TMVCApplication and I need help
Offline
ZeroSSL required externalAccountBinding with their "EAB Credentials".
https://zerossl.com/documentation/acme/
So, we need some changes in mormot.net.acme to add such binding.
I'll try to figure it out.
Last edited by Chaa (2025-07-28 10:48:37)
Offline
I created pull request to support zerossl.com ACME server:
https://github.com/synopse/mORMot2/pull/371
To provide External Account Binding (EAB) credentials you need go to Developer Section of your ZeroSSL account. Then select Generate EAB Credentials and put it into your domain.json file.
{
"contact": "mailto:support@mvc-blog.fun",
"subjects": ["mvc-blog.fun", "www.mvc-blog.fun"],
"eab": {
"algo": "Sha256",
"kid": "XyzNHzLJ-vzzVz0z4b7zGw",
"mac_key": "zzxxWwwL8109G07_u0sux_zg9Q999im9a9kCHB8PZz1y55vp5xL5rVP5cZa5K5Br2qWu5Wi5e5iC5s355Jw46"
}
}
Then point your app to new ACME server:
aAcmeServer := TAcmeLetsEncryptServer.Create(TSynLog, 'acme', 'https://acme.zerossl.com/v2/DV90',
'', 'passw0rd', nil, 2);
Check it out in real life with ZeroSSL certificate:
https://mvc-blog.fun/
Offline
Your code is just perfect.
I have merged it.
Thanks a lot!
Apart from that, I found out that we do not provide the full certificate chain, and TLS tests services complain about it:
https://www.ssllabs.com/ssltest/analyze … c-blog.fun
Do you have any idea how to improve this?
Offline
Do you have any idea how to improve this?
Let's Encrypt and ZeroSSL returns certificate with full chain.
Problem is in TOpenSslNetTls.SetupCtx - LoadCertificate only load first cert from file.
We need change it to LoadCertificates and then use first cert for SSL_CTX_use_certificate, and the remaining for SSL_CTX_add0_chain_cert.
For reference, see SSL_CTX_use_certificate_chain_file:
https://github.com/openssl/openssl/blob … rsa.c#L551
Quick prototype:
xa := LoadCertificates(cert, 16); // PEM or DER
if Length(xa) > 0 then
try
EOpenSslNetTls.Check(self, 'SetupCtx Certificate',
SSL_CTX_use_certificate(fCtx, xa[0]));
xa[0] := nil;
for i := 1 to Length(xa) - 1 do
begin
EOpenSslNetTls.Check(self, 'SetupCtx Chain Certificate',
SSL_CTX_add_extra_chain_cert(fCtx, xa[i]));
xa[i] := nil;
end;
finally
PX509DynArrayFree(xa);
end
And SSL Rating now A:
https://www.ssllabs.com/ssltest/analyze … c-blog.fun
But at the moment, I dont understand difference between SSL_CTX_add0_chain_cert and SSL_CTX_add_extra_chain_cert.
Offline
I have made a huge refactoring of the OpenSSL server code.
The CA of the certificates were not properly published - not only in the context of ACME.
Also enhanced a lot the error handling.
Please try with the current code.
Offline
But at the moment, I dont understand difference between SSL_CTX_add0_chain_cert and SSL_CTX_add_extra_chain_cert.
Offline
Thank you very much for your efforts! I'm really happy to see how powerful mormot has become.
Offline
Thank you, now difference is clear.
I create pull request with fixes for latest changes:
https://github.com/synopse/mORMot2/pull/372
Offline
Pages: 1