Example #1
0
        public X509Certificate2 ConvertCertificate(X509Certificate certificate,
                                                   AsymmetricCipherKeyPair subjectKeyPair,
                                                   SecureRandom random, string password)
        {
            // Now to convert the Bouncy Castle certificate to a .NET certificate.
            // See http://web.archive.org/web/20100504192226/http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx
            // ...but, basically, we create a PKCS12 store (a .PFX file) in memory, and add the public and private key to that.
            var store = new Pkcs12Store();

            // What Bouncy Castle calls "alias" is the same as what Windows terms the "friendly name".
            string friendlyName = certificate.SubjectDN.ToString();

            // Add the certificate.
            var certificateEntry = new X509CertificateEntry(certificate);

            store.SetCertificateEntry(friendlyName, certificateEntry);

            // Add the private key.
            store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), new[] { certificateEntry });

            var temporaryPassword = password ?? PasswordUtils.GeneratePassword(12, 6);

            // Convert it to an X509Certificate2 object by saving/loading it from a MemoryStream.
            // It needs a password. Since we'll remove this later, it doesn't particularly matter what we use.
            var stream = new MemoryStream();

            store.Save(stream, temporaryPassword.ToCharArray(), random);

            var convertedCertificate =
                new X509Certificate2(stream.ToArray(),
                                     temporaryPassword,
                                     X509KeyStorageFlags.Exportable);

            return(convertedCertificate);
        }
Example #2
0
        public static UserAccount CreateNewUser()
        {
            var result = new UserAccount
            {
                UserId     = Guid.NewGuid(),
                UserSecret = PasswordUtils.GeneratePassword(),
                Salt       = PasswordUtils.GenerateSalt()
            };

            return(result);
        }
 public static PasswordPrivacyStrategy Create()
 {
     return(new PasswordPrivacyStrategy(new PBECipherEncryptor(),
                                        PasswordUtils.GeneratePassword()));
 }
Example #4
0
        public async Task <IActionResult> Create([FromBody] User user)
        {
            if (ModelState.IsValid)
            {
                // Invoked with default operation = create param
                if (!user.Validate(out var validationErrors))
                {
                    _response.Errors.Add(Errors.VALIDATION_FAILED, validationErrors);
                }

                if (user.AuthKey.Equals(AppConfig.CloudConnectDefaultAuthKey))
                {
                    _response.Errors.Add(Errors.RESOURCE_RESERVED, "");
                }
            }
            else
            {
                _response.Errors.Add(Errors.MISSING_BODY, "");
            }

            if (HasErrors())
            {
                return(BadRequest(_response));
            }

            if ((user.HasRole(Models.User.Role.SuperAdmin) ||
                 user.HasRole(Models.User.Role.WebApi)) && !CurrentUser().HasRole(Models.User.Role.SuperAdmin))
            {
                // Privilege escalation attempt, shut it down.
                _response.Errors.Add(Errors.ROLE_ESCALATION_FAILED, "");
                return(StatusCode(403, _response));
            }


            long userId = -1;

            user.CreatedDate = DateTime.Now;
            user.Source      = Models.User.SourceTypes.Local;

            if (!user.AuthKey.ToLower().Equals(user.AuthKey))
            {
                user.AuthKey      = user.AuthKey.ToLower();
                _response.Message = Messages.USER_AUTHKEY_FLATTENED;
            }

            // Check if the user would like their certificate encrypted.
            if (user.EncryptCertificate.HasValue && user.EncryptCertificate.Value)
            {
                user.CertKey = PasswordUtils.GeneratePassword(48, 6);
            }
            else
            {
                user.CertKey = null;
            }

            var userCertBytes = _cryptoService.IssueUserChain(user.AuthKey, new[] { KeyPurposeID.IdKPClientAuth }, user.CertKey);

            user.Cert = Convert.ToBase64String(userCertBytes);

            var existingUser = await Db.SingleAsync <User>(x => x.AuthKey == user.AuthKey);

            if (existingUser != null)
            {
                // Some user already exists with this authkey, let's bail
                _response.Errors.Add(Errors.CRUD_OPERATION_FAILED, "");
                _response.Errors.Add(Errors.VALIDATION_FAILED,
                                     new[] { Errors.RESOURCE_EXISTS + ":authKey:" + user.AuthKey });
                return(StatusCode(409, _response));
            }

            try
            {
                userId = await Db.InsertAsync(user, true);
            }
            catch (DbException e)
            {
                Logger.LogError(e.Message);
                _response.Errors.Add(Errors.OBJECT_PERSIST_FAILED, e.Message);
            }

            if (HasErrors())
            {
                return(BadRequest(_response));
            }

            user.Id          = userId;
            _response.Result = user;

            return(Created(Url.RouteUrl("GetUserById", new { id = userId }), _response));
        }
Example #5
0
        Connect(HttpContext httpContext, string nodeKey)
        {
            var errors = new Dictionary <string, object>();

            // Ok, we aren't already connected. Let's go try talking to the backend and set ourselves up.
            var request = new RestRequest("unauth/node", Method.POST)
            {
                RequestFormat = DataFormat.Json
            };

            var generatedPassword = PasswordUtils.GeneratePassword(24, 0);
            var body = new UnauthNodeAddRequest {
                InstallId = _identityProvider.GetGuid().ToString()
            };

            var ownIp = await _ipResolver.Resolve();

            body.Ip = ownIp.ToString();

            body.Port = httpContext.Connection.LocalPort;

            body.Protocol = "http"; // TODO: When HTTPs support lands, use -> httpContext.Request.Protocol.ToLower() which returns things like http/1.1 (needs further parsing);

            body.NodeKey     = nodeKey;
            body.AccessToken = _defaultCloudUserName + ":" + generatedPassword;

            // This is data about *THIS* specific system being contributed to the cloud/CRM.
            body.SystemData = _apm.GetAllDetails();
            body.Version    = AppConfig.version;

            // Ok, we got the user created. Everything is ready, let's send off the request.
            var serializedBody = JsonConvert.SerializeObject(body);

            _logger.LogDebug($"We will be sending: {serializedBody}");

            request.AddParameter("application/json; charset=utf-8", serializedBody, ParameterType.RequestBody);

            // We have to ensure this user actually exists before sending off the request.
            // First, we need to remove any cached representation.
            AuthUtils.ClearUserFromCacheIfExists(_cache, _defaultCloudUserName);

            // Check if the cloud connect user exists already.
            var user = await _db.SingleAsync <User>(x => x.AuthKey == _defaultCloudUserName) ?? new User();

            user.AuthKey        = _defaultCloudUserName;
            user.PasswordSetter = generatedPassword;
            user.EmailAddress   = _defaultCloudUserName + $"@spectero.com";
            user.FullName       = "Spectero Cloud Management User";
            user.Roles          = new List <User.Role> {
                User.Role.SuperAdmin
            };
            user.Source        = User.SourceTypes.SpecteroCloud;
            user.CloudSyncDate = DateTime.Now;
            user.CertKey       = PasswordUtils.GeneratePassword(48, 6);

            var userCertBytes = _cryptoService.IssueUserChain(user.AuthKey, new[] { KeyPurposeID.IdKPClientAuth }, user.CertKey);

            user.Cert = Convert.ToBase64String(userCertBytes);

            // Checks if user existed already, or is being newly created.
            if (user.Id != 0L)
            {
                await _db.UpdateAsync(user);
            }
            else
            {
                user.CreatedDate = DateTime.Now;
                await _db.InsertAsync(user);
            }

            var response = _restClient.Execute(request);


            if (response.ErrorException != null)
            {
                _logger.LogError(response.ErrorException, "CC: Connect attempt to the Spectero Cloud failed!");

                errors.Add(Core.Constants.Errors.FAILED_TO_CONNECT_TO_SPECTERO_CLOUD, response.ErrorMessage);

                await DeleteCloudUserIfExists();

                return(false, errors, HttpStatusCode.ServiceUnavailable, null);
            }

            CloudAPIResponse <Node> parsedResponse = null;

            try
            {
                // Parse after error checking.
                parsedResponse = JsonConvert.DeserializeObject <CloudAPIResponse <Node> >(response.Content);
            }
            catch (JsonException e)

            {
                // The Cloud Backend fed us bogus stuff, let's bail.
                _logger.LogError(e, "CC: Connect attempt to the Spectero Cloud failed!");
                _logger.LogDebug("Cloud API said: " + response.Content);

                errors.Add(Core.Constants.Errors.FAILED_TO_CONNECT_TO_SPECTERO_CLOUD, e.Message);

                await DeleteCloudUserIfExists();

                return(false, errors, HttpStatusCode.ServiceUnavailable, parsedResponse);
            }


            // ReSharper disable once SwitchStatementMissingSomeCases
            switch (response.StatusCode)
            {
            case HttpStatusCode.Created:

                await ConfigUtils.CreateOrUpdateConfig(_db, ConfigKeys.CloudConnectStatus, true.ToString());

                await ConfigUtils.CreateOrUpdateConfig(_db, ConfigKeys.CloudConnectIdentifier, parsedResponse?.result.id.ToString());

                await ConfigUtils.CreateOrUpdateConfig(_db, ConfigKeys.CloudConnectNodeKey, nodeKey);

                break;

            default:
                // Likely a 400 or a 409, just show the response as is.
                errors.Add(Core.Constants.Errors.FAILED_TO_CONNECT_TO_SPECTERO_CLOUD, "");
                errors.Add(Core.Constants.Errors.RESPONSE_CODE, response.StatusCode);
                errors.Add(Core.Constants.Errors.NODE_PERSIST_FAILED, parsedResponse?.errors);

                _logger.LogDebug("Cloud API said: " + response.Content);

                await DeleteCloudUserIfExists();

                return(false, errors, HttpStatusCode.ServiceUnavailable, parsedResponse);
            }

            return(true, errors, HttpStatusCode.OK, parsedResponse);
        }
Example #6
0
        public void Up()
        {
            var  instanceId         = Guid.NewGuid().ToString();
            long viablePasswordCost = _config.PasswordCostLowerThreshold;

            var specteroCertKey = "";
            X509Certificate2 specteroCertificate = null;
            X509Certificate2 ca = null;

            var localIPs = Utility.GetLocalIPs(_config.IgnoreRFC1918);


            if (!_db.TableExists <Configuration>())
            {
                _db.CreateTable <Configuration>();
                _logger.LogDebug("Firstrun: Creating Configurations table and inserting default values");

                // Identity
                _db.Insert(new Configuration
                {
                    Key   = ConfigKeys.SystemIdentity,
                    Value = instanceId
                });

                // Schema version
                _db.Insert(new Configuration
                {
                    Key   = ConfigKeys.SchemaVersion,
                    Value = AppConfig.version
                });

                // Cloud Connectivity
                _db.Insert(new Configuration
                {
                    Key   = ConfigKeys.CloudConnectStatus,
                    Value = false.ToString()
                });

                // HTTP proxy
                var httpSkeleton      = Defaults.HTTP.Value;
                var proposedListeners = localIPs.Select(ip => Tuple.Create(ip.ToString(), 10240))
                                        .ToList();
                if (proposedListeners.Count > 0)
                {
                    httpSkeleton.listeners = proposedListeners;
                }

                _db.Insert(new Configuration
                {
                    Key   = ConfigKeys.HttpConfig,
                    Value = JsonConvert.SerializeObject(httpSkeleton)
                });

                // Password Hashing
                _logger.LogDebug("Firstrun: Calculating optimal password hashing cost.");
                viablePasswordCost = AuthUtils.GenerateViableCost(_config.PasswordCostCalculationTestTarget,
                                                                  _config.PasswordCostCalculationIterations,
                                                                  _config.PasswordCostTimeThreshold, _config.PasswordCostLowerThreshold);
                _logger.LogDebug($"Firstrun: Determined {viablePasswordCost} to be the optimal password hashing cost.");
                _db.Insert(new Configuration
                {
                    Key   = ConfigKeys.PasswordHashingCost,
                    Value = viablePasswordCost.ToString()
                });

                // JWT security Key
                _logger.LogDebug("Firstrun: Generating JWT security key.");
                _db.Insert(new Configuration
                {
                    Key   = ConfigKeys.JWTSymmetricSecurityKey,
                    Value = PasswordUtils.GeneratePassword(48, 8)
                });

                // Crypto
                // 48 characters len with 12 non-alpha-num characters
                // Ought to be good enough for everyone. -- The IPv4 working group, 1996
                var caPassword     = PasswordUtils.GeneratePassword(48, 8);
                var serverPassword = PasswordUtils.GeneratePassword(48, 8);

                ca = _cryptoService.CreateCertificateAuthorityCertificate($"CN={instanceId}.ca.instance.spectero.io",
                                                                          null, null, caPassword);

                var serverCertificate = _cryptoService.IssueCertificate($"CN={instanceId}.instance.spectero.io", ca, null,
                                                                        new[] { KeyPurposeID.AnyExtendedKeyUsage, KeyPurposeID.IdKPServerAuth }, serverPassword,
                                                                        new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.KeyEncipherment));

                specteroCertKey     = PasswordUtils.GeneratePassword(48, 8);
                specteroCertificate = _cryptoService.IssueCertificate(
                    "CN=" + "spectero", ca, null,
                    new[] { KeyPurposeID.IdKPClientAuth }, specteroCertKey);


                _db.Insert(new Configuration
                {
                    Key   = ConfigKeys.CeritificationAuthorityPassword,
                    Value = caPassword
                });

                _db.Insert(new Configuration
                {
                    Key   = ConfigKeys.ServerCertificatePassword,
                    Value = serverPassword
                });

                _db.Insert(new Configuration
                {
                    Key   = ConfigKeys.CertificationAuthority,
                    Value = Convert.ToBase64String(_cryptoService.GetCertificateBytes(ca, caPassword))
                });

                _db.Insert(new Configuration
                {
                    Key   = ConfigKeys.ServerCertificate,
                    Value = Convert.ToBase64String(_cryptoService.GetCertificateBytes(serverCertificate, serverPassword))
                });

                _db.Insert(new Configuration
                {
                    Key   = ConfigKeys.ServerPFXChain,
                    Value = Convert.ToBase64String(_cryptoService.ExportCertificateChain(serverCertificate, ca)) // Yes, passwordless. Somewhat intentionally, as this is mostly consumed by 3rd party apps.
                });

                // OpenVPN defaults

                _db.Insert(new Configuration
                {
                    Key   = ConfigKeys.OpenVPNListeners,
                    Value = JsonConvert.SerializeObject(Defaults.OpenVPNListeners)
                });

                _db.Insert(new Configuration
                {
                    Key   = ConfigKeys.OpenVPNBaseConfig,
                    Value = JsonConvert.SerializeObject(Defaults.OpenVPN.Value)
                });
            }

            if (!_db.TableExists <User>())
            {
                _logger.LogDebug("Firstrun: Creating Users table");
                var password = PasswordUtils.GeneratePassword(16, 8);
                _db.CreateTable <User>();
                _db.Insert(new User
                {
                    AuthKey = "spectero",
                    Roles   = new List <User.Role>
                    {
                        User.Role.SuperAdmin
                    },
                    FullName     = "Spectero Administrator",
                    EmailAddress = "*****@*****.**",
                    Password     = BCrypt.Net.BCrypt.HashPassword(password, (int)viablePasswordCost),
                    Cert         = specteroCertificate != null && ca != null ? Convert.ToBase64String(_cryptoService.ExportCertificateChain(specteroCertificate, ca, specteroCertKey)) : "",
                    CertKey      = specteroCertKey,
                    Source       = User.SourceTypes.Local,
                    CreatedDate  = DateTime.Now
                });

                using (var tw = new StreamWriter(_firstRunConfigName, false))
                {
                    tw.WriteLine("username: spectero");
                    tw.WriteLine($"password: {password}");
                }
            }


            if (!_db.TableExists <Statistic>())
            {
                _logger.LogDebug("Firstrun: Creating Statistics table");
                _db.CreateTable <Statistic>();

                _logger.LogInformation("Firstrun: Initialization complete.");
            }
        }