示例#1
0
        public Challenge Decode(IdentifierPart ip, ChallengePart cp, ISigner signer)
        {
            if (cp.Type != AcmeProtocol.CHALLENGE_TYPE_HTTP)
                throw new InvalidDataException("unsupported Challenge type")
                    .With("challengeType", cp.Type)
                    .With("supportedChallengeTypes", AcmeProtocol.CHALLENGE_TYPE_HTTP);

            //var token = (string)cp["token"];
            var token = cp.Token;

            // This response calculation is described in:
            //    https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.2

            var keyAuthz = JwsHelper.ComputeKeyAuthorization(signer, token);
            var path = $"{AcmeProtocol.HTTP_CHALLENGE_PATHPREFIX}{token}";
            var url = $"http://{ip.Value}/{path}";


            var ca = new HttpChallengeAnswer
            {
                KeyAuthorization = keyAuthz,
            };

            var c = new HttpChallenge(cp.Type, ca)
            {
                Token = token,
                FileUrl = url,
                FilePath = path,
                FileContent = keyAuthz,
            };

            return c;
        }
        private string GetAnswerPath(HttpChallenge httpChallenge)
        {
            // We need to strip off any leading '/' in the path
            var filePath = httpChallenge.FilePath;

            if (filePath.StartsWith("/", StringComparison.OrdinalIgnoreCase))
            {
                filePath = filePath.Substring(1);
            }
            var answerPath = Environment.ExpandEnvironmentVariables(Path.Combine(WebRootPath(), filePath));

            return(answerPath);
        }
        private async Task <string> GetAnswerPath(HttpChallenge httpChallenge)
        {
            var rootDir = await this.pathProvider.WebRootPath(false);

            // We need to strip off any leading '/' in the path
            var filePath = httpChallenge.FilePath;

            if (filePath.StartsWith("/", StringComparison.OrdinalIgnoreCase))
            {
                filePath = filePath.Substring(1);
            }
            var answerPath = Environment.ExpandEnvironmentVariables(Path.Combine(rootDir, filePath));

            return(answerPath);
        }
示例#4
0
        private async Task <string> GetAnswerPath(HttpChallenge httpChallenge)
        {
            var root = await this.pathProvider.WebRootPath(true);

            // We need to strip off any leading '/' in the path
            var filePath = httpChallenge.FilePath;

            if (filePath.StartsWith("/", StringComparison.OrdinalIgnoreCase))
            {
                filePath = filePath.Substring(1);
            }
            var answerPath = root + "/" + filePath;

            return(answerPath);
        }
示例#5
0
        /// <summary>
        /// Gets challenges used to verify domain ownership.
        /// </summary>
        /// <param name="account">Existing account.</param>
        /// <param name="acmeCertificateFulfillmentPromise">The certificate fulfillment promise retrieved from the RequestCertificate call.</param>
        /// <param name="challengeType">The challenge type expected back.</param>
        /// <returns>Challenge used to verify domain ownership</returns>
        /// <remarks>If requesting a challenge for a wildcard domain, only dns challenge is supported.</remarks>
        /// <exception cref="NotSupportedException">If the challenge type is not supported.</exception>
        /// <exception cref="AcmeProtocolException">On all other Acme related exceptions</exception>
        public async Task <ChallengeCollection> GetChallengesAsync(AcmeAccount account, AcmeCertificateFulfillmentPromise acmeCertificateFulfillmentPromise, ChallengeType challengeType)
        {
            var response = await _acmeApi.GetChallengesAsync(acmeCertificateFulfillmentPromise);

            var errorResponse = response.Where(t => t.Status == AcmeApiResponseStatus.Error);

            if (errorResponse.Any())
            {
                throw new AcmeProtocolException(string.Join(" | ", errorResponse.Select(t => t.Message)));
            }

            ChallengeCollection challenges = new ChallengeCollection();

            foreach (var resp in response)
            {
                AcmeChallenge sChallenge = resp.Data.Challenges.FirstOrDefault(t => t.Type.Equals(challengeType.Value));
                if (sChallenge == null)
                {
                    throw new NotSupportedException($"{challengeType.Value} challenge type not supported in this context.");
                }
                IAcmeChallengeContent challengeContent = null;
                switch (challengeType.Value)
                {
                case ProtoacmeContants.CHALLENGE_HTTP:
                    challengeContent = new HttpChallenge(account, sChallenge, resp.Data.Identifier?.Value);
                    challenges.Add(challengeContent);
                    break;

                case ProtoacmeContants.CHALLENGE_DNS:
                    challengeContent = new DnsChallenge(account, sChallenge, resp.Data.Identifier?.Value);
                    challenges.Add(challengeContent);
                    break;

                case ProtoacmeContants.CHALLENGE_TLS:
                    challengeContent = new TlsChallenge(account, sChallenge, resp.Data.Identifier?.Value);
                    challenges.Add(challengeContent);
                    break;

                default:
                    break;
                }
            }

            return(challenges);
        }
        /// <summary>
        /// Can be used to write out server specific configuration, to handle extensionless files etc.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="answerPath"></param>
        /// <param name="token"></param>
        public virtual void BeforeAuthorize(Target target, HttpChallenge challenge)
        {
            if (target.IIS == true)
            {
                _log.Debug("Writing web.config");
                var destination = CombinePath(target.WebRootPath, challenge.FilePath.Replace(challenge.Token, "web.config"));
                var content     = GetWebConfig(target);

                if (FileExists(destination))
                {
                    var existingContent = ReadFile(destination);
                    if (existingContent == content)
                    {
                        // do not touch web.config if it has allredy the required content.
                        return;
                    }
                }

                WriteFile(destination, content);
            }
        }
示例#7
0
        private void EditFile(HttpChallenge httpChallenge, bool delete)
        {
            var filePath = httpChallenge.FilePath;

            // We need to strip off any leading '/' in the path or
            // else it creates a path with an empty leading segment
            if (filePath.StartsWith("/"))
            {
                filePath = filePath.Substring(1);
            }

            using (var s3 = new Amazon.S3.AmazonS3Client(
                       CommonParams.ResolveCredentials(),
                       CommonParams.RegionEndpoint))
            {
                if (delete)
                {
                    var s3Requ = new Amazon.S3.Model.DeleteObjectRequest
                    {
                        BucketName = BucketName,
                        Key        = filePath,
                    };
                    var s3Resp = s3.DeleteObject(s3Requ);
                }
                else
                {
                    var s3Requ = new Amazon.S3.Model.PutObjectRequest
                    {
                        BucketName  = BucketName,
                        Key         = filePath,
                        ContentBody = httpChallenge.FileContent,
                        ContentType = ContentType,
                        CannedACL   = S3CannedAcl,
                    };
                    var s3Resp = s3.PutObject(s3Requ);
                }
            }
        }
        private async Task <CloudBlockBlob> GetBlob(HttpChallenge challenge)
        {
            var client    = storageAccount.CreateCloudBlobClient();
            var container = client.GetContainerReference(containerName);
            await container.CreateIfNotExistsAsync();

            if (!"$web".Equals(containerName, StringComparison.InvariantCultureIgnoreCase) && container.Properties.PublicAccess != BlobContainerPublicAccessType.Blob)
            {
                await container.SetPermissionsAsync(new BlobContainerPermissions()
                {
                    PublicAccess = BlobContainerPublicAccessType.Blob
                });
            }
            // We need to strip off any leading '/' in the path
            var filePath = challenge.FilePath;

            if (filePath.StartsWith("/", StringComparison.OrdinalIgnoreCase))
            {
                filePath = filePath.Substring(1);
            }
            var blob = container.GetBlockBlobReference(filePath);

            return(blob);
        }
 public override Task CleanupChallengeFile(HttpChallenge challenge)
 {
     File.Delete(GetAnswerPath(challenge));
     return(Task.CompletedTask);
 }
 public override Task CleanupChallengeFile(HttpChallenge challenge)
 {
     return(Task.CompletedTask);
 }
        public override async Task PersistsChallengeFile(HttpChallenge challenge)
        {
            var answerPath = GetAnswerPath(challenge);

            await WriteFile(answerPath, challenge.FileContent);
        }
        private void EditFile(HttpChallenge httpChallenge, bool delete, TextWriter msg)
        {
            IisWebSiteBinding site = IisHelper.ResolveSingleSite(WebSiteRef,
                                                                 IisHelper.ListDistinctHttpWebSites());

            var siteRoot = site.SiteRoot;

            if (!string.IsNullOrEmpty(OverrideSiteRoot))
            {
                siteRoot = OverrideSiteRoot;
            }
            if (string.IsNullOrEmpty(siteRoot))
            {
                throw new InvalidOperationException("missing root path for resolve site")
                      .With(nameof(IisWebSiteBinding.SiteId), site.SiteId)
                      .With(nameof(IisWebSiteBinding.SiteName), site.SiteName);
            }

            // IIS-configured Site Root can use env vars
            siteRoot = Environment.ExpandEnvironmentVariables(siteRoot);

            // Make sure we're using the canonical full path
            siteRoot = Path.GetFullPath(siteRoot);

            // We need to strip off any leading '/' in the path
            var filePath = httpChallenge.FilePath;

            if (filePath.StartsWith("/"))
            {
                filePath = filePath.Substring(1);
            }

            var fullFilePath   = Path.Combine(siteRoot, filePath);
            var fullDirPath    = Path.GetDirectoryName(fullFilePath);
            var fullConfigPath = Path.Combine(fullDirPath, "web.config");

            // This meta-data file will be placed next to the actual
            // Challenge answer content file and it captures some details
            // that we need in order to properly clean up the handling of
            // this Challenge after it has been submitted
            var fullMetaPath = $"{fullFilePath}-acmesharp_meta";

            // Check if user is running with elevated privs and warn if not
            if (!IisHelper.IsAdministrator())
            {
                Console.Error.WriteLine("WARNING:  You are not running with elelvated privileges.");
                Console.Error.WriteLine("          Write access may be denied to the destination.");
            }

            if (delete)
            {
                bool          skipLocalWebConfig = SkipLocalWebConfig;
                List <string> dirsCreated        = null;

                // First see if there's a meta file there to help us out
                if (File.Exists(fullMetaPath))
                {
                    var meta = JsonHelper.Load <IisChallengeHandlerMeta>(
                        File.ReadAllText(fullMetaPath));

                    skipLocalWebConfig = meta.SkippedLocalWebConfig;
                    dirsCreated        = meta.DirsCreated;
                }

                // Get rid of the Challenge answer content file
                if (File.Exists(fullFilePath))
                {
                    File.Delete(fullFilePath);
                    msg.WriteLine("* Challenge response content has been removed from local file path");
                    msg.WriteLine("    at:  [{0}]", fullFilePath);
                }

                // Get rid of web.config if necessary
                if (!skipLocalWebConfig && File.Exists(fullConfigPath))
                {
                    File.Delete(fullConfigPath);
                    msg.WriteLine("* Local web.config has been removed from local file path");
                    msg.WriteLine("    at:  [{0}]", fullFilePath);
                }

                // Get rid of the meta file so that we can clean up the dirs
                if (File.Exists(fullMetaPath))
                {
                    File.Delete(fullMetaPath);
                }

                // Walk up the tree if needed
                if (dirsCreated?.Count > 0)
                {
                    var dirsDeleted = new List <string>();
                    dirsCreated.Reverse();
                    foreach (var dir in dirsCreated)
                    {
                        if (Directory.Exists(dir))
                        {
                            if (Directory.GetFileSystemEntries(dir).Length == 0)
                            {
                                Directory.Delete(dir);
                                dirsDeleted.Add(dir);
                            }
                        }
                    }

                    if (dirsDeleted.Count > 0)
                    {
                        msg.WriteLine("* Removed the following directories:");
                        foreach (var dd in dirsDeleted)
                        {
                            msg.WriteLine("  - [{0}]", dd);
                        }
                    }
                }
            }
            else
            {
                // Figure out which dirs we have to create so
                // we can capture and clean it up later on
                var meta = new IisChallengeHandlerMeta
                {
                    WasAdmin = IisHelper.IsAdministrator(),
                    SkippedLocalWebConfig = SkipLocalWebConfig,
                    DirsCreated           = new List <string>(),
                };

                // In theory this ascending of the dir path should work
                // just fine, but just in case, this path segment counter
                // should gaurd against the possibility of an infinite loop
                var dirLimit = 100;

                var testDir = fullDirPath;
                while (!Directory.Exists(testDir))
                {
                    // Sanity check against an infinite loop
                    if (--dirLimit <= 0)
                    {
                        throw new Exception("Unexpected directory path segment count reached")
                              .With(nameof(dirLimit), "100")
                              .With(nameof(fullDirPath), fullDirPath)
                              .With(nameof(testDir), testDir)
                              .With($"first-{nameof(meta.DirsCreated)}",
                                    meta.DirsCreated[0])
                              .With($"last-{nameof(meta.DirsCreated)}",
                                    meta.DirsCreated[meta.DirsCreated.Count - 1]);
                    }

                    if (Path.GetFullPath(testDir) == siteRoot)
                    {
                        break;
                    }

                    // Add to the top of the list
                    meta.DirsCreated.Insert(0, testDir);
                    // Move to the parent
                    testDir = Path.GetDirectoryName(testDir);
                }

                foreach (var dir in meta.DirsCreated)
                {
                    Directory.CreateDirectory(dir);
                }

                File.WriteAllText(fullFilePath, httpChallenge.FileContent);
                File.WriteAllText(fullMetaPath, JsonHelper.Save(meta));

                msg.WriteLine("* Challenge response content has been written to local file path");
                msg.WriteLine("    at:  [{0}]", fullFilePath);
                msg.WriteLine("* Challenge response should be accessible with a MIME type of [text/json]");
                msg.WriteLine("    at:  [{0}]", httpChallenge.FileUrl);

                if (!SkipLocalWebConfig)
                {
                    var t = typeof(IisChallengeHandler);
                    var r = $"{t.Namespace}.{t.Name}-WebConfig";
                    using (Stream rs = t.Assembly.GetManifestResourceStream(r))
                    {
                        using (var fs = new FileStream(fullConfigPath, FileMode.Create))
                        {
                            rs.CopyTo(fs);
                        }
                    }
                    msg.WriteLine("* Local web.config has been created to serve response file as JSON");
                    msg.WriteLine("  however, you may need to adjust this file for your environment");
                    msg.WriteLine("    at: [{0}]", httpChallenge.FileUrl);
                }
                else
                {
                    msg.WriteLine("* Local web.config file creation has been skipped!");
                    msg.WriteLine("  You may need to manually adjust your configuration to serve the");
                    msg.WriteLine("  Challenge Response file with a MIME type of [text/json]");
                }
            }
        }
示例#13
0
        private void ChallengeGetCertAndInstall(List <SiteBinding> sites)
        {
            AuthorizationState authorizationState = null;
            AuthorizeChallenge authorizeChallenge = null;
            HttpChallenge      httpChallenge      = null;

            Log("\tChallengeGetCertAndInstall started");
            Log("\tChallengeGetCertAndInstall total sites: {0}", sites.Count);
            foreach (var site in sites)
            {
                Log("\t\tChallengeGetCertAndInstall * procesing {0}", site.BindingHost);

                try
                {
                    authorizationState = this.client.AuthorizeIdentifier(site.BindingHost);
                    Log("\t\tChallengeGetCertAndInstall authorizationState ok");
                }
                catch (Exception exception)
                {
                    Log("\t\tChallengeGetCertAndInstall Error AuthorizeIdentifier {0}", exception.Message);
                    continue;
                }

                try
                {
                    authorizeChallenge = this.client.DecodeChallenge(authorizationState, AcmeProtocol.CHALLENGE_TYPE_HTTP);
                }
                catch (Exception exception)
                {
                    Log("\t\tChallengeGetCertAndInstall Error DecodeChallenge {0}", exception.Message);
                    continue;
                }

                if (authorizeChallenge == null)
                {
                    Log("\t\tChallengeGetCertAndInstall authorizeChallenge == null");
                    continue;
                }

                httpChallenge = authorizeChallenge.Challenge as HttpChallenge;
                if (httpChallenge == null)
                {
                    Log("\t\tChallengeGetCertAndInstall httpChallenge == null");
                    continue;
                }

                var challengePath = Path.Combine(site.Path, httpChallenge.FilePath.Replace('/', '\\'));

                if (!CreateChallenge(challengePath, httpChallenge.FileContent))
                {
                    continue;
                }
                if (!CreateWebConfig(challengePath))
                {
                    continue;
                }

                // warmup?
                try
                {
                    using (var wc = new WebClient())
                    {
                        var response = wc.DownloadString(httpChallenge.FileUrl);
                        if (httpChallenge.FileContent != response)
                        {
                            Log("\t\tChallengeGetCertAndInstall error FileContent != response for {0}", site.BindingHost);
                            continue;
                        }
                    }
                }
                catch (Exception exception)
                {
                    Log("\t\tChallengeGetCertAndInstall DownloadString {0} Error {1}", httpChallenge.FileUrl, exception.Message);
                    continue;
                }
                Log("\t\tChallengeGetCertAndInstall Challenge placed for {0}", site.BindingHost);

                authorizationState.Challenges = new AuthorizeChallenge[] { authorizeChallenge };
                this.client.SubmitChallengeAnswer(authorizationState, AcmeProtocol.CHALLENGE_TYPE_HTTP, true);

                for (int intI = 0; intI < 60; intI++)
                {
                    System.Threading.Thread.Sleep(1000);
                    var newAuthorizationState = this.client.RefreshIdentifierAuthorization(authorizationState);
                    if (newAuthorizationState.Status != "pending")
                    {
                        authorizationState = newAuthorizationState;
                        break;
                    }
                }

                if (DeleteWebConfig(challengePath))
                {
                    Log("\t\tChallengeGetCertAndInstall WebConfig deleted for {0}", site.BindingHost);
                }

                if (DeleteChallenge(challengePath))
                {
                    Log("\t\tChallengeGetCertAndInstall Challenge deleted for {0}", site.BindingHost);
                }

                switch (authorizationState.Status)
                {
                default:
                    Log("\t\tChallengeGetCertAndInstall unknown status {0}", authorizationState.Status);
                    break;

                case "pending":
                    Log("\t\tChallengeGetCertAndInstall error pending");
                    break;

                case "invalid":
                    Log("\t\tChallengeGetCertAndInstall error invalid");
                    break;

                case "valid":
                    Log("\t\tChallengeGetCertAndInstall valid for {0}", site.BindingHost);
                    if (InstallCertificate(site.BindingHost))
                    {
                        Log("\t\tChallengeGetCertAndInstall ready {0}", site.BindingHost);
                    }
                    break;
                }
            }
            Log("\tChallengeGetCertAndInstall ended");
        }
 /// <summary>
 /// Should create any directory structure needed and write the file for authorization
 /// </summary>
 /// <param name="answerPath">where the answerFile should be located</param>
 /// <param name="fileContents">the contents of the file to write</param>
 public virtual void CreateAuthorizationFile(Target target, HttpChallenge challenge)
 {
     WriteFile(CombinePath(target.WebRootPath, challenge.FilePath), challenge.FileContent);
 }
 /// <summary>
 /// Handle clean-up steps
 /// </summary>
 /// <param name="options"></param>
 /// <param name="target"></param>
 /// <param name="challenge"></param>
 private void Cleanup(Target target, HttpChallenge challenge)
 {
     BeforeDelete(target, challenge);
     DeleteAuthorization(target, challenge);
 }
示例#16
0
 public override void BeforeAuthorize(Target target, HttpChallenge challenge)
 {
     _iisClient.PrepareSite(target);
     base.BeforeAuthorize(target, challenge);
 }
示例#17
0
 static void Upload(Uri ftp, NetworkCredential credentials, HttpChallenge challenge)
 {
     Upload(ftp, credentials, challenge.FileContent);
 }
示例#18
0
 public override void BeforeDelete(Target target, HttpChallenge challenge)
 {
     _iisClient.UnprepareSite(target);
     base.BeforeDelete(target, challenge);
 }
 public abstract Task CleanupChallengeFile(HttpChallenge challenge);
示例#20
0
        private static async Task Process(bool Verbose, Uri Directory, string[] ContactURLs, bool TermsOfServiceAgreed,
                                          bool NewKey, string[] DomainNames, DateTime?NotBefore, DateTime?NotAfter, string HttpRootFolder, int PollingInterval,
                                          int KeySize, string EMail, string Country, string Locality, string StateOrProvince, string Organization,
                                          string OrganizationalUnit, string FileName, string Password)
        {
            using (AcmeClient Client = new AcmeClient(Directory))
            {
                Log.Informational("Connecting to directory.",
                                  new KeyValuePair <string, object>("URL", Directory.ToString()));

                AcmeDirectory AcmeDirectory = await Client.GetDirectory();

                if (AcmeDirectory.ExternalAccountRequired)
                {
                    Log.Warning("An external account is required.");
                }

                if (AcmeDirectory.TermsOfService != null)
                {
                    Log.Informational("Terms of service available.",
                                      new KeyValuePair <string, object>("URL", AcmeDirectory.TermsOfService.ToString()));
                }

                if (AcmeDirectory.Website != null)
                {
                    Log.Informational("Web site available.",
                                      new KeyValuePair <string, object>("URL", AcmeDirectory.Website.ToString()));
                }


                Log.Informational("Getting account.");

                AcmeAccount Account;

                try
                {
                    Account = await Client.GetAccount();

                    Log.Informational("Account found.",
                                      new KeyValuePair <string, object>("Created", Account.CreatedAt),
                                      new KeyValuePair <string, object>("Initial IP", Account.InitialIp),
                                      new KeyValuePair <string, object>("Status", Account.Status),
                                      new KeyValuePair <string, object>("Contact", Account.Contact));

                    if (ContactURLs != null && !AreEqual(Account.Contact, ContactURLs))
                    {
                        Log.Informational("Updating contact URIs in account.");

                        Account = await Account.Update(ContactURLs);

                        Log.Informational("Account updated.",
                                          new KeyValuePair <string, object>("Created", Account.CreatedAt),
                                          new KeyValuePair <string, object>("Initial IP", Account.InitialIp),
                                          new KeyValuePair <string, object>("Status", Account.Status),
                                          new KeyValuePair <string, object>("Contact", Account.Contact));
                    }
                }
                catch (AcmeAccountDoesNotExistException)
                {
                    Log.Warning("Account not found. Creating account.",
                                new KeyValuePair <string, object>("Contact", ContactURLs),
                                new KeyValuePair <string, object>("TermsOfServiceAgreed", TermsOfServiceAgreed));

                    Account = await Client.CreateAccount(ContactURLs, TermsOfServiceAgreed);

                    Log.Informational("Account created.",
                                      new KeyValuePair <string, object>("Created", Account.CreatedAt),
                                      new KeyValuePair <string, object>("Initial IP", Account.InitialIp),
                                      new KeyValuePair <string, object>("Status", Account.Status),
                                      new KeyValuePair <string, object>("Contact", Account.Contact));
                }

                if (NewKey)
                {
                    Log.Informational("Generating new key.");

                    await Account.NewKey();

                    Log.Informational("New key generated.");
                }


                if (DomainNames != null)
                {
                    if (!string.IsNullOrEmpty(HttpRootFolder))
                    {
                        CheckExists(HttpRootFolder);
                        HttpRootFolder = Path.Combine(HttpRootFolder, ".well-known");
                        CheckExists(HttpRootFolder);
                        HttpRootFolder = Path.Combine(HttpRootFolder, "acme-challenge");
                        CheckExists(HttpRootFolder);
                    }

                    Log.Informational("Creating order.");

                    AcmeOrder Order = await Account.OrderCertificate(DomainNames, NotBefore, NotAfter);

                    Log.Informational("Order created.",
                                      new KeyValuePair <string, object>("Status", Order.Status),
                                      new KeyValuePair <string, object>("Expires", Order.Expires),
                                      new KeyValuePair <string, object>("NotBefore", Order.NotBefore),
                                      new KeyValuePair <string, object>("NotAfter", Order.NotAfter),
                                      new KeyValuePair <string, object>("Identifiers", Order.Identifiers));

                    List <string> FileNames = null;

                    try
                    {
                        foreach (AcmeAuthorization Authorization in await Order.GetAuthorizations())
                        {
                            Log.Informational("Processing authorization.",
                                              new KeyValuePair <string, object>("Type", Authorization.Type),
                                              new KeyValuePair <string, object>("Value", Authorization.Value),
                                              new KeyValuePair <string, object>("Status", Authorization.Status),
                                              new KeyValuePair <string, object>("Expires", Authorization.Expires),
                                              new KeyValuePair <string, object>("Wildcard", Authorization.Wildcard));

                            AcmeChallenge Challenge;
                            bool          Manual       = true;
                            int           Index        = 1;
                            int           NrChallenges = Authorization.Challenges.Length;
                            string        s;

                            for (Index = 1; Index <= NrChallenges; Index++)
                            {
                                Challenge = Authorization.Challenges[Index - 1];

                                if (Challenge is AcmeHttpChallenge HttpChallenge)
                                {
                                    Log.Informational(Index.ToString() + ") HTTP challenge.",
                                                      new KeyValuePair <string, object>("Resource", HttpChallenge.ResourceName),
                                                      new KeyValuePair <string, object>("Response", HttpChallenge.KeyAuthorization),
                                                      new KeyValuePair <string, object>("Content-Type", "application/octet-stream"));

                                    if (!string.IsNullOrEmpty(HttpRootFolder))
                                    {
                                        string ChallengeFileName = Path.Combine(HttpRootFolder, HttpChallenge.Token);
                                        File.WriteAllBytes(ChallengeFileName, Encoding.ASCII.GetBytes(HttpChallenge.KeyAuthorization));

                                        if (FileNames == null)
                                        {
                                            FileNames = new List <string>();
                                        }

                                        FileNames.Add(ChallengeFileName);

                                        Log.Informational("Acknowleding challenge.");

                                        Challenge = await HttpChallenge.AcknowledgeChallenge();

                                        Log.Informational("Challenge acknowledged.",
                                                          new KeyValuePair <string, object>("Status", Challenge.Status));

                                        Manual = false;
                                    }
                                    else if (!Verbose)
                                    {
                                        Console.Out.WriteLine(Index.ToString() + ") HTTP challenge.");
                                        Console.Out.WriteLine("Resource: " + HttpChallenge.ResourceName);
                                        Console.Out.WriteLine("Response: " + HttpChallenge.KeyAuthorization);
                                        Console.Out.WriteLine("Content-Type: " + "application/octet-stream");
                                    }
                                }
                                else if (Challenge is AcmeDnsChallenge DnsChallenge)
                                {
                                    Log.Informational(Index.ToString() + ") DNS challenge.",
                                                      new KeyValuePair <string, object>("Domain", DnsChallenge.ValidationDomainNamePrefix + Authorization.Value),
                                                      new KeyValuePair <string, object>("TXT Record", DnsChallenge.KeyAuthorization));

                                    if (!Verbose)
                                    {
                                        Console.Out.WriteLine(Index.ToString() + ") DNS challenge.");
                                        Console.Out.WriteLine("Domain: " + DnsChallenge.ValidationDomainNamePrefix + Authorization.Value);
                                        Console.Out.WriteLine("TXT Record: " + DnsChallenge.KeyAuthorization);
                                    }
                                }
                            }

                            if (Manual)
                            {
                                Console.Out.WriteLine();
                                Console.Out.WriteLine("No automated method found to respond to any of the authorization challenges. " +
                                                      "You can respond to a challenge manually. After configuring the corresponding " +
                                                      "resource, enter the number of the corresponding challenge and press ENTER to acknowledge it.");

                                do
                                {
                                    Console.Out.Write("Challenge to acknowledge: ");
                                    s = Console.In.ReadLine();
                                }while (!int.TryParse(s, out Index) || Index <= 0 || Index > NrChallenges);

                                Log.Informational("Acknowleding challenge.");

                                Challenge = await Authorization.Challenges[Index - 1].AcknowledgeChallenge();

                                Log.Informational("Challenge acknowledged.",
                                                  new KeyValuePair <string, object>("Status", Challenge.Status));
                            }

                            AcmeAuthorization Authorization2 = Authorization;

                            do
                            {
                                Log.Informational("Waiting to poll authorization status.",
                                                  new KeyValuePair <string, object>("ms", PollingInterval));

                                System.Threading.Thread.Sleep(PollingInterval);

                                Log.Informational("Polling authorization.");

                                Authorization2 = await Authorization2.Poll();

                                Log.Informational("Authorization polled.",
                                                  new KeyValuePair <string, object>("Type", Authorization2.Type),
                                                  new KeyValuePair <string, object>("Value", Authorization2.Value),
                                                  new KeyValuePair <string, object>("Status", Authorization2.Status),
                                                  new KeyValuePair <string, object>("Expires", Authorization2.Expires),
                                                  new KeyValuePair <string, object>("Wildcard", Authorization2.Wildcard));
                            }while (Authorization2.Status == AcmeAuthorizationStatus.pending);

                            if (Authorization2.Status != AcmeAuthorizationStatus.valid)
                            {
                                switch (Authorization2.Status)
                                {
                                case AcmeAuthorizationStatus.deactivated:
                                    throw new Exception("Authorization deactivated.");

                                case AcmeAuthorizationStatus.expired:
                                    throw new Exception("Authorization expired.");

                                case AcmeAuthorizationStatus.invalid:
                                    throw new Exception("Authorization invalid.");

                                case AcmeAuthorizationStatus.revoked:
                                    throw new Exception("Authorization revoked.");

                                default:
                                    throw new Exception("Authorization not validated.");
                                }
                            }
                        }

                        using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(KeySize))
                        {
                            Log.Informational("Finalizing order.");

                            SignatureAlgorithm SignAlg = new RsaSha256(RSA);

                            Order = await Order.FinalizeOrder(new Security.ACME.CertificateRequest(SignAlg)
                            {
                                CommonName = DomainNames[0],
                                SubjectAlternativeNames = DomainNames,
                                EMailAddress            = EMail,
                                Country            = Country,
                                Locality           = Locality,
                                StateOrProvince    = StateOrProvince,
                                Organization       = Organization,
                                OrganizationalUnit = OrganizationalUnit
                            });

                            Log.Informational("Order finalized.",
                                              new KeyValuePair <string, object>("Status", Order.Status),
                                              new KeyValuePair <string, object>("Expires", Order.Expires),
                                              new KeyValuePair <string, object>("NotBefore", Order.NotBefore),
                                              new KeyValuePair <string, object>("NotAfter", Order.NotAfter),
                                              new KeyValuePair <string, object>("Identifiers", Order.Identifiers));

                            if (Order.Status != AcmeOrderStatus.valid)
                            {
                                switch (Order.Status)
                                {
                                case AcmeOrderStatus.invalid:
                                    throw new Exception("Order invalid.");

                                default:
                                    throw new Exception("Unable to validate oder.");
                                }
                            }

                            if (Order.Certificate == null)
                            {
                                throw new Exception("No certificate URI provided.");
                            }

                            System.Security.Cryptography.X509Certificates.X509Certificate2[] Certificates =
                                await Order.DownloadCertificate();

                            string CertificateFileName;
                            string CertificateFileName2;
                            int    Index = 1;
                            byte[] Bin;

                            DerEncoder KeyOutput = new DerEncoder();
                            SignAlg.ExportPrivateKey(KeyOutput);

                            StringBuilder PemOutput = new StringBuilder();

                            PemOutput.AppendLine("-----BEGIN RSA PRIVATE KEY-----");
                            PemOutput.AppendLine(Convert.ToBase64String(KeyOutput.ToArray(), Base64FormattingOptions.InsertLineBreaks));
                            PemOutput.AppendLine("-----END RSA PRIVATE KEY-----");

                            CertificateFileName = FileName + ".key";

                            Log.Informational("Saving private key.",
                                              new KeyValuePair <string, object>("FileName", CertificateFileName));

                            File.WriteAllText(CertificateFileName, PemOutput.ToString(), Encoding.ASCII);

                            foreach (X509Certificate2 Certificate in Certificates)
                            {
                                if (Index == 1)
                                {
                                    CertificateFileName = FileName;
                                }
                                else
                                {
                                    CertificateFileName = FileName + Index.ToString();
                                }

                                CertificateFileName2 = CertificateFileName + ".pem";
                                CertificateFileName += ".cer";

                                Bin = Certificate.Export(X509ContentType.Cert);

                                Log.Informational("Saving certificate.",
                                                  new KeyValuePair <string, object>("FileName", CertificateFileName),
                                                  new KeyValuePair <string, object>("FileName2", CertificateFileName2),
                                                  new KeyValuePair <string, object>("FriendlyName", Certificate.FriendlyName),
                                                  new KeyValuePair <string, object>("HasPrivateKey", Certificate.HasPrivateKey),
                                                  new KeyValuePair <string, object>("Issuer", Certificate.Issuer),
                                                  new KeyValuePair <string, object>("NotAfter", Certificate.NotAfter),
                                                  new KeyValuePair <string, object>("NotBefore", Certificate.NotBefore),
                                                  new KeyValuePair <string, object>("SerialNumber", Certificate.SerialNumber),
                                                  new KeyValuePair <string, object>("Subject", Certificate.Subject),
                                                  new KeyValuePair <string, object>("Thumbprint", Certificate.Thumbprint));

                                File.WriteAllBytes(CertificateFileName, Bin);

                                PemOutput.Clear();
                                PemOutput.AppendLine("-----BEGIN CERTIFICATE-----");
                                PemOutput.AppendLine(Convert.ToBase64String(Bin, Base64FormattingOptions.InsertLineBreaks));
                                PemOutput.AppendLine("-----END CERTIFICATE-----");

                                File.WriteAllText(CertificateFileName2, PemOutput.ToString(), Encoding.ASCII);

                                Index++;
                            }
                        }
                    }
                    finally
                    {
                        if (FileNames != null)
                        {
                            foreach (string FileName2 in FileNames)
                            {
                                File.Delete(FileName2);
                            }
                        }
                    }
                }
            }
        }
        public override async Task CleanupChallengeFile(HttpChallenge challenge)
        {
            var blob = await GetBlob(challenge);

            await blob.DeleteIfExistsAsync();
        }
示例#22
0
        public override async Task CleanupChallengeFile(HttpChallenge challenge)
        {
            var blobFile = await GetChallengeBlobReferenceAsync(challenge);

            await blobFile.DeleteIfExistsAsync();
        }
示例#23
0
 private void CreateChallengeResponse(HttpChallenge httpChallenge)
 {
     _logger.Information("Make sure the URL {responseUrl} responds with {response}", httpChallenge.FileUrl, httpChallenge.FileContent);
     _logger.Information("Press a key to continue when the response is available...", httpChallenge.FileUrl, httpChallenge.FileContent);
     Console.ReadKey();
 }
 public override async Task CleanupChallengeFile(HttpChallenge challenge)
 {
     File.Delete(await GetAnswerPath(challenge));
 }
        internal async Task <bool> CreateCertificate(string TabID)
        {
            try
            {
                string        URL = this.customCA ? this.acmeDirectory : "https://acme-v02.api.letsencrypt.org/directory";
                RSAParameters Parameters;
                CspParameters CspParams = new CspParameters()
                {
                    Flags            = CspProviderFlags.UseMachineKeyStore,
                    KeyContainerName = "IoTGateway:" + URL
                };

                try
                {
                    bool Ok;

                    using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(4096, CspParams))
                    {
                        Parameters = RSA.ExportParameters(true);

                        if (RSA.KeySize < 4096)
                        {
                            RSA.PersistKeyInCsp = false;
                            RSA.Clear();
                            Ok = false;
                        }
                        else
                        {
                            Ok = true;
                        }
                    }

                    if (!Ok)
                    {
                        using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(4096, CspParams))
                        {
                            Parameters = RSA.ExportParameters(true);
                        }
                    }
                }
                catch (CryptographicException ex)
                {
                    throw new CryptographicException("Unable to get access to cryptographic key for \"IoTGateway:" + URL +
                                                     "\". Was the database created using another user?", ex);
                }

                using (AcmeClient Client = new AcmeClient(new Uri(URL), Parameters))
                {
                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Connecting to directory.", false, "User");

                    AcmeDirectory AcmeDirectory = await Client.GetDirectory();

                    if (AcmeDirectory.ExternalAccountRequired)
                    {
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "An external account is required.", false, "User");
                    }

                    if (AcmeDirectory.TermsOfService != null)
                    {
                        URL = AcmeDirectory.TermsOfService.ToString();
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Terms of service available on: " + URL, false, "User");
                        ClientEvents.PushEvent(new string[] { TabID }, "TermsOfService", URL, false, "User");

                        this.urlToS = URL;

                        if (!this.acceptToS)
                        {
                            ClientEvents.PushEvent(new string[] { TabID }, "CertificateError", "You need to accept the terms of service.", false, "User");
                            return(false);
                        }
                    }

                    if (AcmeDirectory.Website != null)
                    {
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Web site available on: " + AcmeDirectory.Website.ToString(), false, "User");
                    }

                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Getting account.", false, "User");

                    List <string> Names = new List <string>();

                    if (!string.IsNullOrEmpty(this.domain))
                    {
                        Names.Add(this.domain);
                    }

                    if (this.alternativeDomains != null)
                    {
                        foreach (string Name in this.alternativeDomains)
                        {
                            if (!Names.Contains(Name))
                            {
                                Names.Add(Name);
                            }
                        }
                    }
                    string[] DomainNames = Names.ToArray();

                    AcmeAccount Account;

                    try
                    {
                        Account = await Client.GetAccount();

                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Account found.", false, "User");
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Created: " + Account.CreatedAt.ToString(), false, "User");
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Initial IP: " + Account.InitialIp, false, "User");
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Status: " + Account.Status.ToString(), false, "User");

                        if (string.IsNullOrEmpty(this.contactEMail))
                        {
                            if (Account.Contact != null && Account.Contact.Length != 0)
                            {
                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Updating contact URIs in account.", false, "User");
                                Account = await Account.Update(new string[0]);

                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Account updated.", false, "User");
                            }
                        }
                        else
                        {
                            if (Account.Contact is null || Account.Contact.Length != 1 || Account.Contact[0] != "mailto:" + this.contactEMail)
                            {
                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Updating contact URIs in account.", false, "User");
                                Account = await Account.Update(new string[] { "mailto:" + this.contactEMail });

                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Account updated.", false, "User");
                            }
                        }
                    }
                    catch (AcmeAccountDoesNotExistException)
                    {
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Account not found.", false, "User");
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Creating account.", false, "User");

                        Account = await Client.CreateAccount(string.IsNullOrEmpty(this.contactEMail)?new string[0] : new string[] { "mailto:" + this.contactEMail },
                                                             this.acceptToS);

                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Account created.", false, "User");
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Status: " + Account.Status.ToString(), false, "User");
                    }

                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Generating new key.", false, "User");
                    await Account.NewKey();

                    using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(4096, CspParams))
                    {
                        RSA.ImportParameters(Client.ExportAccountKey(true));
                    }

                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "New key generated.", false, "User");

                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Creating order.", false, "User");
                    AcmeOrder Order = await Account.OrderCertificate(DomainNames, null, null);

                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Order created.", false, "User");

                    foreach (AcmeAuthorization Authorization in await Order.GetAuthorizations())
                    {
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Processing authorization for " + Authorization.Value, false, "User");

                        AcmeChallenge Challenge;
                        bool          Acknowledged = false;
                        int           Index        = 1;
                        int           NrChallenges = Authorization.Challenges.Length;

                        for (Index = 1; Index <= NrChallenges; Index++)
                        {
                            Challenge = Authorization.Challenges[Index - 1];

                            if (Challenge is AcmeHttpChallenge HttpChallenge)
                            {
                                this.challenge = "/" + HttpChallenge.Token;
                                this.token     = HttpChallenge.KeyAuthorization;

                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Acknowleding challenge.", false, "User");
                                Challenge = await HttpChallenge.AcknowledgeChallenge();

                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Challenge acknowledged: " + Challenge.Status.ToString(), false, "User");

                                Acknowledged = true;
                            }
                        }

                        if (!Acknowledged)
                        {
                            ClientEvents.PushEvent(new string[] { TabID }, "CertificateError", "No automated method found to respond to any of the authorization challenges.", false, "User");
                            return(false);
                        }

                        AcmeAuthorization Authorization2 = Authorization;

                        do
                        {
                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Waiting to poll authorization status.", false, "User");
                            await Task.Delay(5000);

                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Polling authorization.", false, "User");
                            Authorization2 = await Authorization2.Poll();

                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Authorization polled: " + Authorization2.Status.ToString(), false, "User");
                        }while (Authorization2.Status == AcmeAuthorizationStatus.pending);

                        if (Authorization2.Status != AcmeAuthorizationStatus.valid)
                        {
                            switch (Authorization2.Status)
                            {
                            case AcmeAuthorizationStatus.deactivated:
                                throw new Exception("Authorization deactivated.");

                            case AcmeAuthorizationStatus.expired:
                                throw new Exception("Authorization expired.");

                            case AcmeAuthorizationStatus.invalid:
                                throw new Exception("Authorization invalid.");

                            case AcmeAuthorizationStatus.revoked:
                                throw new Exception("Authorization revoked.");

                            default:
                                throw new Exception("Authorization not validated.");
                            }
                        }
                    }

                    using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(4096))                       // TODO: Make configurable
                    {
                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Finalizing order.", false, "User");

                        SignatureAlgorithm SignAlg = new RsaSha256(RSA);

                        Order = await Order.FinalizeOrder(new CertificateRequest(SignAlg)
                        {
                            CommonName = this.domain,
                            SubjectAlternativeNames = DomainNames,
                            EMailAddress            = this.contactEMail
                        });

                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Order finalized: " + Order.Status.ToString(), false, "User");

                        if (Order.Status != AcmeOrderStatus.valid)
                        {
                            switch (Order.Status)
                            {
                            case AcmeOrderStatus.invalid:
                                throw new Exception("Order invalid.");

                            default:
                                throw new Exception("Unable to validate oder.");
                            }
                        }

                        if (Order.Certificate is null)
                        {
                            throw new Exception("No certificate URI provided.");
                        }

                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Downloading certificate.", false, "User");

                        X509Certificate2[] Certificates = await Order.DownloadCertificate();

                        X509Certificate2 Certificate = Certificates[0];

                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Exporting certificate.", false, "User");

                        this.certificate = Certificate.Export(X509ContentType.Cert);
                        this.privateKey  = RSA.ExportCspBlob(true);
                        this.pfx         = null;
                        this.password    = string.Empty;

                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Adding private key.", false, "User");

                        try
                        {
                            Certificate.PrivateKey = RSA;
                        }
                        catch (PlatformNotSupportedException)
                        {
                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Platform does not support adding of private key.", false, "User");
                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Searching for OpenSSL on machine.", false, "User");

                            string[] Files;
                            string   Password      = Hashes.BinaryToString(Gateway.NextBytes(32));
                            string   CertFileName  = null;
                            string   CertFileName2 = null;
                            string   KeyFileName   = null;

                            if (string.IsNullOrEmpty(this.openSslPath) || !File.Exists(this.openSslPath))
                            {
                                Files = GetFiles(new string(Path.DirectorySeparatorChar, 1), "openssl.exe");
                                if (Files is null)
                                {
                                    List <string> Files2 = new List <string>();

                                    Files = GetFiles(Environment.SpecialFolder.ProgramFiles, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.CommonProgramFilesX86, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.Programs, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.System, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.SystemX86, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.Windows, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.CommonProgramFiles, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.CommonProgramFilesX86, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Environment.SpecialFolder.CommonPrograms, "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Path.DirectorySeparatorChar + "OpenSSL-Win32", "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = GetFiles(Path.DirectorySeparatorChar + "OpenSSL-Win64", "openssl.exe");
                                    if (Files != null)
                                    {
                                        Files2.AddRange(Files);
                                    }

                                    Files = Files2.ToArray();
                                }
                            }
                            else
                            {
                                Files = new string[] { this.openSslPath }
                            };

                            try
                            {
                                if (Files.Length == 0)
                                {
                                    ClientEvents.PushEvent(new string[] { TabID }, "CertificateError", "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.", false, "User");
                                    return(false);
                                }
                                else
                                {
                                    foreach (string OpenSslFile in Files)
                                    {
                                        if (CertFileName is null)
                                        {
                                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Generating temporary certificate file.", false, "User");

                                            StringBuilder PemOutput = new StringBuilder();
                                            byte[]        Bin       = Certificate.Export(X509ContentType.Cert);

                                            PemOutput.AppendLine("-----BEGIN CERTIFICATE-----");
                                            PemOutput.AppendLine(Convert.ToBase64String(Bin, Base64FormattingOptions.InsertLineBreaks));
                                            PemOutput.AppendLine("-----END CERTIFICATE-----");

                                            CertFileName = Path.Combine(Gateway.AppDataFolder, "Certificate.pem");
                                            File.WriteAllText(CertFileName, PemOutput.ToString(), Encoding.ASCII);

                                            ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Generating temporary key file.", false, "User");

                                            DerEncoder KeyOutput = new DerEncoder();
                                            SignAlg.ExportPrivateKey(KeyOutput);

                                            PemOutput.Clear();
                                            PemOutput.AppendLine("-----BEGIN RSA PRIVATE KEY-----");
                                            PemOutput.AppendLine(Convert.ToBase64String(KeyOutput.ToArray(), Base64FormattingOptions.InsertLineBreaks));
                                            PemOutput.AppendLine("-----END RSA PRIVATE KEY-----");

                                            KeyFileName = Path.Combine(Gateway.AppDataFolder, "Certificate.key");

                                            File.WriteAllText(KeyFileName, PemOutput.ToString(), Encoding.ASCII);
                                        }

                                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Converting to PFX using " + OpenSslFile, false, "User");

                                        Process P = new Process()
                                        {
                                            StartInfo = new ProcessStartInfo()
                                            {
                                                FileName               = OpenSslFile,
                                                Arguments              = "pkcs12 -nodes -export -out Certificate.pfx -inkey Certificate.key -in Certificate.pem -password pass:"******"ShowStatus", "Output: " + P.StandardOutput.ReadToEnd(), false, "User");
                                            }

                                            if (!P.StandardError.EndOfStream)
                                            {
                                                ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Error: " + P.StandardError.ReadToEnd(), false, "User");
                                            }

                                            continue;
                                        }

                                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Loading PFX.", false, "User");

                                        CertFileName2    = Path.Combine(Gateway.AppDataFolder, "Certificate.pfx");
                                        this.pfx         = File.ReadAllBytes(CertFileName2);
                                        this.password    = Password;
                                        this.openSslPath = OpenSslFile;

                                        ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "PFX successfully generated using OpenSSL.", false, "User");
                                        break;
                                    }

                                    if (this.pfx is null)
                                    {
                                        this.openSslPath = string.Empty;
                                        ClientEvents.PushEvent(new string[] { TabID }, "CertificateError", "Unable to convert to PFX using OpenSSL.", false, "User");
                                        return(false);
                                    }
                                }
                            }
                            finally
                            {
                                if (CertFileName != null && File.Exists(CertFileName))
                                {
                                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Deleting temporary certificate file.", false, "User");
                                    File.Delete(CertFileName);
                                }

                                if (KeyFileName != null && File.Exists(KeyFileName))
                                {
                                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Deleting temporary key file.", false, "User");
                                    File.Delete(KeyFileName);
                                }

                                if (CertFileName2 != null && File.Exists(CertFileName2))
                                {
                                    ClientEvents.PushEvent(new string[] { TabID }, "ShowStatus", "Deleting temporary pfx file.", false, "User");
                                    File.Delete(CertFileName2);
                                }
                            }
                        }


                        if (this.Step < 2)
                        {
                            this.Step = 2;
                        }

                        this.Updated = DateTime.Now;
                        await Database.Update(this);

                        ClientEvents.PushEvent(new string[] { TabID }, "CertificateOk", string.Empty, false, "User");

                        Gateway.UpdateCertificate(this);

                        return(true);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Critical(ex);
                ClientEvents.PushEvent(new string[] { TabID }, "CertificateError", "Unable to create certificate: " + XML.HtmlValueEncode(ex.Message), false, "User");
                return(false);
            }
            finally
            {
                this.inProgress = false;
            }
        }
 public abstract Task PersistsChallengeFile(HttpChallenge challenge);