Tuesday, February 7, 2012

PowerShell: Create-ComplexPassword v2

Description: Generates complex passwords based off of the criteria given. Able to create passwords of any length and any complexity given the following standard options:

  • length
  • lowercase
  • uppercase
  • numbers
  • special characters

Acknowledgements: The script should run efficiently no matter how complex of a password is requested.  The most visible limitation is that the script could take a second or two to complete an abnormally long password ( > 2000 chars) because of the way the script generates and shuffles the characters.

Logic Overview:

  • Determine supplied options
  • Build out all the requirements specified sequentially in a string.
  • Loop ($PasswordLength * 5) times to shuffle each character in the string to guarantee the password is randomized.  As an example, if the password requested is 20 characters long, the password's characters are shuffled 100 (20*5) times to produce a "randomizing" effect.

Example Syntax:

Create-ComplexPassword -PasswordLength 20 -LowerAlphas 2 -UpperAlphas 2 -Numbers 2 -SpecialCharacters 2
Create-ComplexPassword -PasswordLength 30 -SpecialCharacters 20
Create-ComplexPassword -PasswordLength 25 -LowerAlphas 2 -UpperAlphas 2

Function Syntax:

Function Create-ComplexPassword {
#  http://msdn.microsoft.com/en-us/library/60ecse8t(VS.80).aspx
Param (
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -gt 0})] $PasswordLength = 8,
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -ge 0})] $LowerAlphas = 0,
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -ge 0})] $UpperAlphas = 0,
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -ge 0})] $Numbers = 0,
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -ge 0})] $SpecialCharacters = 0
    )
  
    If (($LowerAlphas + $UpperAlphas + $Numbers + $SpecialCharacters) -gt $PasswordLength) { 
        Throw "The specified sum of the number of upper, lower, numeric, and special characters cannot be greater than the desired length of the password."
    }
    
    # Creates random object with seed to guarantee uniqueness
 $objRnd = New-Object System.Random((Get-Random))
    
    $strPassword = $Null
    $arrSpecials = @(33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,58,59,60,61,62,63,64,91,92,93,94,95,96,123,124,125,126)
    
    # Add the specified number of lowercase values to the string
    For ($i=0; $i -lt $LowerAlphas; $i++) {
        $strPassword += [char]$objRnd.next(97,122)
    }

 # Add the specified number of uppercase values to the string
    For ($i=0; $i -lt $UpperAlphas; $i++) {
        $strPassword += [char]$objRnd.next(65,90)
    }
    
    # Add the specified number of number values to the string
    For ($i=0; $i -lt $Numbers; $i++) {
        $strPassword += [char]$objRnd.next(48,57)
    }
    
    # Add the specified number of special character values to the string
    For ($i=0; $i -lt $SpecialCharacters; $i++) {
        $strPassword += [char]$arrSpecials[$objRnd.next(0,$arrSpecials.length)]
    }
    
    # Add the remaining number of random characters for those not specified to fill the rest of the length
    For ($i = $strPassword.length; $i -lt $PasswordLength; $i++) {
  $strPassword += [char]$objRnd.next(33,126)
    }
 
 For ($i = 0; $i -lt ($PasswordLength * 5); $i++) {
  $pos = $objRnd.Next(0, ($strPassword.length - 1))
  
  $lpart = $strPassword.Substring(0,$pos)
  $hpart = $strPassword.Substring($pos + 1, ($strPassword.Length - ($pos + 1)))

  $strPassword = "$lpart$hpart" + $strPassword[$pos]
 }

 $strPassword
}

2 comments:

  1. I've been using this for a while, and just today I've noticed that it has generated the same password three times in the last couple of weeks. Any idea why this might be?

    I'm using Create-ComplexPassword -PasswordLength 8 -LowerAlphas 6 -UpperAlphas 1 -Numbers 1 -SpecialCharacters 0

    ReplyDelete
  2. You are correct. The randomization function created passwords using a millisecond counter for seed-value but the function can create 5-12ish passwords every millisecond so when run to generate a large number of passwords in a loop, there were duplicates. I updated the code to use a random Int32 value instead (approx. 1-2Billion) for the seed value. After making this change, I was unable to reproduce a duplicate in 100,000 iterations. Please let me know if you find different.

    ReplyDelete