Your basic ITPro blog... What's going on at work, what I'm interested in.

Thursday, October 23, 2008

Generating Passwords with Powershell

UPDATES AT BOTTOM OF POST!

I wanted a way to generate random passwords using Powershell. Research for this was a lot of fun, and I ran across some great resources.

--------------------------------------------

http://dmitrysotnikov.wordpress.com/2007/07/18/generate-random-password-with-powershell/ 
- Love the comment referring to using ”System.Web”

http://www.peterprovost.org/blog/post/Quick-n-Dirty-PowerShell-Password-Generator.aspx

http://www.hanselman.com/blog/DictionaryPasswordGeneratorInPowershell.aspx 
- Interesting variation, while promoting passphrases over passwords (a great discussion to have, no doubt!)

http://www.terminal23.net/2007/06/powershell_random_password_gen.html 
- Cool solution to this

http://www.leadfollowmove.com/archives/powershell/powershell-less-code-same-result

--------------------------------------------

I wanted a couple of specifics regarding my tool's functionality.

  • The option of including/excluding subsets of characters (uppercase, lowercase, numbers, special characters)
  • The option of setting the password length
  • The results had to include at least one character from each subset desired
  • It had to be easily usable in the pipeline

With that all in mind, I came up with the following function. This is the format that I have settled on (more or less) for my functions. I don't know if it's the best way to do things (probably not), but it seems to be working for me at this time.

I am glad to be able to add my $0.02 to the conversation.

#######################################################################
# FUNCTION NAME: New-Password
#   
# See USAGE() function for docs.
#
# WRITTEN BY: Derek Mangrum
#
# REVISION HISTORY:
#     2008-10-23 : Initial version
#######################################################################
function New-Password
{
    param 
    (
        [int]$length,
        [switch]$lowerCase,
        [switch]$upperCase,
        [switch]$numbers,
        [switch]$specialChars
    )

    BEGIN
    {
        # Usage Instructions
        function Usage() 
        {
            Write-Host ''
            Write-Host 'FUNCTION NAME: New-Password' -ForegroundColor White
            Write-Host ''
            Write-Host 'USAGE'
            Write-Host '    New-Password -length 10 -upperCase -lowerCase -numbers'
            Write-Host '    New-Password -length 10 -specialChars'
            Write-Host '    New-Password -le 10 -lo -u -n -s'
            Write-Host '    New-Password'
            Write-Host ''
            Write-Host 'DESCRIPTION:'
            Write-Host ' Generates a random password of a given length (-length parameter)'
            Write-Host ' comprised of at least one character from each subset provided'
            Write-Host ' as a switch parameter.'
            Write-Host ''
            Write-Host 'AVAILABLE SWITCHES:'
            Write-Host ' -lowerCase    : include all lower case letters'
            Write-Host ' -upperCase    : include all upper case letters'
            Write-Host ' -numbers      : include 0-9'
            Write-Host ' -specialChars : include the following- !@#$%^&*()_+-={}[]<>'
            Write-Host ''
            Write-Host 'REQUIREMENTS:'
            Write-Host ' You must provide the -length (four or greater) and at least one character switch'
            Write-Host ''
        }
        
        function generate_password
        {
            if ($lowerCase)    
            { 
                $charsToUse += $lCase
                $regexExp += "(?=.*[$lCase])"
            }
            if ($upperCase)        
            { 
                $charsToUse += $uCase 
                $regexExp += "(?=.*[$uCase])"
            }
            if ($numbers)
            { 
                $charsToUse += $nums 
                $regexExp += "(?=.*[$nums])"
            }
            if ($specialChars)    
            { 
                $charsToUse += $specChars
                $regexExp += "(?=.*[\W])"
            }
            
            $test = [regex]$regexExp
            $rnd = New-Object System.Random
            
            do 
            {
                $pw = $null
                for ($i = 0 ; $i -lt $length ; $i++)
                {
                    $pw += $charsToUse[($rnd.Next(0,$charsToUse.Length))]
                    Start-Sleep -milliseconds 20
                }
            }
            until ($pw -match $test)
            
            return $pw
        }

        # Displays help
        if (($Args[0] -eq "-?") -or ($Args[0] -eq "-help")) 
        {
            Usage
            break
        }
        else
        {
            $lCase = 'abcdefghijklmnopqrstuvwxyz'
            $uCase = $lCase.ToUpper()
            $nums = '1234567890'
            $specChars = '!@#$%^&*()_+-={}[]<>'
        }
    }
    
    PROCESS
    {
        if (($length -ge 4) -and ($lowerCase -or $upperCase -or $numbers -or $specialChars))
        {
            $newPassword = generate_password
        }
        else
        {
            Usage
            break
        }
        
        $newPassword
    }
    
    END
    {
    }
}

UPDATE:
I was talking with friends about this script and something about it I did not like. Namely, the need I had to pause briefly in my password-generation loop (the 'Start-Sleep -m 20'). They said that I needed to seed my System.Random object. Hmmm... OK. Then we talked about how to create a good seed value for this. Long story short, we landed on the following:

$seed = ([system.Guid]::NewGuid().GetHashCode())
$rnd = New-Object System.Random ($seed)

We create a $seed by calling the NewGuid().GetHashCode() method of System.Guid. Then we use this $seed to create our $rnd object. Also, we can then take out the pause. Passwords, as you can imagine, generate much more quickly now.

No comments:

Additional Info

My photo
email: support (AT) mangrumtech (DOT) com
mobile: 480-270-4332