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
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
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
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
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With