Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Visual Basic Password Generator

I have been tasked with creating a visual basic random password generator. I have come up with the below which works however its a bit crude with its password criteria. I would like each password it generates to have at least 1 number, uppercase and lowercase in it. However how I have coded this it will generate a random combination which quite often results in one of the criteria being missed out.

I have had a play myself and I was going to have three strings, one with uppercase, one with lower case and a third with numbers. Once it has one of each it will then generate the rest of the password using my code. This doesn't sound very clean and I have been having problems doing this.

Can anyone help point me in the right direction or assist with the code below. Passwords must be between 6 and 20 characters in length.

Thanks in advance

Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        TextBox2.Text = GenerateCode()
    End Sub

    Private Function GenerateCode() As Object
        Dim intRnd As Object
        Dim intStep As Object
        Dim strName As Object
        Dim intNameLength As Object
        Dim intLength As Object
        Dim strInputString As Object
        strInputString = "1234567890ABCDEFGHIJKLMEOPQRSTUWXYZabcdefghijklmnopqrstuvwxyz"

        intLength = Len(strInputString)

        Randomize()


        strName = ""

        'Check for valid numeric entry
        If Integer.TryParse(TextBox1.Text, intNameLength) And intNameLength >= 6 And intNameLength <= 20 Then
            For intStep = 1 To intNameLength
                intRnd = Int((intLength * Rnd()) + 1)

                strName = strName & Mid(strInputString, intRnd, 1)

            Next
            GenerateCode = strName
        Else
            TextBox1.Text =("Please enter a valid password length")
        End If

    End Function
    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click

        My.Computer.Clipboard.SetText(TextBox2.Text)

    End Sub

    Private Sub TextBox1_Enter(sender As Object, e As EventArgs) Handles TextBox1.Enter
        TextBox1.Text = ""
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    End Sub
End Class

========== UPDATE ==========

Right I have changed my code a little so I have now got three strings with the different characters in, 1 for uppers, one for lowers and one for numbers.

This enables me to have a checkbox functionality in the form which is a plus. I have played around with the code and it now generates passwords depending on what I select with the checkboxes which as I say is awesome however I cant always guarantee that if I select, number, upper and lower I will get a password that contains all three, and sometimes the password will only contain numbers even though all three boxes are checked.

In guessing this is something to do with the fact I'm just asking it to generate random password using the characters I give it, and there is no verification that it has used all three of the options.

Any help on this would be awesome. I am trying and I'm not just posting and hoping someone will do the work for me. If someone can point me in the right direction would be great.

Here is my new code.

Public Function GenerateCode()
    Dim intRnd As Integer
    Dim intStep As Integer = Nothing
    Dim strname As String
    Dim intlength As Integer
    Dim strinputstring As String = ""
    Dim Numbers As String = "12345678901234567890"
    Dim Lower As String = "abcdefghijklmnopqrstuvwxyzyz"
    Dim Upper As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZYZ"
    Dim intnamelength As Integer = 1


    If CheckBox1.Checked Then strinputstring &= Lower
    If CheckBox2.Checked Then strinputstring &= Numbers
    If CheckBox3.Checked Then strinputstring &= Upper

    intlength = Len(strinputstring)

    Integer.TryParse(NumericUpDown1.Text, intnamelength)

    Randomize()

    strname = ""

    For inStep = 1 To intnamelength

        intRnd = Int(Rnd() * intlength) + 1

        strname = strname & Mid(strinputstring, intRnd, 1)

    Next

    Return strname

End Function
like image 951
Tim Hannah Avatar asked Jan 04 '17 08:01

Tim Hannah


2 Answers

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.

like image 100
Ňɏssa Pøngjǣrdenlarp Avatar answered Oct 27 '22 01:10

Ňɏssa Pøngjǣrdenlarp


Try:

Here is the basic idea. If you want upper and lower case letters, then add the lower case letters to the string s.

Sub Main()
    Dim s As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    Dim r As New Random
    Dim sb As New StringBuilder
    For i As Integer = 1 To 8
        Dim idx As Integer = r.Next(0, 35)
        sb.Append(s.Substring(idx, 1))
    Next
    Console.WriteLine(sb.ToString())
    Console.ReadKey()
End Sub

And this should replace all your old code

like image 42
TGamer Avatar answered Oct 27 '22 03:10

TGamer