CertificateEngine.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Formats.Asn1;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Security.Cryptography.X509Certificates;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. using System.Timers;
  11. using ACMESharp.Authorizations;
  12. using ACMESharp.Protocol;
  13. using ACMESharp.Protocol.Resources;
  14. using GenHTTP.Api.Content;
  15. using GenHTTP.Api.Infrastructure;
  16. using GenHTTP.Api.Protocol;
  17. using GenHTTP.Engine;
  18. using GenHTTP.Modules.IO;
  19. using GenHTTP.Modules.Practices;
  20. using InABox.Core;
  21. using Microsoft.VisualBasic;
  22. using Newtonsoft.Json;
  23. using PRSServices;
  24. using Timer = System.Timers.Timer;
  25. namespace PRSServer;
  26. public class CertificateHandler : IHandler
  27. {
  28. private static readonly string AcmeHttp01PathPrefix =
  29. Http01ChallengeValidationDetails.HttpPathPrefix.Trim('/');
  30. public CertificateHandler(IHandler parent)
  31. {
  32. Parent = parent;
  33. }
  34. public IDictionary<string, Http01ChallengeValidationDetails> Http01Responses { get; set; }
  35. = new Dictionary<string, Http01ChallengeValidationDetails>();
  36. public IHandler Parent { get; init; }
  37. public IAsyncEnumerable<ContentElement> GetContentAsync(IRequest request)
  38. {
  39. throw new NotImplementedException();
  40. }
  41. public ValueTask<IResponse?> HandleAsync(IRequest request)
  42. {
  43. var fullPath = request.Target.Path.ToString().Trim('/');
  44. Logger.Send(LogType.Information, "", "Running ACME Challenge Request Handler");
  45. IResponseBuilder response;
  46. if (Http01Responses.TryGetValue(fullPath, out var httpDetails))
  47. {
  48. Logger.Send(LogType.Information, "",
  49. string.Format("Found match on [{0}] with response [{1}]", fullPath, httpDetails.HttpResourceValue));
  50. response = request.Respond()
  51. .Type(new FlexibleContentType(httpDetails.HttpResourceContentType))
  52. .Content(Resource.FromString(httpDetails.HttpResourceValue).Build());
  53. }
  54. else
  55. {
  56. Logger.Send(LogType.Information, "", string.Format("NO MATCH FOUND ON [{0}]", fullPath));
  57. response = request.Respond()
  58. .Status(ResponseStatus.NotFound)
  59. .Content(Resource.FromString("No matching ACME response path").Build());
  60. }
  61. return new ValueTask<IResponse?>(response.Build());
  62. }
  63. public ValueTask PrepareAsync()
  64. {
  65. return new ValueTask();
  66. }
  67. public IEnumerable<ContentElement> GetContent(IRequest request)
  68. {
  69. return Enumerable.Empty<ContentElement>();
  70. }
  71. public bool AddChallengeHandling(IChallengeValidationDetails chlngDetails)
  72. {
  73. if (chlngDetails is not Http01ChallengeValidationDetails httpDetails)
  74. {
  75. Logger.Send(LogType.Information, "", "Unable to handle non-Http01 Challenge details");
  76. return false;
  77. }
  78. var fullPath = httpDetails.HttpResourcePath.Trim('/');
  79. Logger.Send(LogType.Information, "", string.Format("Handling Challenges with HTTP full path of [{0}]", fullPath));
  80. Http01Responses[fullPath] = httpDetails;
  81. return true;
  82. }
  83. }
  84. public class CertificateHandlerBuilder : IHandlerBuilder<CertificateHandlerBuilder>
  85. {
  86. private readonly List<IConcernBuilder> _Concerns = new();
  87. public CertificateHandlerBuilder(CertificateEngine engine)
  88. {
  89. Engine = engine;
  90. }
  91. private CertificateEngine Engine { get; }
  92. public CertificateHandlerBuilder Add(IConcernBuilder concern)
  93. {
  94. _Concerns.Add(concern);
  95. return this;
  96. }
  97. public IHandler Build(IHandler parent)
  98. {
  99. return Concerns.Chain(parent, _Concerns, p =>
  100. {
  101. Engine.Handler = new CertificateHandler(p);
  102. return Engine.Handler;
  103. });
  104. }
  105. }
  106. public class CertificateEngine : Engine<CertificateEngineProperties>
  107. {
  108. private readonly DateTime _scheduleTime;
  109. public CertificateHandler? Handler;
  110. private IServerHost? host;
  111. private static string ChallengeType = AcmeState.Http01ChallengeType;
  112. static CertificateEngine()
  113. {
  114. CertificateFolder = Path.Combine(
  115. Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location ?? "") ?? "",
  116. "certs"
  117. );
  118. }
  119. private static T? Load<T>(string path)
  120. {
  121. if (!File.Exists(path))
  122. return default;
  123. if (typeof(T) == typeof(Stream)) return (T)(object)new FileStream(path, FileMode.Open);
  124. if (typeof(T) == typeof(byte[])) return (T)(object)File.ReadAllBytes(path);
  125. return JsonConvert.DeserializeObject<T>(File.ReadAllText(path)) ?? default;
  126. }
  127. protected static void Save<T>(string path, T value)
  128. {
  129. if (typeof(Stream).IsAssignableFrom(typeof(T)))
  130. {
  131. var rs = (Stream)(object)value;
  132. using (var ws = new FileStream(path, FileMode.Create))
  133. {
  134. rs.CopyTo(ws);
  135. }
  136. }
  137. else if (typeof(T) == typeof(byte[]))
  138. {
  139. var ba = (byte[])(object)value;
  140. File.WriteAllBytes(path, ba);
  141. }
  142. else
  143. {
  144. File.WriteAllText(path,
  145. JsonConvert.SerializeObject(value, Formatting.Indented));
  146. }
  147. }
  148. public void DeleteFile(string filename)
  149. {
  150. try
  151. {
  152. File.Delete(filename);
  153. }
  154. catch (Exception) { }
  155. }
  156. public void RefreshCertificateFile(CancellationToken cancellationToken)
  157. {
  158. Logger.Send(LogType.Information, "", "Updating HTTPS certificate...");
  159. if (!Directory.Exists(CertificateFolder))
  160. {
  161. Logger.Send(LogType.Information, "", "Creating certificate folder...");
  162. Directory.CreateDirectory(CertificateFolder);
  163. }
  164. DeleteFile(OrderFile);
  165. DeleteFile(CertificateFile);
  166. //DeleteFile(AuthorizationsFile);
  167. ServiceDirectory = Load<ServiceDirectory>(ServiceDirectoryFile);
  168. Account = Load<AccountDetails>(AccountFile);
  169. AccountKey = Load<ExamplesAccountKey>(AccountKeyFile);
  170. Order = Load<OrderDetails>(OrderFile);
  171. Authorizations = Load<Dictionary<string, Authorization>>(AuthorizationsFile);
  172. var certRaw = Load<byte[]>(CertificateFile);
  173. if (certRaw?.Length > 0)
  174. Certificate = new X509Certificate2(certRaw);
  175. DnsNames = Properties.ParseDomainNames();
  176. AccountContactEmails = Properties.AccountContactEmails.Split(',');
  177. Logger.Send(LogType.Information, "", "Initializing HTTP Handler");
  178. host = Host.Create()
  179. .Handler(new CertificateHandlerBuilder(this))
  180. .Defaults()
  181. .Port(80)
  182. .Start();
  183. // We delay for 5 seconds just to give other parts of
  184. // the service (like request handling) to get in place
  185. Task.Delay(5 * 1000, cancellationToken);
  186. if (DoTheWork())
  187. {
  188. Logger.Send(LogType.Information, "", "HTTPS Refresh Complete!");
  189. }
  190. else
  191. {
  192. Logger.Send(LogType.Information, "", "HTTPS Refresh Failed");
  193. }
  194. host.Stop();
  195. }
  196. private bool DoTheWork()
  197. {
  198. try
  199. {
  200. Logger.Send(LogType.Information, "", "** DOING WORKING *****************************************");
  201. Logger.Send(LogType.Information, "", $"DNS Names: {Properties.DomainNames}");
  202. var acmeUrl = new Uri(Properties.CaUrl);
  203. using var acme = new AcmeProtocolClient(acmeUrl, usePostAsGet: true);
  204. var task = DoTheWorkAsync(acme);
  205. task.Wait();
  206. return task.Result;
  207. }
  208. catch(Exception e)
  209. {
  210. Logger.Send(LogType.Error, "", CoreUtils.FormatException(e));
  211. return false;
  212. }
  213. }
  214. private async Task ClearAuthorizations(AcmeProtocolClient acme)
  215. {
  216. if(Authorizations != null)
  217. {
  218. foreach (var (url, authorization) in Authorizations)
  219. {
  220. if (authorization.Status != AcmeState.ValidStatus)
  221. {
  222. try
  223. {
  224. await acme.DeactivateAuthorizationAsync(url);
  225. }
  226. catch(Exception e)
  227. {
  228. Logger.Send(LogType.Error, "", $"Could not deactivate authorization: {CoreUtils.FormatException(e)}");
  229. }
  230. }
  231. }
  232. Authorizations.Clear();
  233. Save(AuthorizationsFile, Authorizations);
  234. }
  235. else
  236. {
  237. Authorizations = new Dictionary<string, Authorization>();
  238. }
  239. }
  240. private async Task<bool> DoTheWorkAsync(AcmeProtocolClient acme)
  241. {
  242. ServiceDirectory = await acme.GetDirectoryAsync();
  243. Save(ServiceDirectoryFile, ServiceDirectory);
  244. acme.Directory = ServiceDirectory;
  245. Save(TermsOfServiceFile, await acme.GetTermsOfServiceAsync());
  246. // This line basically has to be called before all ACME things.
  247. await acme.GetNonceAsync();
  248. ClearAuthorizations(acme).Wait();
  249. if (!await ResolveAccount(acme))
  250. return false;
  251. if (!await ResolveOrder(acme))
  252. return false;
  253. if (!await ResolveChallenges(acme))
  254. return false;
  255. if (!await ResolveAuthorizations(acme))
  256. return false;
  257. if (!await ResolveCertificate(acme))
  258. return false;
  259. return true;
  260. }
  261. private async Task<bool> ResolveAccount(AcmeProtocolClient acme)
  262. {
  263. // TODO: All this ASSUMES a fixed key type/size for now
  264. if (Account == null || AccountKey == null)
  265. {
  266. var contacts = AccountContactEmails.Where(x => !string.IsNullOrEmpty(x)).Select(x => $"mailto:{x}");
  267. Logger.Send(LogType.Information, "", "Creating ACME Account");
  268. Account = await acme.CreateAccountAsync(
  269. contacts,
  270. Properties.AcceptTermsOfService);
  271. AccountKey = new ExamplesAccountKey
  272. {
  273. KeyType = acme.Signer.JwsAlg,
  274. KeyExport = acme.Signer.Export()
  275. };
  276. Save(AccountFile, Account);
  277. Save(AccountKeyFile, AccountKey);
  278. acme.Account = Account;
  279. }
  280. else
  281. {
  282. acme.Account = Account;
  283. acme.Signer.Import(AccountKey.KeyExport);
  284. }
  285. return true;
  286. }
  287. private async Task<bool> ResolveOrder(AcmeProtocolClient acme)
  288. {
  289. var now = DateTime.Now;
  290. if (!string.IsNullOrEmpty(Order?.OrderUrl))
  291. {
  292. Logger.Send(LogType.Information, "", "Existing Order found; refreshing");
  293. Order = await acme.GetOrderDetailsAsync(Order.OrderUrl, Order);
  294. }
  295. if (Order?.Payload?.Error != null)
  296. {
  297. Logger.Send(LogType.Information, "", "Existing Order reported an Error:");
  298. Logger.Send(LogType.Information, "", JsonConvert.SerializeObject(Order.Payload.Error));
  299. Logger.Send(LogType.Information, "", "Resetting existing order");
  300. Order = null;
  301. }
  302. if (AcmeState.InvalidStatus == Order?.Payload.Status)
  303. {
  304. Logger.Send(LogType.Information, "", "Existing Order is INVALID; resetting");
  305. Order = null;
  306. }
  307. if (!DateTime.TryParse(Order?.Payload?.Expires, out var orderExpires)
  308. || orderExpires < now)
  309. {
  310. Logger.Send(LogType.Information, "", "Existing Order is EXPIRED; resetting");
  311. Order = null;
  312. }
  313. if (DateTime.TryParse(Order?.Payload?.NotAfter, out var orderNotAfter)
  314. && orderNotAfter < now)
  315. {
  316. Logger.Send(LogType.Information, "", "Existing Order is OUT-OF-DATE; resetting");
  317. Order = null;
  318. }
  319. if (Order?.Payload == null)
  320. {
  321. Logger.Send(LogType.Information, "", "Creating NEW Order");
  322. Order = await acme.CreateOrderAsync(DnsNames);
  323. }
  324. Save(OrderFile, Order);
  325. return true;
  326. }
  327. private async Task<bool> ResolveChallenges(AcmeProtocolClient acme)
  328. {
  329. if (AcmeState.PendingStatus == Order?.Payload?.Status)
  330. {
  331. Logger.Send(LogType.Information, "", "Order is pending, resolving Authorizations");
  332. if (Authorizations == null)
  333. Authorizations = new Dictionary<string, Authorization>();
  334. foreach (var authzUrl in Order.Payload.Authorizations)
  335. {
  336. var authz = await acme.GetAuthorizationDetailsAsync(authzUrl);
  337. Authorizations[authzUrl] = authz;
  338. if (AcmeState.PendingStatus == authz.Status)
  339. foreach (var chlng in authz.Challenges)
  340. if (string.IsNullOrEmpty(ChallengeType) || ChallengeType == chlng.Type)
  341. {
  342. var chlngValidation = AuthorizationDecoder.DecodeChallengeValidation(
  343. authz, chlng.Type, acme.Signer);
  344. if (Handler.AddChallengeHandling(chlngValidation))
  345. {
  346. Logger.Send(LogType.Information, "", "Challenge Handler has handled challenge:");
  347. Logger.Send(LogType.Information, "", JsonConvert.SerializeObject(chlngValidation, Formatting.Indented));
  348. var chlngUpdated = await acme.AnswerChallengeAsync(chlng.Url);
  349. if (chlngUpdated.Error != null)
  350. {
  351. Logger.Send(LogType.Error, "", "Submitting Challenge Answer reported an error:");
  352. Logger.Send(LogType.Error, "", JsonConvert.SerializeObject(chlngUpdated.Error));
  353. }
  354. }
  355. Logger.Send(LogType.Information, "", "Refreshing Authorization status");
  356. authz = await acme.GetAuthorizationDetailsAsync(authzUrl);
  357. if (AcmeState.PendingStatus != authz.Status)
  358. break;
  359. }
  360. }
  361. Save(AuthorizationsFile, Authorizations);
  362. Logger.Send(LogType.Information, "", "Refreshing Order status");
  363. Order = await acme.GetOrderDetailsAsync(Order.OrderUrl, Order);
  364. Save(OrderFile, Order);
  365. }
  366. return true;
  367. }
  368. private async Task<bool> ResolveAuthorizations(AcmeProtocolClient acme)
  369. {
  370. if (AcmeState.InvalidStatus == Order?.Payload?.Status)
  371. {
  372. Logger.Send(LogType.Information, "", "Current Order is INVALID; aborting");
  373. return false;
  374. }
  375. if (AcmeState.ValidStatus == Order?.Payload?.Status)
  376. {
  377. Logger.Send(LogType.Information, "", "Current Order is already VALID; skipping");
  378. return true;
  379. }
  380. var now = DateTime.Now;
  381. do
  382. {
  383. if (Authorizations == null)
  384. Authorizations = new Dictionary<string, Authorization>();
  385. // Wait for all Authorizations to be valid or any one to go invalid
  386. var validCount = 0;
  387. var invalidCount = 0;
  388. foreach (var authz in Authorizations)
  389. switch (authz.Value.Status)
  390. {
  391. case AcmeState.ValidStatus:
  392. ++validCount;
  393. break;
  394. case AcmeState.InvalidStatus:
  395. ++invalidCount;
  396. break;
  397. }
  398. if (validCount == Authorizations.Count)
  399. {
  400. Logger.Send(LogType.Information, "", string.Format("All Authorizations ({0}) are valid", validCount));
  401. break;
  402. }
  403. if (invalidCount > 0)
  404. {
  405. Logger.Send(LogType.Error, "", string.Format("Found {0} invalid Authorization(s); ABORTING", invalidCount));
  406. return false;
  407. }
  408. Logger.Send(LogType.Information, "", string.Format("Found {0} Authorization(s) NOT YET valid", Authorizations.Count - validCount));
  409. if (now.AddSeconds(Properties.WaitForAuthorizations) < DateTime.Now)
  410. {
  411. Logger.Send(LogType.Error, "", "Timed out waiting for Authorizations; ABORTING");
  412. return false;
  413. }
  414. // We wait in 5s increments
  415. await Task.Delay(5000);
  416. foreach (var authzUrl in Order.Payload.Authorizations)
  417. // Update all the Authorizations still pending
  418. if (AcmeState.PendingStatus == Authorizations[authzUrl].Status)
  419. Authorizations[authzUrl] = await acme.GetAuthorizationDetailsAsync(authzUrl);
  420. } while (true);
  421. Save(AuthorizationsFile, Authorizations);
  422. return true;
  423. }
  424. private async Task<bool> ResolveCertificate(AcmeProtocolClient acme)
  425. {
  426. if (Certificate != null)
  427. {
  428. Logger.Send(LogType.Information, "", "Certificate is already resolved");
  429. return true;
  430. }
  431. CertPrivateKey? key = null;
  432. Logger.Send(LogType.Information, "", "Refreshing Order status");
  433. Order = await acme.GetOrderDetailsAsync(Order.OrderUrl, Order);
  434. Save(OrderFile, Order);
  435. if (AcmeState.PendingStatus == Order.Payload.Status || AcmeState.ReadyStatus == Order.Payload.Status)
  436. {
  437. Logger.Send(LogType.Information, "", "Generating CSR");
  438. byte[] csr;
  439. switch (Properties.CertificateKeyAlgor)
  440. {
  441. case "rsa":
  442. key = CertHelper.GenerateRsaPrivateKey(
  443. Properties.CertificateKeySize ?? CertificateEngineProperties.DefaultRsaKeySize);
  444. csr = CertHelper.GenerateRsaCsr(DnsNames, key);
  445. break;
  446. case "ec":
  447. key = CertHelper.GenerateEcPrivateKey(
  448. Properties.CertificateKeySize ?? CertificateEngineProperties.DefaultEcKeySize);
  449. csr = CertHelper.GenerateEcCsr(DnsNames, key);
  450. break;
  451. default:
  452. throw new Exception("Unknown Certificate Key Algorithm: "
  453. + Properties.CertificateKeyAlgor);
  454. }
  455. using (var keyPem = new MemoryStream())
  456. {
  457. CertHelper.ExportPrivateKey(key, EncodingFormat.PEM, keyPem);
  458. keyPem.Position = 0L;
  459. Save(CertificateKeysFile, keyPem);
  460. }
  461. Save(CertificateRequestFile, csr);
  462. Logger.Send(LogType.Information, "", "Finalizing Order");
  463. Order = await acme.FinalizeOrderAsync(Order.Payload.Finalize, csr);
  464. Save(OrderFile, Order);
  465. }
  466. if (string.IsNullOrEmpty(Order.Payload.Certificate))
  467. {
  468. Logger.Send(LogType.Information, "", "Order Certificate is NOT READY YET");
  469. var now = DateTime.Now;
  470. do
  471. {
  472. Logger.Send(LogType.Information, "", "Waiting...");
  473. // We wait in 5s increments
  474. await Task.Delay(5000);
  475. Order = await acme.GetOrderDetailsAsync(Order.OrderUrl, Order);
  476. Save(OrderFile, Order);
  477. if (!string.IsNullOrEmpty(Order.Payload.Certificate))
  478. break;
  479. if (DateTime.Now < now.AddSeconds(Properties.WaitForCertificate))
  480. {
  481. Logger.Send(LogType.Information, "", "Timed Out!");
  482. return false;
  483. }
  484. } while (true);
  485. }
  486. if (AcmeState.ValidStatus != Order.Payload.Status)
  487. {
  488. Logger.Send(LogType.Information, "", "Order is NOT VALID");
  489. return false;
  490. }
  491. Logger.Send(LogType.Information, "", "Retreiving Certificate");
  492. var certBytes = await acme.GetOrderCertificateAsync(Order);
  493. Save(CertificateChainFile, certBytes);
  494. if (key == null)
  495. {
  496. Logger.Send(LogType.Information, "", "Loading private key");
  497. key = CertHelper.ImportPrivateKey(EncodingFormat.PEM, Load<Stream>(CertificateKeysFile));
  498. }
  499. using (var crtStream = new MemoryStream(certBytes))
  500. using (var pfxStream = new MemoryStream())
  501. {
  502. Logger.Send(LogType.Information, "", "Reading in Certificate chain (PEM)");
  503. var cert = CertHelper.ImportCertificate(EncodingFormat.PEM, crtStream);
  504. Logger.Send(LogType.Information, "", "Writing out Certificate archive (PKCS12)");
  505. CertHelper.ExportArchive(key, new[] { cert }, ArchiveFormat.PKCS12, pfxStream);
  506. pfxStream.Position = 0L;
  507. Save(CertificateFile, pfxStream);
  508. }
  509. Logger.Send(LogType.Information, "", "Loading PKCS12 archive as active certificate");
  510. Certificate = new X509Certificate2(Load<byte[]>(CertificateFile));
  511. return true;
  512. }
  513. private void CheckForRefresh()
  514. {
  515. Logger.Send(LogType.Information, "", "Checking for refresh...");
  516. if (!Directory.Exists(CertificateFolder))
  517. {
  518. Logger.Send(LogType.Information, "", "Creating certificate folder...");
  519. Directory.CreateDirectory(CertificateFolder);
  520. }
  521. var certRaw = Load<byte[]>(CertificateFile);
  522. if (certRaw?.Length > 0)
  523. {
  524. Certificate = new X509Certificate2(certRaw);
  525. }
  526. if(Certificate == null)
  527. {
  528. RefreshCertificateFile(CancellationToken.None);
  529. return;
  530. }
  531. DateTime now = DateTime.Now;
  532. TimeSpan startDiff = now - Certificate.NotBefore;
  533. TimeSpan endDiff = Certificate.NotAfter - now;
  534. // If the certificate will expire in less than 30 days or if the certificate is not yet valid
  535. if (startDiff < TimeSpan.Zero || endDiff < TimeSpan.FromDays(30))
  536. {
  537. RefreshCertificateFile(CancellationToken.None);
  538. return;
  539. }
  540. var names = GetDnsNames(Certificate).ToHashSet();
  541. var requiredNames = Properties.ParseDomainNames();
  542. if(requiredNames.Any(x => !names.Contains(x)))
  543. {
  544. RefreshCertificateFile(CancellationToken.None);
  545. return;
  546. }
  547. Logger.Send(LogType.Information, "", "Refresh not required!");
  548. }
  549. public override void Run()
  550. {
  551. System.Threading.Timer timer = new System.Threading.Timer(
  552. (o) => { CheckForRefresh(); },
  553. null,
  554. 3000,
  555. 1000 * 60 * 60 * 24
  556. );
  557. Thread.Sleep(Timeout.Infinite);
  558. }
  559. public override void Stop()
  560. {
  561. host?.Stop();
  562. }
  563. #region File Names
  564. public static string CertificateFolder { get; }
  565. private string ServiceDirectoryFile => Path.Combine(CertificateFolder, "00-ServiceDirectory.json");
  566. private string TermsOfServiceFile => Path.Combine(CertificateFolder, "05-TermsOfService");
  567. private string AccountFile => Path.Combine(CertificateFolder, "10-Account.json");
  568. private string AccountKeyFile => Path.Combine(CertificateFolder, "15-AccountKey.json");
  569. private string OrderFile => Path.Combine(CertificateFolder, "50-Order.json");
  570. private string AuthorizationsFile => Path.Combine(CertificateFolder, "52-Authorizations.json");
  571. private string CertificateKeysFile => Path.Combine(CertificateFolder, "70-CertificateKeys.pem");
  572. private string CertificateRequestFile => Path.Combine(CertificateFolder, "72-CertificateRequest.der");
  573. private string CertificateChainFile => Path.Combine(CertificateFolder, "74-CertificateChain.pem");
  574. public static string CertificateFile => Path.Combine(CertificateFolder, "80-Certificate.pfx");
  575. #endregion
  576. #region Extra State Properties
  577. private ServiceDirectory? ServiceDirectory;
  578. private AccountDetails? Account;
  579. private ExamplesAccountKey? AccountKey;
  580. private OrderDetails? Order;
  581. private Dictionary<string, Authorization>? Authorizations;
  582. private X509Certificate2? Certificate;
  583. private IEnumerable<string> DnsNames;
  584. private IEnumerable<string> AccountContactEmails;
  585. #endregion
  586. }