Esempio n. 1
0
        internal async Task ConfigureAcmeClient()
        {
            var httpClient = _proxyService.GetHttpClient();

            httpClient.BaseAddress = _settings.BaseUri;

            _log.Verbose("Loading ACME account signer...");
            IJwsTool?signer        = null;
            var      accountSigner = AccountSigner;

            if (accountSigner != null)
            {
                signer = accountSigner.JwsTool();
            }

            _log.Verbose("Constructing ACME protocol client...");
            try
            {
                _client = new AcmeProtocolClient(
                    httpClient,
                    signer: signer,
                    usePostAsGet: _settings.Acme.PostAsGet);
            }
            catch (CryptographicException)
            {
                if (signer == null)
                {
                    // There has been a problem generate a signer for the
                    // new account, possibly because some EC curve is not
                    // on available on the system? So we give it another
                    // shot with a less fancy RSA signer
                    _log.Verbose("First chance error generating new signer, retrying with RSA instead of ECC");
                    signer = new RSJwsTool
                    {
                        KeySize = _settings.Security.RSAKeyBits
                    };
                    signer.Init();
                    _client = new AcmeProtocolClient(
                        httpClient,
                        signer: signer,
                        usePostAsGet: _settings.Acme.PostAsGet);
                }
                else
                {
                    throw;
                }
            }
            _client.BeforeHttpSend = (x, r) => _log.Debug("Send {method} request to {uri}", r.Method, r.RequestUri);
            _client.AfterHttpSend  = (x, r) => _log.Verbose("Request completed with status {s}", r.StatusCode);
            _client.Directory      = await _client.GetDirectoryAsync();

            await _client.GetNonceAsync();

            _client.Account = await LoadAccount(_client, signer);

            if (_client.Account == null)
            {
                throw new Exception("AcmeClient was unable to find or create an account");
            }
        }
Esempio n. 2
0
        private async Task <AccountDetails?> LoadAccount(AcmeProtocolClient client, IJwsTool?signer)
        {
            AccountDetails?account = null;

            if (File.Exists(AccountPath))
            {
                if (signer != null)
                {
                    _log.Debug("Loading account information from {registrationPath}", AccountPath);
                    account        = JsonConvert.DeserializeObject <AccountDetails>(File.ReadAllText(AccountPath));
                    client.Account = account;
                    // Maybe we should update the account details
                    // on every start of the program to figure out
                    // if it hasn't been suspended or cancelled?
                    // UpdateAccount();
                }
                else
                {
                    _log.Error("Account found but no valid signer could be loaded");
                }
            }
            else
            {
                var contacts = await GetContacts();

                var(_, filename, content) = await client.GetTermsOfServiceAsync();

                if (!string.IsNullOrEmpty(filename))
                {
                    if (!await AcceptTos(filename, content))
                    {
                        return(null);
                    }
                }
                account = await client.CreateAccountAsync(contacts, termsOfServiceAgreed : true);

                _log.Debug("Saving registration");
                var accountKey = new AccountSigner
                {
                    KeyType   = client.Signer.JwsAlg,
                    KeyExport = client.Signer.Export(),
                };
                AccountSigner = accountKey;
                File.WriteAllText(AccountPath, JsonConvert.SerializeObject(account));
            }
            return(account);
        }
Esempio n. 3
0
        internal AcmeProtocolClient PrepareClient(HttpClient httpClient, IJwsTool?signer)
        {
            AcmeProtocolClient?client = null;

            _log.Verbose("Constructing ACME protocol client...");
            try
            {
                client = new AcmeProtocolClient(
                    httpClient,
                    signer: signer,
                    usePostAsGet: _settings.Acme.PostAsGet);
            }
            catch (CryptographicException)
            {
                if (signer == null)
                {
                    // There has been a problem generate a signer for the
                    // new account, possibly because some EC curve is not
                    // on available on the system? So we give it another
                    // shot with a less fancy RSA signer
                    _log.Verbose("First chance error generating new signer, retrying with RSA instead of ECC");
                    signer = new RSJwsTool
                    {
                        KeySize = _settings.Security.RSAKeyBits
                    };
                    signer.Init();
                    client = new AcmeProtocolClient(
                        httpClient,
                        signer: signer,
                        usePostAsGet: _settings.Acme.PostAsGet);
                }
                else
                {
                    throw;
                }
            }
            client.BeforeHttpSend = (x, r) => _log.Debug("Send {method} request to {uri}", r.Method, r.RequestUri);
            client.AfterHttpSend  = (x, r) => _log.Verbose("Request completed with status {s}", r.StatusCode);
            return(client);
        }
Esempio n. 4
0
        internal async Task ConfigureAcmeClient()
        {
            _log.Verbose("Loading ACME account signer...");
            var      accountSigner = AccountSigner;
            IJwsTool?signer        = null;

            if (accountSigner != null)
            {
                signer = accountSigner.JwsTool();
            }

            var httpClient = _proxyService.GetHttpClient();

            httpClient.BaseAddress = _settings.BaseUri;
            var client = PrepareClient(httpClient, signer);

            try
            {
                client.Directory = await client.GetDirectoryAsync();
            }
            catch (Exception)
            {
                // Perhaps the BaseUri *is* the directory, such
                // as implemented by Digicert (#1434)
                client.Directory.Directory = "";
                client.Directory           = await client.GetDirectoryAsync();
            }
            await client.GetNonceAsync();

            client.Account = await LoadAccount(client, signer);

            if (client.Account == null)
            {
                throw new Exception("AcmeClient was unable to find or create an account");
            }
            _client = client;
            _log.Verbose("ACME client initialized");
        }
Esempio n. 5
0
        internal AcmeProtocolClient PrepareClient(HttpClient httpClient, IJwsTool?signer)
        {
            _log.Verbose("Constructing ACME protocol client...");
            AcmeProtocolClient?client;

            try
            {
                client = new AcmeProtocolClient(
                    httpClient,
                    signer: signer,
                    usePostAsGet: _settings.Acme.PostAsGet);
            }
            catch (CryptographicException)
            {
                if (signer == null)
                {
                    // There has been a problem generate a signer for the
                    // new account, possibly because some EC curve is not
                    // on available on the system? So we give it another
                    // shot with a less fancy RSA signer
                    _log.Verbose("First chance error generating new signer, retrying with RSA instead of ECC");
                    signer = new RSJwsTool
                    {
                        KeySize = _settings.Security.RSAKeyBits
                    };
                    signer.Init();
                    client = new AcmeProtocolClient(
                        httpClient,
                        signer: signer,
                        usePostAsGet: _settings.Acme.PostAsGet);
                }
                else
                {
                    throw;
                }
            }
            return(client);
        }
Esempio n. 6
0
        private async Task <AccountDetails?> LoadAccount(AcmeProtocolClient client, IJwsTool?signer)
        {
            _log.Verbose("Loading ACME account");
            AccountDetails?account = null;

            if (File.Exists(AccountPath))
            {
                if (signer != null)
                {
                    _log.Debug("Loading account information from {registrationPath}", AccountPath);
                    account        = JsonConvert.DeserializeObject <AccountDetails>(File.ReadAllText(AccountPath));
                    client.Account = account;
                    // Maybe we should update the account details
                    // on every start of the program to figure out
                    // if it hasn't been suspended or cancelled?
                    // UpdateAccount();
                }
                else
                {
                    _log.Error("Account found but no valid signer could be loaded");
                }
            }
            else
            {
                _log.Verbose("No account found at {path}, creating new one", AccountPath);
                try
                {
                    var(_, filename, content) = await client.GetTermsOfServiceAsync();

                    _log.Verbose("Terms of service downloaded");
                    if (!string.IsNullOrEmpty(filename))
                    {
                        if (!await AcceptTos(filename, content))
                        {
                            return(null);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Error getting terms of service");
                }
                var contacts        = default(string[]);
                var externalAccount = default(ExternalAccountBinding);

                var kid     = _accountArguments.EabKeyIdentifier;
                var key     = _accountArguments.EabKey;
                var alg     = _accountArguments.EabAlgorithm ?? "HS256";
                var eabFlow = client.Directory?.Meta?.ExternalAccountRequired == "true";
                if (eabFlow)
                {
                    _input.CreateSpace();
                    _input.Show(null, "This ACME endpoint requires an external account. You will " +
                                "need to provide a key identifier and a key to proceed. Please refer to the " +
                                "providers instructions on how to obtain these.");
                }
                else if (!string.IsNullOrWhiteSpace(kid))
                {
                    eabFlow = true;
                    _input.CreateSpace();
                    _input.Show(null, "You have provided external account binding key, but the server" +
                                "claims that is not required. We will attempt to register using this key anyway.");
                }
                if (eabFlow)
                {
                    if (string.IsNullOrWhiteSpace(kid))
                    {
                        kid = await _input.RequestString("Key identifier");
                    }
                    if (string.IsNullOrWhiteSpace(key))
                    {
                        key = await _input.ReadPassword("Key (base64url encoded)");
                    }
                    externalAccount = new ExternalAccountBinding(
                        alg,
                        JsonConvert.SerializeObject(client.Signer.ExportJwk(), Formatting.None),
                        kid,
                        key ?? "",
                        client.Directory?.NewAccount ?? "");
                }
                else
                {
                    contacts = await GetContacts();
                }

                try
                {
                    account = await client.CreateAccountAsync(
                        contacts,
                        termsOfServiceAgreed : true,
                        externalAccountBinding : externalAccount?.Payload() ?? null);
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Error creating account");
                }

                if (account != null)
                {
                    try
                    {
                        _log.Debug("Saving account");
                        var accountKey = new AccountSigner
                        {
                            KeyType   = client.Signer.JwsAlg,
                            KeyExport = client.Signer.Export(),
                        };
                        AccountSigner = accountKey;
                        await File.WriteAllTextAsync(AccountPath, JsonConvert.SerializeObject(account));
                    }
                    catch (Exception ex)
                    {
                        _log.Error(ex, "Error saving account");
                        account = null;
                    }
                }
            }
            return(account);
        }
Esempio n. 7
0
        private async Task <AccountDetails?> LoadAccount(AcmeProtocolClient client, IJwsTool?signer)
        {
            _log.Verbose("Loading ACME account");
            AccountDetails?account = null;

            if (File.Exists(AccountPath))
            {
                if (signer != null)
                {
                    _log.Debug("Loading account information from {registrationPath}", AccountPath);
                    account        = JsonConvert.DeserializeObject <AccountDetails>(File.ReadAllText(AccountPath));
                    client.Account = account;
                    // Maybe we should update the account details
                    // on every start of the program to figure out
                    // if it hasn't been suspended or cancelled?
                    // UpdateAccount();
                }
                else
                {
                    _log.Error("Account found but no valid signer could be loaded");
                }
            }
            else
            {
                _log.Verbose("No account found at {path}, creating new one", AccountPath);
                try
                {
                    var(_, filename, content) = await client.GetTermsOfServiceAsync();

                    _log.Verbose("Terms of service downloaded");
                    if (!string.IsNullOrEmpty(filename))
                    {
                        if (!await AcceptTos(filename, content))
                        {
                            return(null);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Error getting terms of service");
                }
                var contacts        = default(string[]);
                var externalAccount = default(ExternalAccountBinding);

                var kid         = _accountArguments.EabKeyIdentifier;
                var key         = _accountArguments.EabKey;
                var alg         = _accountArguments.EabAlgorithm ?? "HS256";
                var eabFlow     = client.Directory?.Meta?.ExternalAccountRequired == "true";
                var zeroSslFlow = _settings.BaseUri.Host.Contains("zerossl.com");

                // Warn about unneeded EAB
                if (!eabFlow && !string.IsNullOrWhiteSpace(kid))
                {
                    eabFlow = true;
                    _input.CreateSpace();
                    _input.Show(null, "You have provided an external account binding key, even though " +
                                "the server does not indicate that this is required. We will attempt to register " +
                                "using this key anyway.");
                }

                if (zeroSslFlow)
                {
                    async Task emailRegistration()
                    {
                        var registration = await GetContacts(allowMultiple : false, prefix : "");

                        var eab = await _zeroSsl.Register(registration.FirstOrDefault() ?? "");

                        if (eab != null)
                        {
                            kid = eab.Kid;
                            key = eab.Hmac;
                        }
                        else
                        {
                            _log.Error("Unable to retrieve EAB credentials using the provided email address");
                        }
                    }

                    async Task apiKeyRegistration()
                    {
                        var accessKey = await _input.ReadPassword("API access key");

                        var eab = await _zeroSsl.Obtain(accessKey ?? "");

                        if (eab != null)
                        {
                            kid = eab.Kid;
                            key = eab.Hmac;
                        }
                        else
                        {
                            _log.Error("Unable to retrieve EAB credentials using the provided API access key");
                        }
                    }

                    if (!string.IsNullOrWhiteSpace(_accountArguments.EmailAddress))
                    {
                        await emailRegistration();
                    }
                    else
                    {
                        var instruction = "ZeroSsl can be used either by setting up a new " +
                                          "account using your email address or by connecting it to your existing " +
                                          "account using the API access key or pre-generated EAB credentials, which can " +
                                          "be obtained from the Developer section of the dashboard.";
                        _input.CreateSpace();
                        _input.Show(null, instruction);
                        var chosen = await _input.ChooseFromMenu(
                            "How would you like to create the account?",
                            new List <Choice <Func <Task> > >()
                        {
                            Choice.Create((Func <Task>)apiKeyRegistration, "API access key"),
                            Choice.Create((Func <Task>)emailRegistration, "Email address"),
                            Choice.Create <Func <Task> >(() => Task.CompletedTask, "Input EAB credentials directly")
                        });

                        await chosen.Invoke();
                    }
                }

                if (eabFlow)
                {
                    if (string.IsNullOrWhiteSpace(kid))
                    {
                        var instruction = "This ACME endpoint requires an external account. " +
                                          "You will need to provide a key identifier and a key to proceed. " +
                                          "Please refer to the providers instructions on how to obtain these.";
                        _input.CreateSpace();
                        _input.Show(null, instruction);
                        kid = await _input.RequestString("Key identifier");
                    }
                    if (string.IsNullOrWhiteSpace(key))
                    {
                        key = await _input.ReadPassword("Key (base64url encoded)");
                    }
                    externalAccount = new ExternalAccountBinding(
                        alg,
                        JsonConvert.SerializeObject(
                            client.Signer.ExportJwk(),
                            Formatting.None),
                        kid,
                        key ?? "",
                        client.Directory?.NewAccount ?? "");
                }
                else
                {
                    contacts = await GetContacts();
                }

                try
                {
                    account = await client.CreateAccountAsync(
                        contacts,
                        termsOfServiceAgreed : true,
                        externalAccountBinding : externalAccount?.Payload() ?? null);
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Error creating account");
                }

                if (account != null)
                {
                    try
                    {
                        _log.Debug("Saving account");
                        var accountKey = new AccountSigner
                        {
                            KeyType   = client.Signer.JwsAlg,
                            KeyExport = client.Signer.Export(),
                        };
                        AccountSigner = accountKey;
                        await File.WriteAllTextAsync(AccountPath, JsonConvert.SerializeObject(account));
                    }
                    catch (Exception ex)
                    {
                        _log.Error(ex, "Error saving account");
                        account = null;
                    }
                }
            }
            return(account);
        }