0

I'm generating a random password automatically but unable to satisfy the requirements. The problem is that the password generated does not have upper case or lowercase or a number or a special character.

This is what I've tried for generating the random password:

public string RandomPassword() {
  string lettre = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  string caractere = "!-_*+&$";
  string number = "0123456789";
  string ensemble = "";
  ensemble += lettre;
  ensemble += number;
  ensemble += caractere;

  // Ici, ensemble contient donc la totalité des caractères autorisés

  string password = "";
  int taillePwd = 8;
  Random rand = new Random();
  for (int i = 0; i < taillePwd; i++) {
    // On ajoute un caractère parmi tous les caractères autorisés
    password += ensemble[rand.Next(0, ensemble.Length)];
  }
  return password;
}

What should I fix, so I can force it to have one upper case, one lower case, one number and one special character. Thanks.

Pirate
  • 1,167
  • 1
  • 9
  • 19
zarzou
  • 113
  • 14
  • 4
    This question will probably be closed, but this is something that you can probably think through yourself, which is a skill more valuable than just getting an answer. Ask yourself "what am I trying to accomplish, with specific questions. In other words, start with "how can I make sure I get one capital letter?" Then, move on to each piece. – Wonko the Sane Dec 21 '20 at 13:50
  • 1
    So, to build on the above - get at least one of each type of required character, add as many remaining characters as required, then shuffle the characters. – stuartd Dec 21 '20 at 13:54
  • [This link has an answer for you](https://codeshare.co.uk/blog/how-to-create-a-random-password-generator-in-c/) – Pirate Dec 21 '20 at 14:22

4 Answers4

2

Give this a try:

public static void Main()
{
    const string upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    const string lower = "abcdefghijklmnopqrstuvwxyz";
    const string character = "!-_*+&$";
    const string number = "0123456789";

    Console.WriteLine(GetPwd(10, upper, lower, character, number));
}

private static Random _rnd = new Random();
private static string GetPwd(int length, params string[] keys)
{
    var chars = new char[length];
    for (int i = 0; i < keys.Length; i++)
    {
        var key = keys[i];
        chars[i] = key[_rnd.Next(key.Length)];
    }
    
    for (int i = keys.Length; i < length; i++)
    {
        var indexKeys = _rnd.Next(keys.Length);
        var key = keys[indexKeys];
        chars[i] = key[_rnd.Next(key.Length)];
    }
    
    return new string(chars.OrderBy(x => Guid.NewGuid()).ToArray());
}
Rand Random
  • 7,300
  • 10
  • 40
  • 88
  • how can you be sure NewGuid would sort in a random order? – MikeJ Dec 21 '20 at 15:30
  • @MikeJ - because it generates a new `Guid` every time this is hit, and a `Guid` should be unique everytime this method calls. The question you have picked for the random shuffel, also contains this answer, you can read the comments there if you have further questions: https://stackoverflow.com/a/4262134/2598770 – Rand Random Dec 21 '20 at 15:35
  • Right, and the comments also mention that NewGuid gives you a unique Guid not necessarily a random ordering. – MikeJ Dec 21 '20 at 15:39
  • @MikeJ - I am more on the side of this comment "It gets the job done in an elegant way. Many people just want their lists shuffled and don't care about how random it is. They prefer this oneliner over any of the other, more academic, methods. I know I do. Thank you!" – Rand Random Dec 21 '20 at 15:40
  • I guess I'd stick with Eric LIppert's comment "[..] If you're using a GUID for a purpose other than creating a unique value, you're doing it wrong" – MikeJ Dec 21 '20 at 15:42
  • @MikeJ - Eric Lippert always underestimates the laziness of developers, we want it easy no matter if it will fail 2% of the time :) – Rand Random Dec 21 '20 at 15:44
  • Yeah probably true. I like using an actual shuffle algorithm better than the orderby but your algorithm for the pwd is cleaner and more extensible than what I came up with. Nice job. – MikeJ Dec 21 '20 at 15:57
1

This is a bit tricky to get it to be totally random. I think @stuartd is right in that the way to do it is to get all categories of chars and then random shuffle them at the end. Here's one way to do that. I randomize the count of chars for each category as well although with the 8 char password length and 4 categories that is not a wide range (you should increase your password length). Random shuffle algorithm from this answer.

    const string upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    const string lower = "abcdefghijklmnopqrstuvwxyz";
    const string caractere = "!-_*+&$";
    const string number = "0123456789";

    static string[] all = new [] { upper,lower,caractere,number };

    const int PwdLength = 8;
    const int CharCategories = 4;
    const int CharCatLimit = PwdLength / CharCategories;

    static void Main(string[] _)
    {
        string[] randomPwds = Enumerable.Range(0, 10).Select(_ => GetRandomPwd()).ToArray();

        foreach (var pwd in randomPwds)
        {
            bool good = pwd.Any(x => upper.Contains(x)) &&
                        pwd.Any(x => lower.Contains(x)) &&
                        pwd.Any(x => caractere.Contains(x)) &&
                        pwd.Any(x => number.Contains(x));

            Console.WriteLine($"{pwd} => has all required chars: {good}");
        }
    }

    static string GetRandomPwd()
    {
        Random rand = new Random();

        var password = GetRandCountRanCharsFromStr(upper, CharCatLimit, rand)
                        .Concat(GetRandCountRanCharsFromStr(lower, CharCatLimit, rand))
                        .Concat(GetRandCountRanCharsFromStr(caractere, CharCatLimit, rand))
                        .Concat(GetRandCountRanCharsFromStr(number, CharCatLimit, rand)).ToArray();
        if (password.Length < PwdLength)
        {
            password = password.Concat(GetRandCharsFromStr(all[rand.Next(0, all.Length)], PwdLength - password.Length, rand))
                        .ToArray();
        }

        Shuffle(password, rand);

        return new string(password);
    }

    static void Shuffle(char[] source, Random rand)
    {
        int n = source.Length;

        while (n > 1)
        {
            --n;
            int k = rand.Next(n + 1);
            var temp = source[k];
            source[k] = source[n];
            source[n] = temp;
        }
    }

    static IEnumerable<char> GetRandCountRanCharsFromStr(string source, int count, Random rand)
    {
        int randLimit = rand.Next(1, count);
        for (int i = 0; i < randLimit; ++i)
        {
            yield return source[rand.Next(0, source.Length)];
        }
    }

    static IEnumerable<char> GetRandCharsFromStr(string source, int count, Random rand)
    {
        for (int i = 0; i < count; ++i)
            yield return source[rand.Next(0, source.Length)];
    }
MikeJ
  • 1,299
  • 7
  • 10
1

I would use an array of string to hold the different categories with their values:

string[] categories = {
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
    "abcdefghijklmnopqrstuvwxyz",
    "!-_*+&$",
    "0123456789" };

Use a List<char> to hold the selected chars for your password:

List<char> chars = new List<char>();

This will make it easy to iterate over the categories and pick one char from each one, adding them to the List. Afterwards you can fill the list with random categories/chars until you reach the desired length. Finally, shuffle the List and return the password:

private static Random rand = new Random();

public static string RandomPassword(int length = 8)
{
    string[] categories = {
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
        "abcdefghijklmnopqrstuvwxyz",
        "!-_*+&$",
        "0123456789" };

    List<char> chars = new List<char>(length);

    // add one char from each category
    foreach(string cat in categories)
    {
        chars.Add(cat[rand.Next(cat.Length)]);
    }

    // add random chars from any category until we hit the length
    string all = string.Concat(categories);            
    while (chars.Count < length)
    {
        chars.Add(all[rand.Next(all.Length)]);
    }

    // shuffle and return our password
    var shuffled = chars.OrderBy(c => rand.NextDouble()).ToArray();
    return new string(shuffled);
}
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
-1

You need to test that all your conditions are met by your password generator:

while (
    !(
        password.IndexOfAny(lettre.ToCharArray()) != -1
     && password.IndexOfAny(caractere.ToCharArray()) != -1
     && password.IndexOfAny(number.ToCharArray()) != -1
     && password.Length < MINIMUM
    )
)
        {
            // On ajoute un caractère parmi tous les caractères autorisés
            password += ensemble[rand.Next(0, ensemble.Length)];
        }

If you need to ensure lower and upper case you will need to make then into separate strings.

adrianwadey
  • 1,719
  • 2
  • 11
  • 17