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.