I have written a simple module which generates passwords fulfilling my complexity requirements.
Module PasswordGenerator
Private ReadOnly _alphaChars As Char() = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToArray()
Private ReadOnly _digitChars As Char() = "0123456789".ToArray()
Private ReadOnly _nonAlphaNumChars As Char() = "_-.,:;!$%&/()[]=+#".ToArray()
Private ReadOnly _allPswChars As Char() = _alphaChars.Concat(_digitChars).Concat(_nonAlphaNumChars).ToArray()
Public Function GeneratePassword(length As Integer, minNonAlphaNum As Integer, minDigits As Integer) As String
Return GeneratePassword(length, minNonAlphaNum, minDigits, New Random(GetSeed()))
End Function
Public Function GeneratePassword(length As Integer, minNonAlphaNum As Integer, minDigits As Integer, rnd As Random) As String
Dim selectedChars As List(Of Char),
psw As String,
rndSelectedChar As Integer
If length < (minNonAlphaNum + minDigits) Then Throw New ArgumentException
selectedChars = New List(Of Char)(length)
selectedChars.AddRange(GetRandomChars(_nonAlphaNumChars, minNonAlphaNum, rnd))
selectedChars.AddRange(GetRandomChars(_digitChars, minDigits, rnd))
selectedChars.AddRange(GetRandomChars(_allPswChars, (length - minNonAlphaNum - minDigits), rnd))
psw = ""
For i As Integer = 1 To selectedChars.Count
rndSelectedChar = rnd.Next(0, selectedChars.Count)
psw &= selectedChars(rndSelectedChar)
selectedChars.RemoveAt(rndSelectedChar)
Next
Return psw
End Function
Private Function GetSeed() As Integer
Dim guidString As String
guidString = Guid.NewGuid().ToString("N").Substring(0, 5)
Return Int32.Parse(guidString, Globalization.NumberStyles.HexNumber)
End Function
Private Function GetRandomChars(charRange As Char(), count As Integer, rnd As Random) As List(Of Char)
Dim retVal As New List(Of Char),
selectedPos As Integer
If count < 1 Then Return retVal
For i As Integer = 1 To count
selectedPos = rnd.Next(0, charRange.Length)
retVal.Add(charRange(selectedPos))
Next
Return retVal
End Function
End Module
If I use this code without my GetSeed()
function, I will get the same password many times if I generate passwords in a loop. By using the guid as random seed I can ensure not getting equal passwords, because the NewGuid
function ensures not generating same guids. The system provided function to generate a guid ensures not getting same guids but is not made for generating random values in crypto environments because it is some kind of predictable (or more predictable than other functions - not predictable is not possible).
Don't use Random
at all for something as sensitive as this. Use RNGCryptoServiceProvider
instead, which provides reasonably secure seeding.
It's not as simple an API to use as Random
- you basically just get bytes, potentially guaranteed to be non-zero - but it's a more appropriate API.
See more on this question at Stackoverflow