Visual Basic Password Generator

This doesn't rely on UI controls, nor does it use the legacy pre-NET VB functions. Instead, variables are passed to the generator so it is UI independent. It also does not use As Object because strings are strings.

<Flags>
Private Enum PasswordParts
    Upper = 1
    Lower = 2
    Numerals = 4
    All = 7
End Enum

Private Shared RNG As New Random()
Private Shared Numerals As String = "0123456789"
Private Shared Upper As String = "ABCDEFGHIJKLMNPQRSTUVWXYZYZ"
Private Shared Lower As String = "abcdefghijkmnopqrstuvwxyzyz"

Private Function GeneratePassword(length As Int32, 
             Optional pwparts As PasswordParts = PasswordParts.All) As String
    Dim PWCharPool As String = ""
    Dim PW As New List(Of String)()

    ' check the requested length
    If length < 6 Then length = 6
    If length > 20 Then length = 20

    ' build the pool and add the first of required characters
    ' for assure minimum conformance
    If pwparts.HasFlag(PasswordParts.Lower) Then
        PW.Add(Lower(RNG.Next(0, Lower.Length)))
        PWCharPool &= Lower
    End If
    If pwparts.HasFlag(PasswordParts.Upper) Then
        PW.Add(Upper(RNG.Next(0, Upper.Length)))
        PWCharPool &= Upper
    End If
    If pwparts.HasFlag(PasswordParts.Numerals) Then
        PW.Add(Numerals(RNG.Next(0, Numerals.Length)))
        PWCharPool &= Numerals
    End If

    ' pick the rest of the elements
    For n As Int32 = PW.Count To length - 1
        PW.Add(PWCharPool(RNG.Next(0, PWCharPool.Length)))
    Next

    ' shuffle the result so that the
    ' first 1-3 chars are not predictable
    Shuffle(PW, RNG)
    ' create a string from the result
    Return String.Join("", PW)
End Function
  • Note that there is no lower case L or upper case O. Users will confuse these with 0 and 1, so either remove them or the 0 and 1 (or both)
  • If the requirements for at least 1 upper, lower and numeral are hard, then there is no real need for the If statements (not blocks): just always pick one from each list before you add them to the pool. You also would not need the Enum or param. I left them based on the code in the question
    • Note that you should add code to handle when they click no checkboxes before calling this method.
  • A StringBuilder could be used in place of the List(of String), but the strings are so short that it really doesn't save any time until you run it in a loop many, many times and even then the time saved is minuscule.

Testing it:

Dim enumValues = [Enum].GetValues(GetType(PasswordParts)).Cast(Of Int32)()

For n As Int32 = 1 To 1000
    Dim style = CType(enumValues(RNG.Next(0, 4)), PasswordParts)
    Dim pwLen = RNG.Next(6, 21)

    Dim PW = GeneratePassword(pwLen, style)

    ' get riled up if the length does not match
    Debug.Assert(PW.Length = pwLen)
    Console.WriteLine("style: {0}  pw: '{1}' (len={2})", style, PW, PW.Length)
Next

Sample output:

style: Upper pw: 'QFHGPLIEEYPRP' (len=13)
style: All pw: 'Z9Y3CoW' (len=7)
style: Lower pw: 'tyghanjzudhhorfmvjr' (len=19)
style: All pw: 'XyY3q10N6S' (len=10)
style: Upper pw: 'IOZGTTQTPCYLKGEFRZ' (len=18)
style: All pw: '7t5CNMUM0GdWb' (len=13)
style: Upper pw: 'YIFXHRKEICOHXEUX' (len=16)

Then, the helper to shuffle (which is somewhat optional):

' Standared FY shuffle for List(Of T)
Public Sub Shuffle(Of T)(items As List(Of T), rnd As Random)
    Dim temp As T
    Dim j As Int32

    For i As Int32 = items.Count - 1 To 0 Step -1
        ' Pick an item for position i.
        j = rnd.Next(i + 1)

        ' Swap them.
        temp = items(i)
        items(i) = items(j)
        items(j) = temp
    Next i
End Sub

The big problem is that no one can remember such passwords, so they get taped to the monitor or bottom of the keyboard. A better method is to let the user pick the PW and just verify that it conforms to whatever rules by checking the string they choose for length and content. This would allow my~MuffyDoodle123 or some such that the user can remember. Perhaps force them to change it periodically and maybe add a TrashCan to store old PWs by user so they cannot reuse the same one within 6 or 9 months.

A way to auto-generate memorable PWs is using words. Create a list of several thousand adjectives and adverbs and another list of a few thousand nouns (easier than it sounds - there are online generators), you can create alliterative combinations plus a special character:

Rancid3Rain
Withered$Whisper
Creamy/Cotton
Enduring/Empire
Maximum7Mist
Greasy/Glory
Vaporized_Vision
Toxic!Tears
Angry!Abacus

The "hard" part is to use a centralized list and discard words as they are used so there can be no repeated words in generated passwords. Only when one of the queues is getting low do you start over.