Exemple #1
0
        public PendingAuthorization BeginRegistrationAndValidation(CertRequestConfig requestConfig, string identifierAlias, string challengeType = ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_HTTP, string domain = null)
        {
            //if no alternative domain specified, use the primary domains as the subject
            if (domain == null)
            {
                domain = requestConfig.PrimaryDomain;
            }

            // if (GetIdentifier(identifierAlias) == null)

            //if an identifier exists for the same dns in vault, remove it to avoid confusion
            this.DeleteIdentifierByDNS(domain);

            // ACME service requires international domain names in ascii mode, register the new
            // identifier with Lets Encrypt
            var authState = ACMESharpUtils.NewIdentifier(identifierAlias, idnMapping.GetAscii(domain));

            var identifier = this.GetIdentifier(identifierAlias, reloadVaultConfig: true);

            //FIXME: when validating subsequent SAN names in parallel request mode, the identifier is null?
            if (identifier != null && identifier.Authorization != null && identifier.Authorization.IsPending())
            {
                ACMESharpUtils.CompleteChallenge(identifier.Alias, challengeType, Handler: "manual", Regenerate: true, Repeat: true);

                //get challenge info
                ReloadVaultConfig();
                identifier = GetIdentifier(identifierAlias);
                try
                {
                    //identifier challenge specification is now ready for use to prepare and answer for LetsEncrypt to check

                    var challengeInfo = identifier.Challenges.FirstOrDefault(c => c.Value.Type == challengeType).Value;
                    return(new PendingAuthorization()
                    {
                        Challenge = GetAuthorizeChallengeItemFromAuthChallenge(challengeInfo), Identifier = GetDomainIdentifierItemFromIdentifierInfo(identifier), TempFilePath = "", ExtensionlessConfigCheckedOK = false, LogItems = this.GetActionLogSummary()
                    });
                }
                catch (Exception exp)
                {
                    LogAction("GetIdentifier", exp.ToString());
                    //identifier challenge could not be requested this time (FIXME: did we discard it when reloading vault?)
                    return(null);
                }
            }
            else
            {
                //identifier is null or already valid (previously authorized)
                return(new PendingAuthorization()
                {
                    Challenge = null, Identifier = GetDomainIdentifierItemFromIdentifierInfo(identifier), TempFilePath = "", ExtensionlessConfigCheckedOK = false, LogItems = this.GetActionLogSummary()
                });
            }
        }
Exemple #2
0
        public PendingAuthorization BeginRegistrationAndValidation(CertRequestConfig requestConfig, string identifierAlias, string challengeType = "http-01", string domain = null)
        {
            //if no alternative domain specified, use the primary domains as the subject
            if (domain == null)
            {
                domain = requestConfig.PrimaryDomain;
            }

            // if (GetIdentifier(identifierAlias) == null)

            //if an identifier exists for the same dns in vault, remove it to avoid confusion
            this.DeleteIdentifierByDNS(domain);

            // ACME service requires international domain names in ascii mode, regiser the new identifier with Lets Encrypt
            var authState = ACMESharpUtils.NewIdentifier(identifierAlias, idnMapping.GetAscii(domain));

            var identifier = this.GetIdentifier(identifierAlias, reloadVaultConfig: true);

            //FIXME: when validating subsequent SAN names in parallel request mode, the identifier is null?
            if (identifier != null && identifier.Authorization != null && identifier.Authorization.IsPending())
            {
                ACMESharpUtils.CompleteChallenge(identifier.Alias, challengeType, Handler: "manual", Regenerate: true, Repeat: true);

                //get challenge info
                ReloadVaultConfig();
                identifier = GetIdentifier(identifierAlias);
                var challengeInfo = identifier.Challenges.FirstOrDefault(c => c.Value.Type == challengeType).Value;

                //identifier challenege specification is now ready for use to prepare and answer for LetsEncrypt to check
                return(new PendingAuthorization()
                {
                    Challenge = challengeInfo, Identifier = identifier, TempFilePath = "", ExtensionlessConfigCheckedOK = false
                });
            }
            else
            {
                //identifier is null or already valid (previously authorized)
                return(new PendingAuthorization()
                {
                    Challenge = null, Identifier = identifier, TempFilePath = "", ExtensionlessConfigCheckedOK = false
                });
            }
        }
Exemple #3
0
        public PendingAuthorization PerformIISAutomatedChallengeResponse(CertRequestConfig requestConfig, PendingAuthorization pendingAuth)
        {
            bool extensionlessConfigOK = false;
            bool checkViaProxy         = true;

            //if copying the file for the user, attempt that now
            if (pendingAuth.Challenge != null && requestConfig.PerformChallengeFileCopy)
            {
                var httpChallenge = (ACMESharp.ACME.HttpChallenge)pendingAuth.Challenge.Challenge;
                this.LogAction("Preparing challenge response for LetsEncrypt server to check at: " + httpChallenge.FileUrl);
                this.LogAction("If the challenge response file is not accessible at this exact URL the validation will fail and a certificate will not be issued.");

                //copy temp file to path challenge expects in web folder
                var destFile = Path.Combine(requestConfig.WebsiteRootPath, httpChallenge.FilePath);
                var destPath = Path.GetDirectoryName(destFile);
                if (!Directory.Exists(destPath))
                {
                    Directory.CreateDirectory(destPath);
                }

                //copy challenge response to web folder /.well-known/acme-challenge
                System.IO.File.WriteAllText(destFile, httpChallenge.FileContent);

                var wellknownContentPath = httpChallenge.FilePath.Substring(0, httpChallenge.FilePath.LastIndexOf("/"));
                var testFilePath         = Path.Combine(requestConfig.WebsiteRootPath, wellknownContentPath + "//configcheck");
                System.IO.File.WriteAllText(testFilePath, "Extensionless File Config Test - OK");

                //create a web.config for extensionless files, then test it (make a request for the extensionless configcheck file over http)
                string webConfigContent = Properties.Resources.IISWebConfig;

                if (!File.Exists(destPath + "\\web.config"))
                {
                    //no existing config, attempt auto config and perform test
                    System.IO.File.WriteAllText(destPath + "\\web.config", webConfigContent);
                    if (requestConfig.PerformExtensionlessConfigChecks)
                    {
                        if (CheckURL("http://" + requestConfig.PrimaryDomain + "/" + wellknownContentPath + "/configcheck", checkViaProxy))
                        {
                            extensionlessConfigOK = true;
                        }
                    }
                }
                else
                {
                    //web config already exists, don't overwrite it, just test it

                    if (requestConfig.PerformExtensionlessConfigChecks)
                    {
                        if (CheckURL("http://" + requestConfig.PrimaryDomain + "/" + wellknownContentPath + "/configcheck", checkViaProxy))
                        {
                            extensionlessConfigOK = true;
                        }
                        if (!extensionlessConfigOK && requestConfig.PerformAutoConfig)
                        {
                            //didn't work, try our default config
                            System.IO.File.WriteAllText(destPath + "\\web.config", webConfigContent);

                            if (CheckURL("http://" + requestConfig.PrimaryDomain + "/" + wellknownContentPath + "/configcheck", checkViaProxy))
                            {
                                extensionlessConfigOK = true;
                            }
                        }
                    }
                }

                if (!extensionlessConfigOK && requestConfig.PerformAutoConfig)
                {
                    //if first attempt(s) at config failed, try an alternative config
                    webConfigContent = Properties.Resources.IISWebConfigAlt;

                    System.IO.File.WriteAllText(destPath + "\\web.config", webConfigContent);

                    if (CheckURL("http://" + requestConfig.PrimaryDomain + "/" + wellknownContentPath + "/configcheck", checkViaProxy))
                    {
                        //ready to complete challenge
                        extensionlessConfigOK = true;
                    }
                }
            }

            //configuration applied, ready to ask LE to validate our answer
            pendingAuth.ExtensionlessConfigCheckedOK = extensionlessConfigOK;
            return(pendingAuth);
        }
Exemple #4
0
        public PendingAuthorization BeginRegistrationAndValidation(CertRequestConfig requestConfig, string identifierAlias, string challengeType = "http-01", string domain = null)
        {
            //if no alternative domain specified, use the primary domains as the subject
            if (domain == null)
            {
                domain = requestConfig.PrimaryDomain;
            }

            if (GetIdentifier(identifierAlias) == null)
            {
                //if an identifier exists for the same dns in vault, remove it to avoid confusion
                this.DeleteIdentifierByDNS(domain);

                // ACME service requires international domain names in ascii mode

                if (UsePowershell)
                {
                    var result = powershellManager.NewIdentifier(idnMapping.GetAscii(domain), identifierAlias, "Identifier:" + domain);
                    if (!result.IsOK)
                    {
                        return(null);
                    }
                }
                else
                {
                    var cmd = new ACMESharp.POSH.NewIdentifier();
                    cmd.Dns   = idnMapping.GetAscii(domain);
                    cmd.Alias = identifierAlias;
                    cmd.Label = "Identifier:" + domain;

                    try
                    {
                        cmd.ExecuteCommand();
                    }
                    catch (Exception exp)
                    {
                        this.LogAction("NewIdentifier", exp.ToString());
                        return(null);
                    }
                }
            }

            var identifier = this.GetIdentifier(identifierAlias, reloadVaultConfig: true);

            if (identifier.Authorization.IsPending())
            {
                bool ccrResultOK = false;
                if (UsePowershell)
                {
                    var result = powershellManager.CompleteChallenge(identifier.Alias, challengeType, regenerate: true);
                    ccrResultOK = result.IsOK;
                }
                else
                {
                    var cmd = new ACMESharp.POSH.CompleteChallenge();
                    cmd.IdentifierRef = identifier.Alias;
                    cmd.ChallengeType = challengeType;
                    cmd.Handler       = "manual";
                    cmd.Regenerate    = new System.Management.Automation.SwitchParameter(true);
                    cmd.Repeat        = new System.Management.Automation.SwitchParameter(true);
                    cmd.ExecuteCommand();
                    ccrResultOK = true;
                }

                //get challenge info
                ReloadVaultConfig();
                identifier = GetIdentifier(identifierAlias);
                var challengeInfo = identifier.Challenges.FirstOrDefault(c => c.Value.Type == challengeType).Value;

                //identifier challenege specification is now ready for use to prepare and answer for LetsEncrypt to check
                return(new PendingAuthorization()
                {
                    Challenge = challengeInfo, Identifier = identifier, TempFilePath = "", ExtensionlessConfigCheckedOK = false
                });
            }
            else
            {
                //identifier is already valid (previously authorized)
                return(new PendingAuthorization()
                {
                    Challenge = null, Identifier = identifier, TempFilePath = "", ExtensionlessConfigCheckedOK = false
                });
            }
        }
        public PendingAuthorization BeginRegistrationAndValidation(CertRequestConfig requestConfig, string identifierAlias, string challengeType = ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_HTTP, string domain = null)
        {
            //if no alternative domain specified, use the primary domains as the subject
            if (domain == null)
            {
                domain = requestConfig.PrimaryDomain;
            }

            // if (GetIdentifier(identifierAlias) == null)

            //if an identifier exists for the same dns in vault, remove it to avoid confusion
            this.DeleteIdentifierByDNS(domain);

            // ACME service requires international domain names in ascii mode, create new identifier
            // in vault
            try
            {
                var authState = ACMESharpUtils.NewIdentifier(identifierAlias, idnMapping.GetAscii(domain));
            }
            catch (ACMESharp.AcmeClient.AcmeWebException exp)
            {
                //if we don't know the problem details, report the whole exception
                if (exp.Response?.ProblemDetail == null)
                {
                    throw exp;
                }

                // failed to register the domain identifier with LE (invalid, rate limit or CAA fail?)
                LogAction("NewIdentifier [" + domain + "]", exp.Response.ProblemDetail.OrignalContent);
                return(new PendingAuthorization {
                    AuthorizationError = $"{exp.Response.ProblemDetail.Detail} : {exp.Response.ProblemDetail.Type}"
                });
            }
            catch (Exception exp)
            {
                // failed to register the domain identifier with LE (rate limit or CAA fail?)
                LogAction("NewIdentifier [" + domain + "]", exp.ToString());
                return(new PendingAuthorization {
                    AuthorizationError = exp.ToString()
                });
            }

            Thread.Sleep(200);

            var identifier = this.GetIdentifier(identifierAlias, reloadVaultConfig: true);

            //FIXME: when validating subsequent SAN names in parallel request mode, the identifier is null?
            if (identifier != null && identifier.Authorization != null && identifier.Authorization.IsPending())
            {
                var authState = ACMESharpUtils.CompleteChallenge(identifier.Alias, challengeType, Handler: "manual", Regenerate: true, Repeat: true);
                LogAction("CompleteChallenge", authState.Status);

                //get challenge info for this identifier
                identifier = GetIdentifier(identifierAlias, reloadVaultConfig: true);
                try
                {
                    //identifier challenge specification is now ready for use to prepare and answer for LetsEncrypt to check

                    var challenges = new List <AuthorizationChallengeItem>();
                    foreach (var c in identifier.Challenges)
                    {
                        if (c.Value.Type == SupportedChallengeTypes.CHALLENGE_TYPE_HTTP)
                        {
                            var httpChallenge = (ACMESharp.ACME.HttpChallenge)c.Value.Challenge;

                            challenges.Add(new AuthorizationChallengeItem
                            {
                                ChallengeType = SupportedChallengeTypes.CHALLENGE_TYPE_HTTP,
                                ChallengeData = c.Value,
                                ResourcePath  = httpChallenge.FilePath,
                                ResourceUri   = httpChallenge.FileUrl,
                                Key           = c.Value.Token,
                                Value         = httpChallenge.FileContent
                            });
                        }

                        if (c.Value.Type == SupportedChallengeTypes.CHALLENGE_TYPE_SNI)
                        {
                            var tlsSniChallenge = (ACMESharp.ACME.TlsSniChallenge)c.Value.Challenge;
                            var tlsSniAnswer    = (ACMESharp.ACME.TlsSniChallengeAnswer)tlsSniChallenge.Answer;

                            challenges.Add(new AuthorizationChallengeItem
                            {
                                ChallengeType      = SupportedChallengeTypes.CHALLENGE_TYPE_SNI,
                                ChallengeData      = tlsSniChallenge,
                                Key                = tlsSniChallenge.Token,
                                Value              = tlsSniAnswer.KeyAuthorization,
                                HashIterationCount = tlsSniChallenge.IterationCount
                            });
                        }

                        //TODO: dns
                        if (c.Value.Type == SupportedChallengeTypes.CHALLENGE_TYPE_DNS)
                        {
                            var dnsChallenge = (ACMESharp.ACME.DnsChallenge)c.Value.Challenge;

                            challenges.Add(new AuthorizationChallengeItem
                            {
                                ChallengeType = SupportedChallengeTypes.CHALLENGE_TYPE_DNS,
                                ChallengeData = dnsChallenge,
                                Key           = dnsChallenge.RecordName,
                                Value         = dnsChallenge.RecordValue
                            });
                        }
                    }

                    return(new PendingAuthorization()
                    {
                        Challenges = challenges,
                        Identifier = GetDomainIdentifierItemFromIdentifierInfo(identifier)
                    });
                }
                catch (Exception exp)
                {
                    //identifier challenge could not be requested this time

                    LogAction("GetIdentifier", exp.ToString());

                    return(null);
                }
            }
            else
            {
                //identifier is null or already valid (previously authorized)
                return(new PendingAuthorization()
                {
                    Challenges = null,
                    Identifier = GetDomainIdentifierItemFromIdentifierInfo(identifier),
                    LogItems = this.GetActionLogSummary()
                });
            }
        }
Exemple #6
0
        public PendingAuthorization DomainInitAndRegistration(CertRequestConfig requestConfig, string identifierAlias)
        {
            /*
             * //need to manipulate file created above to set file path or request key sshould be written too.
             *
             */
            //perform domain cert requests

            string domain = requestConfig.Domain;

            // powershellManager.SetWorkingDirectory(this.vaultFolderPath);

            if (GetIdentifier(identifierAlias) == null)
            {
                var result = powershellManager.NewIdentifier(domain, identifierAlias, "Identifier:" + domain);
                if (!result.IsOK)
                {
                    return(null);
                }
            }
            ReloadVaultConfig();

            var identifier = this.GetIdentifier(identifierAlias);

            /*
             * //config file now has a temp path to write to, begin challenge (writes to temp file with challenge content)
             */
            var ccrResult = powershellManager.CompleteChallenge(identifier.Alias, regenerate: true);

            if (ccrResult.IsOK)
            {
                bool extensionlessConfigOK = false;
                //get challenge info
                ReloadVaultConfig();
                identifier = GetIdentifier(identifierAlias);
                var challengeInfo = identifier.Challenges.FirstOrDefault(c => c.Value.Type == "http-01").Value;

                //if copying the file for the user, attempt that now
                if (challengeInfo != null && requestConfig.PerformChallengeFileCopy)
                {
                    var httpChallenge = (ACMESharp.ACME.HttpChallenge)challengeInfo.Challenge;
                    //copy temp file to path challenge expects in web folder
                    var destFile = Path.Combine(requestConfig.WebsiteRootPath, httpChallenge.FilePath);
                    var destPath = Path.GetDirectoryName(destFile);
                    if (!Directory.Exists(destPath))
                    {
                        Directory.CreateDirectory(destPath);
                    }

                    //copy challenge response to web folder /.well-known/acme-challenge
                    System.IO.File.WriteAllText(destFile, httpChallenge.FileContent);

                    var wellknownContentPath = httpChallenge.FilePath.Substring(0, httpChallenge.FilePath.LastIndexOf("/"));
                    var testFilePath         = Path.Combine(requestConfig.WebsiteRootPath, wellknownContentPath + "//configcheck");
                    System.IO.File.WriteAllText(testFilePath, "Extensionless File Config Test - OK");

                    //create a web.config for extensionless files
                    string webConfigContent = Properties.Resources.IISWebConfig;
                    if (!File.Exists(destPath + "\\web.config"))
                    {
                        System.IO.File.WriteAllText(destPath + "\\web.config", webConfigContent);
                    }

                    if (CheckURL("http://" + domain + "/" + wellknownContentPath + "/configcheck"))
                    {
                        extensionlessConfigOK = true;
                    }

                    if (!extensionlessConfigOK)
                    {
                        webConfigContent = Properties.Resources.IISWebConfigAlt;

                        System.IO.File.WriteAllText(destPath + "\\web.config", webConfigContent);
                    }

                    if (CheckURL("http://" + domain + "/" + wellknownContentPath + "/configcheck"))
                    {
                        extensionlessConfigOK = true;
                    }
                    //ready to complete challenge
                }
                return(new PendingAuthorization()
                {
                    Challenge = challengeInfo, Identifier = identifier, TempFilePath = "", ExtensionlessConfigCheckedOK = extensionlessConfigOK
                });
            }
            else
            {
                return(null);
            }
        }
Exemple #7
0
        public PendingAuthorization BeginRegistrationAndValidation(CertRequestConfig requestConfig, string identifierAlias)
        {
            string domain = requestConfig.Domain;

            if (GetIdentifier(identifierAlias) == null)
            {
                //if an identifier exists for the same dns in vault, remove it to avoid confusion
                this.DeleteIdentifierByDNS(domain);
                var result = powershellManager.NewIdentifier(domain, identifierAlias, "Identifier:" + domain);
                if (!result.IsOK)
                {
                    return(null);
                }
            }

            var identifier = this.GetIdentifier(identifierAlias, reloadVaultConfig: true);

            /*
             * //config file now has a temp path to write to, begin challenge (writes to temp file with challenge content)
             */
            if (identifier.Authorization.IsPending())
            {
                var ccrResult = powershellManager.CompleteChallenge(identifier.Alias, regenerate: true);

                if (ccrResult.IsOK)
                {
                    bool extensionlessConfigOK = false;
                    bool checkViaProxy         = true;

                    //get challenge info
                    ReloadVaultConfig();
                    identifier = GetIdentifier(identifierAlias);
                    var challengeInfo = identifier.Challenges.FirstOrDefault(c => c.Value.Type == "http-01").Value;

                    //if copying the file for the user, attempt that now
                    if (challengeInfo != null && requestConfig.PerformChallengeFileCopy)
                    {
                        var httpChallenge = (ACMESharp.ACME.HttpChallenge)challengeInfo.Challenge;
                        this.LogAction("Preparing challenge response for LetsEncrypt server to check at: " + httpChallenge.FileUrl);
                        this.LogAction("If the challenge response file is not accessible at this exact URL the validation will fail and a certificate will not be issued.");

                        //copy temp file to path challenge expects in web folder
                        var destFile = Path.Combine(requestConfig.WebsiteRootPath, httpChallenge.FilePath);
                        var destPath = Path.GetDirectoryName(destFile);
                        if (!Directory.Exists(destPath))
                        {
                            Directory.CreateDirectory(destPath);
                        }

                        //copy challenge response to web folder /.well-known/acme-challenge
                        System.IO.File.WriteAllText(destFile, httpChallenge.FileContent);

                        var wellknownContentPath = httpChallenge.FilePath.Substring(0, httpChallenge.FilePath.LastIndexOf("/"));
                        var testFilePath         = Path.Combine(requestConfig.WebsiteRootPath, wellknownContentPath + "//configcheck");
                        System.IO.File.WriteAllText(testFilePath, "Extensionless File Config Test - OK");

                        //create a web.config for extensionless files, then test it (make a request for the extensionless configcheck file over http)
                        string webConfigContent = Properties.Resources.IISWebConfig;

                        if (!File.Exists(destPath + "\\web.config"))
                        {
                            //no existing config, attempt auto config and perform test
                            System.IO.File.WriteAllText(destPath + "\\web.config", webConfigContent);
                            if (requestConfig.PerformExtensionlessConfigChecks)
                            {
                                if (CheckURL("http://" + domain + "/" + wellknownContentPath + "/configcheck", checkViaProxy))
                                {
                                    extensionlessConfigOK = true;
                                }
                            }
                        }
                        else
                        {
                            //web config already exists, don't overwrite it, just test it

                            if (requestConfig.PerformExtensionlessConfigChecks)
                            {
                                if (CheckURL("http://" + domain + "/" + wellknownContentPath + "/configcheck", checkViaProxy))
                                {
                                    extensionlessConfigOK = true;
                                }
                                if (!extensionlessConfigOK && requestConfig.PerformExtensionlessAutoConfig)
                                {
                                    //didn't work, try our default config
                                    System.IO.File.WriteAllText(destPath + "\\web.config", webConfigContent);

                                    if (CheckURL("http://" + domain + "/" + wellknownContentPath + "/configcheck", checkViaProxy))
                                    {
                                        extensionlessConfigOK = true;
                                    }
                                }
                            }
                        }

                        if (!extensionlessConfigOK && requestConfig.PerformExtensionlessAutoConfig)
                        {
                            //if first attempt(s) at config failed, try an alternative config
                            webConfigContent = Properties.Resources.IISWebConfigAlt;

                            System.IO.File.WriteAllText(destPath + "\\web.config", webConfigContent);

                            if (CheckURL("http://" + domain + "/" + wellknownContentPath + "/configcheck", checkViaProxy))
                            {
                                //ready to complete challenge
                                extensionlessConfigOK = true;
                            }
                        }
                    }

                    return(new PendingAuthorization()
                    {
                        Challenge = challengeInfo, Identifier = identifier, TempFilePath = "", ExtensionlessConfigCheckedOK = extensionlessConfigOK
                    });
                }
                else
                {
                    return(null);
                }
            }
            else
            {
                //identifier is already valid (previously authorized)
                return(new PendingAuthorization()
                {
                    Challenge = null, Identifier = identifier, TempFilePath = "", ExtensionlessConfigCheckedOK = false
                });
            }
        }