/// <summary> /// Generates a random password with a high entropy. /// </summary> /// <returns>Random ASCII password.</returns> public static string GenerateRandomPassword() { var passArray = new char[30]; string password; var csprng = new SecureRandom(new DigestRandomGenerator(new Sha256Digest())); csprng.SetSeed(DateTime.Now.Ticks); // TODO: is this a good seed value? while (true) { for (var i = 0; i < 30; ++i) { // ASCII printable characters are >= SPACE (0x20) and < DEL (0x7e). passArray[i] = (char)csprng.Next(0x20, 0x7f); } password = new string(passArray); //passArray = Enumerable.Repeat('0', passArray.Length).ToArray(); // zeroization if (PasswordAdvisor.IsPasswordStrong(password, out var _, false)) { break; } } return(password); }
/// <summary> /// Registers a new user if the register policy are met. /// </summary> /// <param name="username">Users account username.</param> /// <param name="password">Users password.</param> /// <param name="certificateFilePath">Path on FS to users certificate.</param> public void Register(ref string username, string password, string certificateFilePath, bool skipPasswordStrengthCheck = false) { if (username.Length > 25) { throw new Exception("Usernames can't have more than 25 characters."); } if (!skipPasswordStrengthCheck && password.Contains(username)) { throw new Exception("Password cannot contain your username."); } // Probability of repetition of this do-while loop is low. do { // Add a random 4-digit number to users username. var csprng = new SecureRandom(new DigestRandomGenerator(new Sha256Digest())); csprng.SetSeed(DateTime.Now.Ticks); // TODO: is this a good seed value? var suffix = "#" + csprng.Next(1_000, 9_999).ToString(); // If username collision occurs, generate new 4-digit suffix. if (data.GetUser(username + suffix) != null) { continue; } else { username += suffix; break; } } while (true); // Check if a password used some of the most common passwords discovered in various data breaches. if (!skipPasswordStrengthCheck && PasswordAdvisor.CommonPasswordCheck(password, commonPasswordsPath)) { throw new Exception("This password is not allowed. Please try again."); } // Check password strength. if (!skipPasswordStrengthCheck && !PasswordAdvisor.IsPasswordStrong(password, out var passwordStrength, false)) { throw new Exception($"Password is deemed {passwordStrength}. Please try again."); } var cert = new X509Certificate2(certificateFilePath); // Check if key length is >= 2048 bits. if (CertificateValidator.VerifyCertificateKeyLength(cert) == false) { throw new Exception("Key length has to be at least 2048 bits."); } // Checks if the certificate has expired and if it is issued by a proper root certificate. if (CertificateValidator.VerifyCertificate(cert, caTrustListPath, out var errorMsg, true) == false) { throw new Exception(errorMsg); } // Check if the certificate is revoked. if (CertificateValidator.VerifyCertificateRevocationStatus(cert, crlListPath, caTrustListPath) == true) { throw new Exception("Certificate has been revoked."); } // Check if certificate has a proper key usage set. if (CertificateValidator.VerifyKeyUsage(cert) == false) { throw new Exception("Certificate must have 'digitalSignature' and 'keyEncipherment' set as it's key usage."); } }
/// <summary> /// Generates a random passphrase that contains between 6 and 10 words using a <em>Diceware</em> method (<see href="https://www.eff.org/dice">EFF Dice-Generated Passphrases</see>). /// </summary> /// <returns>Random ASCII password createad using a <em>Diceware</em> method.</returns> public static string GeneratePassphrase(string dicewareWordsPath) { var diceRollResult = 0; string passphrase; var delimiter = GeneratePassphraseDelimiter(); var csprng = new SecureRandom(new DigestRandomGenerator(new Sha256Digest())); csprng.SetSeed(DateTime.Now.Ticks); // TODO: is this a good seed value? var maxNumberOfWords = csprng.Next(6, 10); // Loop only repeats if the generated passphrase has low entropy; this loop will never repeat because for the minimum of 6 words passphrase will have a good entropy. while (true) { var numberOfWords = 0; string index; passphrase = ""; // Loop repeats until we create a passphrase with an appropriate number of words. do { var numberExist = false; // Loop is used if the resulting 5-digit number isn't in the list. do { // five dice rolls for (var i = 0; i < 5; ++i) { diceRollResult += csprng.Next(1, 7) * (int)Math.Pow(10, i); } index = Convert.ToString(diceRollResult); diceRollResult = 0; // TODO: can this be optimized? using (var file = new StreamReader(dicewareWordsPath)) { string line = null; while ((line = file.ReadLine()) != null) { if (line.Contains(index)) { passphrase += line.Split('\t')[1].Trim(); numberOfWords++; numberExist = true; break; } } } } while (!numberExist); // Add delimiter between words. if (numberOfWords != maxNumberOfWords - 1) { passphrase += delimiter; } } while (numberOfWords < maxNumberOfWords); if (PasswordAdvisor.IsPasswordStrong(passphrase, out _, true, maxNumberOfWords)) { break; } } return(passphrase); }