2using System.Collections.Generic;
3using System.Diagnostics;
7using System.Security.Cryptography;
8using System.Security.Cryptography.X509Certificates;
10using System.Threading.Tasks;
45 private string[] alternativeDomains =
null;
46 private byte[] certificate =
null;
47 private byte[] privateKey =
null;
48 private byte[] pfx =
null;
49 private string humanReadableName =
string.Empty;
50 private string humanReadableNameLanguage =
string.Empty;
51 private string humanReadableDescription =
string.Empty;
52 private string humanReadableDescriptionLanguage =
string.Empty;
53 private string domain =
string.Empty;
54 private string acmeDirectory =
string.Empty;
55 private string contactEMail =
string.Empty;
56 private string urlToS =
string.Empty;
57 private string password =
string.Empty;
58 private string openSslPath =
string.Empty;
59 private string dynDnsTemplate =
string.Empty;
60 private string checkIpScript =
string.Empty;
61 private string updateIpScript =
string.Empty;
62 private string dynDnsAccount =
string.Empty;
63 private string dynDnsPassword =
string.Empty;
64 private int dynDnsInterval = 300;
65 private bool useDomainName =
false;
66 private bool dynamicDns =
false;
67 private bool useEncryption =
true;
68 private bool customCA =
false;
69 private bool acceptToS =
false;
71 private string challenge =
string.Empty;
72 private string token =
string.Empty;
82 [DefaultValueStringEmpty]
86 set => this.domain = value;
95 get => this.alternativeDomains;
96 set => this.alternativeDomains = value;
102 [DefaultValue(
false)]
105 get => this.useDomainName;
108 this.useDomainName = value;
109 if (!this.useDomainName)
111 this.domain =
string.Empty;
112 this.alternativeDomains =
null;
113 this.dynamicDns =
false;
114 this.useEncryption =
false;
115 this.customCA =
false;
123 [DefaultValue(
false)]
126 get => this.dynamicDns;
127 set => this.dynamicDns = value;
136 get => this.useEncryption;
137 set => this.useEncryption = value;
143 [DefaultValue(
false)]
146 get => this.customCA;
147 set => this.customCA = value;
153 [DefaultValueStringEmpty]
156 get => this.acmeDirectory;
157 set => this.acmeDirectory = value;
163 [DefaultValueStringEmpty]
166 get => this.contactEMail;
167 set => this.contactEMail = value;
173 [DefaultValueStringEmpty]
177 set => this.urlToS = value;
183 [DefaultValue(
false)]
186 get => this.acceptToS;
187 set => this.acceptToS = value;
196 get => this.certificate;
197 set => this.certificate = value;
206 get => this.privateKey;
207 set => this.privateKey = value;
217 set => this.pfx = value;
223 [DefaultValueStringEmpty]
226 get => this.password;
227 set => this.password = value;
233 [DefaultValueStringEmpty]
236 get => this.openSslPath;
237 set => this.openSslPath = value;
243 [DefaultValueStringEmpty]
246 get => this.dynDnsTemplate;
247 set => this.dynDnsTemplate = value;
253 [DefaultValueStringEmpty]
256 get => this.checkIpScript;
257 set => this.checkIpScript = value;
263 [DefaultValueStringEmpty]
266 get => this.updateIpScript;
267 set => this.updateIpScript = value;
273 [DefaultValueStringEmpty]
276 get => this.dynDnsAccount;
277 set => this.dynDnsAccount = value;
286 get => this.dynDnsInterval;
287 set => this.dynDnsInterval = value;
293 [DefaultValueStringEmpty]
296 get => this.dynDnsPassword;
297 set => this.dynDnsPassword = value;
303 [DefaultValueStringEmpty]
306 get => this.humanReadableName;
307 set => this.humanReadableName = value;
313 [DefaultValueStringEmpty]
316 get => this.humanReadableNameLanguage;
317 set => this.humanReadableNameLanguage = value;
323 [DefaultValueStringEmpty]
326 get => this.humanReadableDescription;
327 set => this.humanReadableDescription = value;
333 [DefaultValueStringEmpty]
336 get => this.humanReadableDescriptionLanguage;
337 set => this.humanReadableDescriptionLanguage = value;
346 get => this.localizedNames;
347 set => this.localizedNames = value;
356 get => this.localizedDescriptions;
357 set => this.localizedDescriptions = value;
363 public bool HasToS => !
string.IsNullOrEmpty(this.urlToS);
368 public override string Resource =>
"/Settings/Domain.md";
390 return Gateway.ConfigureDomain(
this);
408 this.testDomainNames = WebServer.
Register(
"/Settings/TestDomainNames",
null, this.TestDomainNames,
true,
false,
true);
409 this.testDomainName = WebServer.
Register(
"/Settings/TestDomainName", this.TestDomainName,
true,
false,
true);
410 this.testCA = WebServer.
Register(
"/Settings/TestCA",
null, this.TestCA,
true,
false,
true);
411 this.acmeChallenge = WebServer.
Register(
"/.well-known/acme-challenge", this.
AcmeChallenge,
true,
true,
true);
412 this.saveNames = WebServer.
Register(
"/Settings/SaveNames",
null, this.SaveNames,
true,
false,
true);
413 this.saveDescriptions = WebServer.
Register(
"/Settings/SaveDescriptions",
null, this.SaveDescriptions,
true,
false,
true);
415 return base.InitSetup(WebServer);
431 return base.UnregisterSetup(WebServer);
447 if (!(Obj is Dictionary<string, object> Parameters))
450 if (!Parameters.TryGetValue(
"domainName", out Obj) || !(Obj is
string DomainName) ||
451 !Parameters.TryGetValue(
"dynamicDns", out Obj) || !(Obj is
bool DynamicDns) ||
452 !Parameters.TryGetValue(
"dynDnsTemplate", out Obj) || !(Obj is
string DynDnsTemplate) ||
453 !Parameters.TryGetValue(
"checkIpScript", out Obj) || !(Obj is
string CheckIpScript) ||
454 !Parameters.TryGetValue(
"updateIpScript", out Obj) || !(Obj is
string UpdateIpScript) ||
455 !Parameters.TryGetValue(
"dynDnsAccount", out Obj) || !(Obj is
string DynDnsAccount) ||
456 !Parameters.TryGetValue(
"dynDnsPassword", out Obj) || !(Obj is
string DynDnsPassword) ||
457 !Parameters.TryGetValue(
"dynDnsInterval", out Obj) || !(Obj is
int DynDnsInterval))
462 if (
string.Compare(DomainName,
"localhost",
true) == 0)
465 List<string> AlternativeNames =
new List<string>();
468 while (Parameters.TryGetValue(
"altDomainName" + Index.ToString(), out Obj) && Obj is
string AltDomainName && !
string.IsNullOrEmpty(AltDomainName))
470 if (
string.Compare(AltDomainName,
"localhost",
true) == 0)
473 AlternativeNames.Add(AltDomainName);
477 if (Parameters.TryGetValue(
"altDomainName", out Obj) && Obj is
string AltDomainName2 && !
string.IsNullOrEmpty(AltDomainName2))
479 if (
string.Compare(AltDomainName2,
"localhost",
true) == 0)
482 AlternativeNames.Add(AltDomainName2);
485 string TabID = Request.
Header[
"X-TabID"];
486 if (
string.IsNullOrEmpty(TabID))
496 this.domain = DomainName;
497 this.alternativeDomains = AlternativeNames.Count == 0 ? null : AlternativeNames.ToArray();
498 this.useDomainName =
true;
500 Response.StatusCode = 200;
502 Task _ = Task.Run(async () => await this.Test(TabID,
false));
507 Response.StatusCode = 200;
509 return Response.
Write(this.token);
512 private async Task<bool> Test(
string TabID,
bool EnvironmentSetup)
516 if (!
string.IsNullOrEmpty(this.domain))
518 if (!await this.Test(TabID, EnvironmentSetup, this.domain))
520 if (
string.IsNullOrEmpty(TabID))
528 if (!(this.alternativeDomains is
null))
530 foreach (
string AltDomainName
in this.alternativeDomains)
532 if (!await this.Test(TabID, EnvironmentSetup, AltDomainName))
534 if (
string.IsNullOrEmpty(TabID))
537 await ClientEvents.PushEvent(
new string[] { TabID },
"NameNotValid", AltDomainName,
false,
"User");
546 this.Updated = DateTime.Now;
549 if (!
string.IsNullOrEmpty(TabID))
550 await ClientEvents.PushEvent(
new string[] { TabID },
"NamesOK",
string.Empty,
false,
"User");
556 if (
string.IsNullOrEmpty(TabID))
559 await ClientEvents.PushEvent(
new string[] { TabID },
"ShowStatus", ex.Message,
false,
"User");
565 internal Task<bool> CheckDynamicIp()
567 return this.CheckDynamicIp(
null,
false);
570 internal Task<bool> CheckDynamicIp(
bool EnvironmentSetup)
572 return this.CheckDynamicIp(
null, EnvironmentSetup);
575 private async Task<bool> CheckDynamicIp(
string TabID,
bool EnvironmentSetup)
579 if (!this.useDomainName || !this.dynamicDns)
584 if (!await this.CheckDynamicIp(TabID, this.domain, EnvironmentSetup))
587 foreach (
string AlternativeDomain
in this.alternativeDomains)
589 if (!await this.CheckDynamicIp(TabID, AlternativeDomain, EnvironmentSetup))
602 internal async Task<bool> CheckDynamicIp(
string TabID,
string DomainName,
bool EnvironmentSetup)
604 string Msg = await this.CheckDynamicIp(DomainName, EnvironmentSetup, async (_, Status) =>
606 if (!
string.IsNullOrEmpty(TabID))
607 await ClientEvents.PushEvent(
new string[] { TabID },
"ShowStatus", Status,
false,
"User");
610 if (!
string.IsNullOrEmpty(Msg))
612 if (!
string.IsNullOrEmpty(TabID))
613 await ClientEvents.PushEvent(
new string[] { TabID },
"CertificateError", Msg,
false,
"User");
628 public async Task<string>
CheckDynamicIp(
string DomainName,
bool EnvironmentSetup, EventHandlerAsync<string> Status)
630 if (!this.dynamicDns)
642 string Msg =
"Unable to parse script checking current IP Address: " + ex.Message;
644 if (EnvironmentSetup)
656 string Msg =
"Unable to parse script updating the dynamic DNS server: " + ex.Message;
658 if (EnvironmentSetup)
664 await Status.Raise(
this,
"Checking current IP Address.");
675 string Msg =
"Unable to get current IP Address: " + ex.Message;
677 if (EnvironmentSetup)
683 if (!(Result is
string CurrentIP) || !IPAddress.TryParse(CurrentIP, out IPAddress _))
685 string Msg =
"Unable to get current IP Address. Unexpected response.";
687 if (EnvironmentSetup)
693 await Status.Raise(
this,
"Current IP Address: " + CurrentIP);
697 if (LastIP == CurrentIP)
698 await Status.Raise(
this,
"IP Address has not changed for " + DomainName +
".");
703 await Status.Raise(
this,
"Updating IP address for " + DomainName +
" to " + CurrentIP +
".");
705 Variables[
"Account"] = this.dynDnsAccount;
706 Variables[
"Password"] = this.dynDnsPassword;
716 string Msg =
"Unable to register new dynamic IP Address: " + ex.Message;
718 if (EnvironmentSetup)
728 private async Task<bool> Test(
string TabID,
bool EnvironmentSetup,
string DomainName)
730 string Msg = await this.Test(DomainName, EnvironmentSetup, async (_, Status) =>
732 if (!
string.IsNullOrEmpty(TabID))
736 if (!
string.IsNullOrEmpty(Msg))
738 if (!
string.IsNullOrEmpty(TabID))
754 public async Task<string>
Test(
string DomainName,
bool EnvironmentSetup, EventHandlerAsync<string> Status)
756 await Status.Raise(
this,
"Testing " + DomainName +
"...");
758 string Msg = await this.CheckDynamicIp(DomainName, EnvironmentSetup, Status);
759 if (!
string.IsNullOrEmpty(Msg))
764 using (HttpClient HttpClient =
new HttpClient()
766 Timeout = TimeSpan.FromMilliseconds(10000)
771 StringBuilder Url =
new StringBuilder();
774 Url.Append(
"http://");
775 Url.Append(DomainName);
777 if (Array.IndexOf(HttpPorts, 80) < 0 && HttpPorts.Length > 0)
780 Url.Append(HttpPorts[0].
ToString());
783 Url.Append(
"/Settings/TestDomainName");
785 HttpResponseMessage Response = await HttpClient.GetAsync(
"http://" + DomainName +
"/Settings/TestDomainName");
786 if (!Response.IsSuccessStatusCode)
788 return "Domain name does not point to this machine.";
791 byte[] Bin = await Response.Content.ReadAsByteArrayAsync();
792 string Token = System.Text.
Encoding.ASCII.GetString(Bin);
794 if (Token != this.token)
795 return "Unexpected response returned. Domain name does not point to this machine.";
797 catch (TimeoutException)
799 return "Time-out. Check that the domain name points to this machine.";
803 return "Unable to validate domain name: " + ex.Message;
807 await Status.Raise(
this,
"Domain name valid.");
820 if (!(Obj is Dictionary<string, object> Parameters))
823 if (!Parameters.TryGetValue(
"useEncryption", out Obj) || !(Obj is
bool UseEncryption))
826 if (!Parameters.TryGetValue(
"customCA", out Obj) || !(Obj is
bool CustomCA))
829 if (!Parameters.TryGetValue(
"acmeDirectory", out Obj) || !(Obj is
string AcmeDirectory))
832 if (!Parameters.TryGetValue(
"contactEMail", out Obj) || !(Obj is
string ContactEMail))
835 if (!Parameters.TryGetValue(
"acceptToS", out Obj) || !(Obj is
bool AcceptToS))
838 if (!Parameters.TryGetValue(
"domainName", out Obj) || !(Obj is
string DomainName) ||
839 !Parameters.TryGetValue(
"dynamicDns", out Obj) || !(Obj is
bool DynamicDns) ||
840 !Parameters.TryGetValue(
"dynDnsTemplate", out Obj) || !(Obj is
string DynDnsTemplate) ||
841 !Parameters.TryGetValue(
"checkIpScript", out Obj) || !(Obj is
string CheckIpScript) ||
842 !Parameters.TryGetValue(
"updateIpScript", out Obj) || !(Obj is
string UpdateIpScript) ||
843 !Parameters.TryGetValue(
"dynDnsAccount", out Obj) || !(Obj is
string DynDnsAccount) ||
844 !Parameters.TryGetValue(
"dynDnsPassword", out Obj) || !(Obj is
string DynDnsPassword) ||
845 !Parameters.TryGetValue(
"dynDnsInterval", out Obj) || !(Obj is
int DynDnsInterval))
848 List<string> AlternativeNames =
new List<string>();
851 while (Parameters.TryGetValue(
"altDomainName" + Index.ToString(), out Obj) && Obj is
string AltDomainName && !
string.IsNullOrEmpty(AltDomainName))
853 AlternativeNames.Add(AltDomainName);
857 if (Parameters.TryGetValue(
"altDomainName", out Obj) && Obj is
string AltDomainName2 && !
string.IsNullOrEmpty(AltDomainName2))
858 AlternativeNames.Add(AltDomainName2);
860 string TabID = Request.
Header[
"X-TabID"];
861 if (
string.IsNullOrEmpty(TabID))
871 this.domain = DomainName;
872 this.alternativeDomains = AlternativeNames.Count == 0 ? null : AlternativeNames.ToArray();
873 this.useDomainName =
true;
880 Response.StatusCode = 200;
882 if (!this.inProgress)
884 this.inProgress =
true;
885 Task _ = Task.Run(async () =>
889 await this.CreateCertificate(TabID,
false);
899 private bool inProgress =
false;
907 public async Task<string>
AddDomain(
string Name, EventHandlerAsync<string> Status)
910 return "Domain already registered.";
912 string Msg = await this.Test(Name,
false, Status);
913 if (!
string.IsNullOrEmpty(Msg))
916 string DomainBak = this.domain;
917 bool UseDomainBak = this.useDomainName;
918 string[] AlternativeBak = this.alternativeDomains;
920 if (
string.IsNullOrEmpty(this.domain))
923 this.useDomainName =
true;
925 else if (this.alternativeDomains is
null)
926 this.alternativeDomains =
new string[] { Name };
929 int c = this.alternativeDomains.Length;
930 string[] NewArray =
new string[c + 1];
931 this.alternativeDomains.CopyTo(NewArray, 0);
933 this.alternativeDomains = NewArray;
936 Msg = await this.CreateCertificate(
false, Status, (_, __) => Task.CompletedTask);
937 if (!
string.IsNullOrEmpty(Msg))
939 this.domain = DomainBak;
940 this.useDomainName = UseDomainBak;
941 this.alternativeDomains = AlternativeBak;
946 this.Updated = DateTime.Now;
958 public async Task<string>
RemoveDomain(
string Name, EventHandlerAsync<string> Status)
961 return "Domain not registered.";
963 string DomainBak = this.domain;
964 bool UseDomainBak = this.useDomainName;
965 string[] AlternativeBak = this.alternativeDomains;
968 if (
string.Compare(this.domain, Name,
true) == 0)
970 if ((this.alternativeDomains?.Length ?? 0) == 0)
972 this.domain =
string.Empty;
973 this.useDomainName =
false;
977 this.domain = this.alternativeDomains[0];
979 int c = this.alternativeDomains.Length;
982 this.alternativeDomains =
null;
985 NewArray =
new string[c - 1];
986 Array.Copy(this.alternativeDomains, 1, NewArray, 0, c - 1);
987 this.alternativeDomains = NewArray;
991 else if (!(this.alternativeDomains is
null))
993 int c = this.alternativeDomains.Length;
995 this.alternativeDomains =
null;
998 int i = Array.IndexOf(this.alternativeDomains, Name);
1000 NewArray =
new string[c - 1];
1003 Array.Copy(this.alternativeDomains, 0, NewArray, 0, i);
1006 Array.Copy(this.alternativeDomains, i + 1, NewArray, i, c - i - 1);
1008 this.alternativeDomains = NewArray;
1012 if (this.useDomainName)
1014 string Msg = await this.CreateCertificate(
false, Status, (_, __) => Task.CompletedTask);
1015 if (!
string.IsNullOrEmpty(Msg))
1017 this.domain = DomainBak;
1018 this.useDomainName = UseDomainBak;
1019 this.alternativeDomains = AlternativeBak;
1025 this.Updated = DateTime.Now;
1038 if (
string.Compare(Name, this.domain,
true) == 0)
1041 if (this.alternativeDomains is
null)
1044 foreach (
string Alternative
in this.alternativeDomains)
1046 if (
string.Compare(Name, Alternative,
true) == 0)
1053 internal Task<bool> CreateCertificate()
1055 return this.CreateCertificate(
null,
false);
1058 private async Task<bool> CreateCertificate(
string TabID,
bool EnvironmentSetup)
1062 string Msg = await this.CreateCertificate(EnvironmentSetup,
1063 async (_, Status) =>
1065 if (!
string.IsNullOrEmpty(TabID))
1070 if (!
string.IsNullOrEmpty(TabID))
1071 await ClientEvents.PushEvent(
new string[] { TabID },
"TermsOfService", URL,
false,
"User");
1074 if (
string.IsNullOrEmpty(Msg))
1076 if (!
string.IsNullOrEmpty(TabID))
1077 await ClientEvents.PushEvent(
new string[] { TabID },
"CertificateOk",
string.Empty,
false,
"User");
1083 if (!
string.IsNullOrEmpty(TabID))
1084 await ClientEvents.PushEvent(
new string[] { TabID },
"CertificateError", Msg,
false,
"User");
1089 catch (Exception ex)
1091 if (!
string.IsNullOrEmpty(TabID))
1092 await ClientEvents.PushEvent(
new string[] { TabID },
"CertificateError", ex.Message,
false,
"User");
1094 if (EnvironmentSetup)
1095 this.LogEnvironmentError(ex.Message, GATEWAY_ENCRYPTION,
this.useEncryption);
1101 internal async Task<string> CreateCertificate(
bool EnvironmentSetup, EventHandlerAsync<string> Status,
1102 EventHandlerAsync<string> TermsOfService)
1106 string URL = this.customCA ? this.acmeDirectory :
"https://acme-v02.api.letsencrypt.org/directory";
1107 RSAParameters Parameters;
1108 CspParameters CspParams =
new CspParameters()
1110 Flags = CspProviderFlags.UseMachineKeyStore,
1111 KeyContainerName =
"IoTGateway:" + URL
1118 using (RSACryptoServiceProvider RSA =
new RSACryptoServiceProvider(4096, CspParams))
1120 Parameters = RSA.ExportParameters(
true);
1122 if (RSA.KeySize < 4096)
1124 RSA.PersistKeyInCsp =
false;
1134 using (RSACryptoServiceProvider RSA =
new RSACryptoServiceProvider(4096, CspParams))
1136 Parameters = RSA.ExportParameters(
true);
1140 catch (CryptographicException ex)
1142 throw new CryptographicException(
"Unable to get access to cryptographic key for \"IoTGateway:" + URL +
1143 "\". Was the database created using another user?", ex);
1148 await Status.Raise(
this,
"Connecting to directory.");
1153 await Status.Raise(
this,
"An external account is required.");
1158 await Status.Raise(
this,
"Terms of service available on: " + URL);
1159 await TermsOfService.Raise(
this, URL);
1163 if (!this.acceptToS)
1165 string Msg =
"You need to accept the terms of service.";
1167 if (EnvironmentSetup)
1168 this.LogEnvironmentError(Msg, GATEWAY_ACME_ACCEPT_TOS, this.acceptToS);
1177 await Status.Raise(
this,
"Getting account.");
1179 List<string> Names =
new List<string>();
1181 if (!
string.IsNullOrEmpty(this.domain))
1182 Names.Add(this.domain);
1184 if (!(this.alternativeDomains is
null))
1186 foreach (
string Name
in this.alternativeDomains)
1188 if (!Names.Contains(Name))
1192 string[] DomainNames = Names.ToArray();
1200 await Status.Raise(
this,
"Account found.");
1201 await Status.Raise(
this,
"Created: " + Account.
CreatedAt.ToString());
1202 await Status.Raise(
this,
"Initial IP: " + Account.
InitialIp);
1203 await Status.Raise(
this,
"Status: " + Account.
Status.ToString());
1205 if (
string.IsNullOrEmpty(this.contactEMail))
1209 await Status.Raise(
this,
"Updating contact URIs in account.");
1210 Account = await Account.
Update(
new string[0]);
1211 await Status.Raise(
this,
"Account updated.");
1216 if (Account.
Contact is
null || Account.
Contact.Length != 1 || Account.
Contact[0] !=
"mailto:" +
this.contactEMail)
1218 await Status.Raise(
this,
"Updating contact URIs in account.");
1219 Account = await Account.
Update(
new string[] {
"mailto:" + this.contactEMail });
1220 await Status.Raise(
this,
"Account updated.");
1226 await Status.Raise(
this,
"Account not found.");
1227 await Status.Raise(
this,
"Creating account.");
1229 Account = await Client.
CreateAccount(
string.IsNullOrEmpty(this.contactEMail) ?
new string[0] :
new string[] {
"mailto:" + this.contactEMail },
1232 await Status.Raise(
this,
"Account created.");
1233 await Status.Raise(
this,
"Status: " + Account.
Status.ToString());
1236 await Status.Raise(
this,
"Generating new key.");
1239 using (RSACryptoServiceProvider RSA =
new RSACryptoServiceProvider(4096, CspParams))
1244 await Status.Raise(
this,
"New key generated.");
1246 await Status.Raise(
this,
"Creating order.");
1255 await Task.Delay(5000);
1256 await Status.Raise(
this,
"Retrying.");
1260 await Status.Raise(
this,
"Order created.");
1264 await Status.Raise(
this,
"Getting authorizations.");
1271 await Task.Delay(5000);
1272 await Status.Raise(
this,
"Retrying.");
1278 await Status.Raise(
this,
"Processing authorization for " + Authorization.
Value);
1281 bool Acknowledged =
false;
1283 int NrChallenges = Authorization.
Challenges.Length;
1285 for (Index = 1; Index <= NrChallenges; Index++)
1287 Challenge = Authorization.
Challenges[Index - 1];
1291 this.challenge =
"/" + HttpChallenge.
Token;
1292 this.token = HttpChallenge.KeyAuthorization;
1294 await Status.Raise(
this,
"Acknowleding challenge.");
1296 string Msg = await this.CheckDynamicIp(Authorization.
Value, EnvironmentSetup, Status);
1297 if (
string.IsNullOrEmpty(Msg))
1300 await Status.Raise(
this,
"Challenge acknowledged: " + Challenge.
Status.ToString());
1302 Acknowledged =
true;
1311 string Msg =
"No automated method found to respond to any of the authorization challenges.";
1313 if (EnvironmentSetup)
1314 this.LogEnvironmentError(Msg, GATEWAY_ENCRYPTION, this.useEncryption);
1323 await Status.Raise(
this,
"Waiting to poll authorization status.");
1324 await Task.Delay(5000);
1326 await Status.Raise(
this,
"Polling authorization.");
1327 Authorization2 = await Authorization2.
Poll();
1329 await Status.Raise(
this,
"Authorization polled: " + Authorization2.
Status.ToString());
1335 switch (Authorization2.
Status)
1338 throw new Exception(
"Authorization deactivated.");
1341 throw new Exception(
"Authorization expired.");
1344 throw new Exception(
"Authorization invalid.");
1347 throw new Exception(
"Authorization revoked.");
1350 throw new Exception(
"Authorization not validated.");
1355 using (RSACryptoServiceProvider RSA =
new RSACryptoServiceProvider(4096))
1357 await Status.Raise(
this,
"Finalizing order.");
1363 CommonName = this.domain,
1364 SubjectAlternativeNames = DomainNames,
1365 EMailAddress = this.contactEMail
1368 await Status.Raise(
this,
"Order finalized: " + Order.
Status.ToString());
1375 throw new Exception(
"Order invalid.");
1378 throw new Exception(
"Unable to validate order.");
1383 throw new Exception(
"No certificate URI provided.");
1385 await Status.Raise(
this,
"Downloading certificate.");
1388 X509Certificate2 Certificate = Certificates[0];
1390 await Status.Raise(
this,
"Exporting certificate.");
1392 this.certificate = Certificate.Export(X509ContentType.Cert);
1393 this.privateKey = RSA.ExportCspBlob(
true);
1395 this.password =
string.Empty;
1397 await Status.Raise(
this,
"Adding private key.");
1401 Certificate.PrivateKey = RSA;
1403 catch (PlatformNotSupportedException)
1405 await Status.Raise(
this,
"Platform does not support adding of private key.");
1406 await Status.Raise(
this,
"Searching for OpenSSL on machine.");
1410 string CertFileName =
null;
1411 string CertFileName2 =
null;
1412 string KeyFileName =
null;
1414 if (
string.IsNullOrEmpty(this.openSslPath) || !File.Exists(
this.openSslPath))
1416 string[] Folders = Gateway.GetFolders(
new Environment.SpecialFolder[]
1418 Environment.SpecialFolder.ProgramFiles,
1419 Environment.SpecialFolder.ProgramFilesX86
1421 Path.DirectorySeparatorChar +
"OpenSSL-Win32",
1422 Path.DirectorySeparatorChar +
"OpenSSL-Win64");
1424 Files = Gateway.FindFiles(Folders,
"openssl.exe", 2,
int.MaxValue);
1427 Files =
new string[] { this.openSslPath };
1431 if (Files.Length == 0)
1433 string Msg =
"Unable to join certificate with private key. Try installing <a target=\"_blank\" href=\"https://wiki.openssl.org/index.php/Binaries\">OpenSSL</a> and try again.";
1435 if (EnvironmentSetup)
1436 this.LogEnvironmentError(Msg, GATEWAY_ENCRYPTION, this.useEncryption);
1442 foreach (
string OpenSslFile
in Files)
1444 if (CertFileName is
null)
1446 await Status.Raise(
this,
"Generating temporary certificate file.");
1448 StringBuilder PemOutput =
new StringBuilder();
1449 byte[] Bin = Certificate.Export(X509ContentType.Cert);
1451 PemOutput.AppendLine(
"-----BEGIN CERTIFICATE-----");
1452 PemOutput.AppendLine(Convert.ToBase64String(Bin, Base64FormattingOptions.InsertLineBreaks));
1453 PemOutput.AppendLine(
"-----END CERTIFICATE-----");
1455 CertFileName = Path.Combine(Gateway.AppDataFolder,
"Certificate.pem");
1458 await Status.Raise(
this,
"Generating temporary key file.");
1464 PemOutput.AppendLine(
"-----BEGIN RSA PRIVATE KEY-----");
1465 PemOutput.AppendLine(Convert.ToBase64String(KeyOutput.
ToArray(), Base64FormattingOptions.InsertLineBreaks));
1466 PemOutput.AppendLine(
"-----END RSA PRIVATE KEY-----");
1468 KeyFileName = Path.Combine(Gateway.AppDataFolder,
"Certificate.key");
1473 await Status.Raise(
this,
"Converting to PFX using " + OpenSslFile);
1475 Process P =
new Process()
1477 StartInfo =
new ProcessStartInfo()
1479 FileName = OpenSslFile,
1480 Arguments =
"pkcs12 -nodes -export -out Certificate.pfx -inkey Certificate.key -in Certificate.pem -password pass:" + Password,
1481 UseShellExecute =
false,
1482 RedirectStandardError =
true,
1483 RedirectStandardOutput =
true,
1484 WorkingDirectory = Gateway.AppDataFolder,
1485 CreateNoWindow =
true,
1486 WindowStyle = ProcessWindowStyle.Hidden
1492 if (!P.WaitForExit(60000) || P.ExitCode != 0)
1494 if (!P.StandardOutput.EndOfStream)
1495 await Status.Raise(
this,
"Output: " + P.StandardOutput.ReadToEnd());
1497 if (!P.StandardError.EndOfStream)
1498 await Status.Raise(
this,
"Error: " + P.StandardError.ReadToEnd());
1503 await Status.Raise(
this,
"Loading PFX.");
1505 CertFileName2 = Path.Combine(Gateway.AppDataFolder,
"Certificate.pfx");
1507 this.password = Password;
1508 this.openSslPath = OpenSslFile;
1510 await Status.Raise(
this,
"PFX successfully generated using OpenSSL.");
1514 if (this.pfx is
null)
1516 this.openSslPath =
string.Empty;
1518 string Msg =
"Unable to convert to PFX using OpenSSL.";
1520 if (EnvironmentSetup)
1521 this.LogEnvironmentError(Msg, GATEWAY_ENCRYPTION, this.useEncryption);
1529 if (!(CertFileName is
null) && File.Exists(CertFileName))
1531 await Status.Raise(
this,
"Deleting temporary certificate file.");
1532 File.Delete(CertFileName);
1535 if (!(KeyFileName is
null) && File.Exists(KeyFileName))
1537 await Status.Raise(
this,
"Deleting temporary key file.");
1538 File.Delete(KeyFileName);
1541 if (!(CertFileName2 is
null) && File.Exists(CertFileName2))
1543 await Status.Raise(
this,
"Deleting temporary pfx file.");
1544 File.Delete(CertFileName2);
1553 this.Updated = DateTime.Now;
1556 await Gateway.UpdateCertificate(
this);
1562 catch (Exception ex)
1568 if (EnvironmentSetup)
1569 this.LogEnvironmentError(Msg, GATEWAY_ENCRYPTION, this.useEncryption);
1575 this.inProgress =
false;
1581 if (Request.
SubPath !=
this.challenge)
1584 Response.StatusCode = 200;
1586 return Response.
Write(System.Text.Encoding.ASCII.GetBytes(
this.token));
1591 Gateway.AssertUserAuthenticated(Request, this.ConfigPrivilege);
1597 if (!(Obj is Dictionary<string, object> Parameters))
1600 if (!Parameters.TryGetValue(
"humanReadableName", out Obj) ||
1601 !(Obj is
string HumanReadableName) ||
1602 !Parameters.TryGetValue(
"humanReadableNameLanguage", out Obj) ||
1603 !(Obj is
string HumanReadableNameLanguage))
1608 List<AlternativeField> LocalizedNames =
new List<AlternativeField>();
1611 while (Parameters.TryGetValue(
"nameLanguage" + Index.ToString(), out Obj) &&
1612 Obj is
string NameLanguage &&
1613 Parameters.TryGetValue(
"nameLocalized" + Index.ToString(), out Obj) &&
1614 Obj is
string NameLocalized)
1616 if (
string.IsNullOrEmpty(NameLanguage))
1619 if (
string.IsNullOrEmpty(NameLocalized))
1622 LocalizedNames.Add(
new AlternativeField(NameLanguage, NameLocalized));
1626 this.HumanReadableName = HumanReadableName;
1627 this.HumanReadableNameLanguage = HumanReadableNameLanguage;
1628 this.LocalizedNames = LocalizedNames.ToArray();
1630 this.Updated = DateTime.Now;
1636 Gateway.AssertUserAuthenticated(Request, this.ConfigPrivilege);
1642 if (!(Obj is Dictionary<string, object> Parameters))
1645 if (!Parameters.TryGetValue(
"humanReadableDescription", out Obj) ||
1646 !(Obj is
string HumanReadableDescription) ||
1647 !Parameters.TryGetValue(
"humanReadableDescriptionLanguage", out Obj) ||
1648 !(Obj is
string HumanReadableDescriptionLanguage))
1653 List<AlternativeField> LocalizedDescriptions =
new List<AlternativeField>();
1656 while (Parameters.TryGetValue(
"descriptionLanguage" + Index.ToString(), out Obj) &&
1657 Obj is
string DescriptionLanguage &&
1658 Parameters.TryGetValue(
"descriptionLocalized" + Index.ToString(), out Obj) &&
1659 Obj is
string DescriptionLocalized)
1661 if (
string.IsNullOrEmpty(DescriptionLanguage))
1664 if (
string.IsNullOrEmpty(DescriptionLocalized))
1667 LocalizedDescriptions.Add(
new AlternativeField(DescriptionLanguage, DescriptionLocalized));
1671 this.HumanReadableDescription = HumanReadableDescription;
1672 this.HumanReadableDescriptionLanguage = HumanReadableDescriptionLanguage;
1673 this.LocalizedDescriptions = LocalizedDescriptions.ToArray();
1675 this.Updated = DateTime.Now;
1685 return Task.FromResult(
true);
1691 public const string GATEWAY_DOMAIN_USE = nameof(GATEWAY_DOMAIN_USE);
1696 public const string GATEWAY_DOMAIN_NAME = nameof(GATEWAY_DOMAIN_NAME);
1703 public const string GATEWAY_DOMAIN_ALT = nameof(GATEWAY_DOMAIN_ALT);
1708 public const string GATEWAY_DYNDNS = nameof(GATEWAY_DYNDNS);
1713 public const string GATEWAY_ENCRYPTION = nameof(GATEWAY_ENCRYPTION);
1718 public const string GATEWAY_CA_CUSTOM = nameof(GATEWAY_CA_CUSTOM);
1723 public const string GATEWAY_ACME_EMAIL = nameof(GATEWAY_ACME_EMAIL);
1728 public const string GATEWAY_ACME_ACCEPT_TOS = nameof(GATEWAY_ACME_ACCEPT_TOS);
1733 public const string GATEWAY_HR_NAME = nameof(GATEWAY_HR_NAME);
1738 public const string GATEWAY_HR_NAME_LANG = nameof(GATEWAY_HR_NAME_LANG);
1743 public const string GATEWAY_HR_DESC = nameof(GATEWAY_HR_DESC);
1748 public const string GATEWAY_HR_DESC_LANG = nameof(GATEWAY_HR_DESC_LANG);
1753 public const string GATEWAY_HR_NAME_LOC = nameof(GATEWAY_HR_NAME_LOC);
1758 public const string GATEWAY_HR_NAME_ = nameof(GATEWAY_HR_NAME_);
1763 public const string GATEWAY_HR_DESC_LOC = nameof(GATEWAY_HR_DESC_LOC);
1768 public const string GATEWAY_HR_DESC_ = nameof(GATEWAY_HR_DESC_);
1775 public const string GATEWAY_DYNDNS_TEMPLATE = nameof(GATEWAY_DYNDNS_TEMPLATE);
1780 public const string GATEWAY_DYNDNS_CHECK = nameof(GATEWAY_DYNDNS_CHECK);
1785 public const string GATEWAY_DYNDNS_UPDATE = nameof(GATEWAY_DYNDNS_UPDATE);
1790 public const string GATEWAY_DYNDNS_ACCOUNT = nameof(GATEWAY_DYNDNS_ACCOUNT);
1795 public const string GATEWAY_DYNDNS_PASSWORD = nameof(GATEWAY_DYNDNS_PASSWORD);
1800 public const string GATEWAY_DYNDNS_INTERVAL = nameof(GATEWAY_DYNDNS_INTERVAL);
1807 public const string GATEWAY_ACME_DIRECTORY = nameof(GATEWAY_ACME_DIRECTORY);
1815 if (!this.TryGetEnvironmentVariable(GATEWAY_DOMAIN_USE,
false, out this.useDomainName))
1818 if (!this.useDomainName)
1820 this.Domain =
string.Empty;
1821 this.DynamicDns =
false;
1822 this.UseEncryption =
false;
1823 this.CustomCA =
false;
1824 this.ContactEMail =
string.Empty;
1825 this.AcceptToS =
false;
1829 if (!this.TryGetEnvironmentVariable(GATEWAY_DOMAIN_NAME,
true, out
string Value))
1832 if (
string.Compare(Value,
"localhost",
true) == 0)
1834 this.LogEnvironmentError(
"localhost is not a valid domain name.", GATEWAY_DOMAIN_NAME, Value);
1838 this.Domain = Value;
1840 Value = Environment.GetEnvironmentVariable(GATEWAY_DOMAIN_ALT);
1841 if (
string.IsNullOrEmpty(Value))
1842 this.AlternativeDomains =
null;
1845 string[] Parts = Value.Split(
',');
1847 foreach (
string Part
in Parts)
1849 if (
string.Compare(Part,
"localhost",
true) == 0)
1851 this.LogEnvironmentError(
"localhost is not a valid alternative domain name.", GATEWAY_DOMAIN_ALT, Value);
1856 this.AlternativeDomains = Parts;
1859 if (!this.TryGetEnvironmentVariable(GATEWAY_DYNDNS, out this.dynamicDns,
false))
1862 if (this.dynamicDns)
1864 if (!this.TryGetEnvironmentVariable(GATEWAY_DYNDNS_TEMPLATE,
true, out Value))
1867 this.DynDnsTemplate = Value;
1869 if (!this.TryGetEnvironmentVariable(GATEWAY_DYNDNS_CHECK,
true, out Value))
1872 this.CheckIpScript = Value;
1874 if (!this.TryGetEnvironmentVariable(GATEWAY_DYNDNS_UPDATE,
true, out Value))
1877 this.UpdateIpScript = Value;
1879 this.TryGetEnvironmentVariable(GATEWAY_DYNDNS_ACCOUNT,
false, out this.dynDnsAccount);
1880 this.TryGetEnvironmentVariable(GATEWAY_DYNDNS_PASSWORD,
false, out this.dynDnsPassword);
1882 if (!this.TryGetEnvironmentVariable(GATEWAY_DYNDNS_UPDATE, 60, 86400,
true, ref this.dynDnsInterval))
1886 if (!this.TryGetEnvironmentVariable(GATEWAY_ENCRYPTION,
true, out this.useEncryption))
1889 if (this.useEncryption)
1891 if (!this.TryGetEnvironmentVariable(GATEWAY_CA_CUSTOM, out this.customCA,
false))
1896 if (!this.TryGetEnvironmentVariable(GATEWAY_ACME_DIRECTORY,
true, out this.acmeDirectory))
1900 if (!this.TryGetEnvironmentVariable(GATEWAY_ACME_EMAIL,
true, out this.contactEMail))
1903 if (!this.TryGetEnvironmentVariable(GATEWAY_ACME_ACCEPT_TOS,
true, out this.acceptToS))
1908 AlternativeField LocalizedString = this.GetLocalizedEnvironmentVariable(GATEWAY_HR_NAME, GATEWAY_HR_NAME_LANG);
1909 if (LocalizedString is
null)
1912 this.HumanReadableName = LocalizedString.
Value;
1913 this.HumanReadableNameLanguage = LocalizedString.
Key;
1915 LocalizedString = this.GetLocalizedEnvironmentVariable(GATEWAY_HR_DESC, GATEWAY_HR_DESC_LANG);
1916 if (LocalizedString is
null)
1919 this.HumanReadableDescription = LocalizedString.
Value;
1920 this.HumanReadableDescriptionLanguage = LocalizedString.
Key;
1922 this.localizedNames = this.GetLocalizedEnvironmentVariables(GATEWAY_HR_NAME_LOC, GATEWAY_HR_NAME_);
1923 if (this.localizedNames is
null)
1926 this.localizedDescriptions = this.GetLocalizedEnvironmentVariables(GATEWAY_HR_DESC_LOC, GATEWAY_HR_DESC_);
1927 if (this.localizedDescriptions is
null)
1930 if (!await this.Test(
null,
true))
1933 if (this.dynamicDns && !await this.CheckDynamicIp(
true))
1936 if (this.useEncryption && this.useDomainName && !await this.CreateCertificate(
null,
true))
1942 private AlternativeField GetLocalizedEnvironmentVariable(
string VariableName,
string LanguageVariableName)
1944 if (!this.TryGetEnvironmentVariable(VariableName,
true, out
string Value) ||
1945 !this.TryGetEnvironmentVariable(LanguageVariableName,
true, out
string Language))
1950 return new AlternativeField(Value,
Language);
1953 private AlternativeField[] GetLocalizedEnvironmentVariables(
string CollectionVariableName,
string VariableName)
1955 List<AlternativeField> Result =
new List<AlternativeField>();
1957 string Value = Environment.GetEnvironmentVariable(CollectionVariableName);
1958 if (!
string.IsNullOrEmpty(Value))
1960 string[] Languages = Value.Split(
',');
1963 foreach (
string Language in Languages)
1967 if (!this.TryGetEnvironmentVariable(Name,
true, out Value))
1970 Result.Add(
new AlternativeField(
Language, Value));
1974 return Result.ToArray();
const string DefaultContentType
text/plain
Static class managing loading of resources stored as embedded resources or in content files.
static async Task< byte[]> ReadAllBytesAsync(string FileName)
Reads a binary file asynchronously.
static Task WriteAllTextAsync(string FileName, string Text)
Creates a text file asynchronously.
Plain text encoder/decoder.
const string DefaultContentType
text/plain
Helps with common XML-related tasks.
static string HtmlValueEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe or the quote.
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
The ClientEvents class allows applications to push information asynchronously to web clients connecte...
static Task< int > PushEvent(string[] TabIDs, string Type, object Data)
Puses an event to a set of Tabs, given their Tab IDs.
Static class managing the runtime environment of the IoT Gateway.
static IUser AssertUserAuthenticated(HttpRequest Request, string Privilege)
Makes sure a request is being made from a session with a successful user login.
static byte[] NextBytes(int NrBytes)
Generates an array of random bytes.
static int[] GetConfigPorts(string Protocol)
Gets the port numbers defined for a given protocol in the configuration file.
Represents an alternative field in a legal identity.
string Value
Alternative field Value.
string Key
Alternative field name.
string UpdateIpScript
Script to use to update the current IP Address.
string DynDnsTemplate
Dynamic DNS Template
AlternativeField[] LocalizedDescriptions
Localized descriptions of domain
static DomainConfiguration Instance
Current instance of configuration.
string Password
Password for PFX file, if any.
const string GATEWAY_DOMAIN_NAME
Main Domain Name of the gateway, if defined. If not provided, the gateway will not use a domain name.
string HumanReadableDescription
Human-readable description of domain
async Task< string > Test(string DomainName, bool EnvironmentSetup, EventHandlerAsync< string > Status)
Checks if the domain points to the server.
string AcmeDirectory
If a custom Certificate Authority is to be used, this property holds the URL to their ACME directory.
override Task ConfigureSystem()
Is called during startup to configure the system.
async Task< string > AddDomain(string Name, EventHandlerAsync< string > Status)
Adds a domain to the list of domains supported by the gateway.
override void SetStaticInstance(ISystemConfiguration Configuration)
Sets the static instance of the configuration.
string HumanReadableDescriptionLanguage
Language of HumanReadableDescription.
override string ConfigPrivilege
Minimum required privilege for a user to be allowed to change the configuration defined by the class.
string HumanReadableName
Human-readable name of domain
bool CustomCA
If a custom Certificate Authority is to be used
override Task UnregisterSetup(HttpServer WebServer)
Unregisters the setup object.
string OpenSslPath
Path to OpenSSL
AlternativeField[] LocalizedNames
Localized names of domain
byte[] PrivateKey
Private Key
int DynDnsInterval
Interval (in seconds) for checking if the IP address has changed.
string[] AlternativeDomains
Alternative domain names
bool IsDomainRegistered(string Name)
Checks if a domain name is registered.
string CheckIpScript
Script to use to evaluate the current IP Address.
async Task< string > CheckDynamicIp(string DomainName, bool EnvironmentSetup, EventHandlerAsync< string > Status)
Checks if Dynamic IP configuration is correct
byte[] PFX
PFX container for certificate and private key, if available.
string ContactEMail
Contact e-mail address
override Task< bool > SimplifiedConfiguration()
Simplified configuration by configuring simple default values.
byte[] Certificate
Certificate
override async Task< bool > EnvironmentConfiguration()
Environment configuration by configuring values available in environment variables.
string UrlToS
CA Terms of Service
bool AcceptToS
If the CA Terms of Service has been accepted.
override int Priority
Priority of the setting. Configurations are sorted in ascending order.
async Task< string > RemoveDomain(string Name, EventHandlerAsync< string > Status)
Removes a domain from the list of domains supported by the gateway.
override string Resource
Resource to be redirected to, to perform the configuration.
bool UseDomainName
If the server uses a domain name.
const string GATEWAY_DYNDNS_UPDATE
Script to use to update the current public IP address of the gateway in the Dynamic DNS service.
string DynDnsAccount
Account Name for the Dynamic DNS service
override Task InitSetup(HttpServer WebServer)
Initializes the setup object.
bool DynamicDns
If the server uses a dynamic DNS service.
bool UseEncryption
If the server uses server-side encryption.
override Task< string > Title(Language Language)
Gets a title for the system configuration.
string DynDnsPassword
Password for the Dynamic DNS service
const string GATEWAY_DOMAIN_ALT
Comma-separated list of alternative domain names for the gateway, if defined.
bool HasToS
If the CA has a Terms of Service.
string Domain
Principal domain name
string HumanReadableNameLanguage
Language of HumanReadableName.
const string GATEWAY_DYNDNS_CHECK
Script to use to check the current public IP address of the gateway.
void LogEnvironmentError(string EnvironmentVariable, object Value)
Logs an error to the event log, telling the operator an environment variable value contains an error.
Abstract base class for multi-step system configurations.
int Step
Configuration step.
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
Represents an HTTP request.
HttpRequestHeader Header
Request header.
bool HasData
If the request has data.
string SubPath
Sub-path. If a resource is found handling the request, this property contains the trailing sub-path o...
async Task< object > DecodeDataAsync()
Decodes data sent in request.
Base class for all HTTP resources.
Represets a response of an HTTP client request.
async Task Write(byte[] Data)
Returns binary data in the response.
Encoding Encoding
Gets the System.Text.Encoding in which the output is written.
Implements an HTTP server.
static Variables CreateVariables()
Creates a new collection of variables, that contains access to the global set of variables.
HttpResource Register(HttpResource Resource)
Registers a resource with the server.
bool Unregister(HttpResource Resource)
Unregisters a resource from the server.
The server has not found anything matching the Request-URI. No indication is given of whether the con...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
static async Task Update(object Object)
Updates an object in the database.
Contains information about a language.
Task< string > GetStringAsync(Type Type, int Id, string Default)
Gets the string value of a string ID. If no such string exists, a string is created with the default ...
Static class managing persistent settings.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
static async Task< bool > SetAsync(string Key, string Value)
Sets a string-valued setting.
Class managing a script expression.
The request specified an account that does not exist
Represents an ACME account.
Task< AcmeAccount > NewKey()
Creates a new key for the account.
Task< AcmeOrder > OrderCertificate(string[] Domains, DateTime? NotBefore, DateTime? NotAfter)
Orders certificate.
string[] Contact
Optional array of URLs that the server can use to contact the client for issues related to this accou...
DateTime? CreatedAt
Date and time of creation, if available.
Task< AcmeAccount > Update(string[] Contact)
Updates the account.
string InitialIp
Initial IP address.
AcmeAccountStatus Status
The status of this account.
Represents an ACME authorization.
Task< AcmeAuthorization > Poll()
Gets the current state of the order.
string Value
The identifier itself.
AcmeChallenge[] Challenges
For pending authorizations, the challenges that the client can fulfill in order to prove possession o...
AcmeAuthorizationStatus Status
The status of this authorization.
Base class of all ACME challenges.
AcmeChallengeStatus Status
The status of this challenge.
Task< AcmeChallenge > AcknowledgeChallenge()
Acknowledges the challenge.
Implements an ACME client for the generation of certificates using ACME-compliant certificate servers...
RSAParameters ExportAccountKey(bool IncludePrivateParameters)
Exports the account key.
async Task< AcmeAccount > GetAccount()
Gets the account object from the ACME server.
async Task< AcmeAccount > CreateAccount(string[] ContactURLs, bool TermsOfServiceAgreed)
Creates an account on the ACME server.
async Task< AcmeDirectory > GetDirectory()
Gets the ACME directory.
Represents an ACME directory.
Uri TermsOfService
URL to terms of service.
Uri Website
URL to website.
bool ExternalAccountRequired
If an external account is required.
Represents an ACME HTTP challenge.
Represents an ACME order.
AcmeOrderStatus Status
The status of this order.
Uri Certificate
A URL for the certificate that has been issued in response to this order.
async Task< AcmeAuthorization[]> GetAuthorizations()
Gets current authorization objects.
Task< X509Certificate2[]> DownloadCertificate()
Downloads the certificate.
Task< AcmeOrder > FinalizeOrder(CertificateRequest CertificateRequest)
Finalize order.
Contains methods for simple hash calculations.
static string BinaryToString(byte[] Data)
Converts an array of bytes to a string with their hexadecimal representations (in lower case).
Contains information about a Certificate Signing Request (CSR).
Encodes data using the Distinguished Encoding Rules (DER), as defined in X.690
byte[] ToArray()
Converts the generated output to a byte arary.
RSA with SHA-256 signatures
Abstract base class for signature algorithms
abstract void ExportPrivateKey(DerEncoder Output)
Exports the private key using DER.
Interface for system configurations. The gateway will scan all module for system configuration classe...
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.
AcmeOrderStatus
ACME Order status enumeration
AcmeAuthorizationStatus
ACME Authorization status enumeration