If you need help building a random password, you should check out pctools.com random password generator. It’s great, it gives you a lot of options and you can have it generate a list of 50 passwords with their callsign spellings in seconds. I’ve found the callsign spelling to be very helpful for remembering and recognizing all the characters in a new password I generate. I liked this solution so much, I decided port this concept over to C# with a set of helpers. This can be used anywhere you want to generate a password, I am currently using it in a ASP.NET LOB app to suggest and show better passwords options.

Generating Random Passwords

using System;
using System.Collections.Generic;
using System.Linq;

public static class StringHelper
{
    // Shared Static Random Generator
    private static readonly Random CommonRandom = new Random();

    public static string GenerateRandomPassword(int passwordLength, bool canRepeatCharacters = false)
    {
        char[] chars = "$%#@!*abcdefghijklmnopqrstuvwxyz1234567890?;:ABCDEFGHIJKLMNOPQRSTUVWXYZ^&".ToCharArray();

        string randomPassword = string.Empty;

        for (int l = 0; l < passwordLength; l++)
        {
            int x = CommonRandom.Next(1, chars.Length);

            if (canRepeatCharacters || !randomPassword.ToCharArray().Any(ch => ch == chars[x]))
                randomPassword += chars[x].ToString();
            else
                l--;
        }

        return randomPassword;
    }

    public static List<string> GenerateRandomPasswords(int quantity, int passwordLength = 8)
    {
        List<string> passwords = new List<string>();

        for (int i = 0; i < quantity; i++)
        {
            passwords.Add(GenerateRandomPassword(passwordLength));
        }

        return passwords;
    }
}

There are a few options on the generator, like not repeating a character and configuring the password length. In addition to the main method, I also have a helper that also returns a list of multiple passwords, to return in a list to give your users options.

Important note, Random() is not very random when your making quick consecutive calls. If you want to call Random() in a loop, you should move your instance of the Random() class to the outside to prevent duplicate seeds, which will result in duplicate passwords.

    public static string GetCallsignSpelling(string password)
    {
        if (string.IsNullOrEmpty(password))
            return string.Empty;

        Dictionary<char, string> callsigns = new Dictionary<char, string>()
            {
                {'$',"Dollar Symbol"},
                {'%',"Percent Symbol"},
                {'#',"Number Symbol"},
                {'@',"At Symbol"},
                {'!',"Exclamation Symbol"},
                {'*',"Asterisk Symbol"},
                {'a',"alpha"},
                {'b',"bravo"},
                {'c',"charlie"},
                {'d',"delta"},
                {'e',"echo"},
                {'f',"foxtrot"},
                {'g',"golf"},
                {'h',"hotel"},
                {'i',"india"},
                {'j',"juliet"},
                {'k',"kilo"},
                {'l',"lima"},
                {'m',"mike"},
                {'n',"november"},
                {'o',"oscar"},
                {'p',"papa"},
                {'q',"quebec"},
                {'r',"romeo"},
                {'s',"sierra"},
                {'t',"tango"},
                {'u',"uniform"},
                {'v',"victor"},
                {'w',"whiskey"},
                {'x',"xray"},
                {'y',"yankee"},
                {'z',"zulu"},
                {'1',"One"},
                {'2',"Two"},
                {'3',"Three"},
                {'4',"Four"},
                {'5',"Five"},
                {'6',"Six"},
                {'7',"Seven"},
                {'8',"Eight"},
                {'9',"Nine"},
                {'0',"Zero"},
                {'?',"Question Symbol"},
                {';',"SemiColon Symbol"},
                {':',"Colon Symbol"},
                {'A',"ALPHA"},
                {'B',"BRAVO"},
                {'C',"CHARLIE"},
                {'D',"DELTA"},
                {'E',"ECHO"},
                {'F',"FOXTROT"},
                {'G',"GOLF"},
                {'H',"HOTEL"},
                {'I',"INDIA"},
                {'J',"JULIET"},
                {'K',"KILO"},
                {'L',"LIMA"},
                {'M',"MIKE"},
                {'N',"NOVEMBER"},
                {'O',"OSCAR"},
                {'P',"PAPA"},
                {'Q',"QUEBEC"},
                {'R',"ROMEO"},
                {'S',"SIERRA"},
                {'T',"TANGO"},
                {'U',"UNIFORM"},
                {'V',"VICTOR"},
                {'W',"WHISKEY"},
                {'X',"XRAY"},
                {'Y',"YANKEE"},
                {'Z',"ZULU"},
                {'^',"Caret Symbol"},
                {'&',"Ampersand Symbol"}
            };

        char[] wordCharacters = password.ToCharArray();

        string callsignSpelling =
            wordCharacters.Aggregate(string.Empty,
                                         (current, passwordCharacter) =>
                                         current + (callsigns[passwordCharacter] + " - ")).TrimEnd(' ', '-');

        return callsignSpelling;
    }

The spelling is done using a Key/Value dictionary, and iterating over each character of the password one-by-one.

The result of using these two helpers is below.