Generating strong random passwords in PowerShell


The sample in this posting demonstrates how to generate a random password using a cryptographically strong random number generator. The sample will generate either a password as a secure string or as a standard string. It is best practice to use secure strings for passwords but for demonstration purposes they are rather dull because by nature you cannot easily view their contents. The password generated can contain a mix of symbols and alphanumeric characters. The random password is generated using the RNGCryptoServiceProvider in the System.Security.Cryptography namespace.

Definition of a strong password

  • Has at least 12 characters
  • Has at least one number
  • Has at least one uppercase character
  • Has at least one lower case character
  • Contains symbols, for example !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

In the sample below, the function Create-RandomPassword can be used to create a strong random password.

function Create-RandomPassword
(
   [int]
$minLength = 12,
   [int]
$maxLength = 20,
   [bool]
$useSymbols = $true,
   [bool]
$asSecureString = $true
)
{
   [System.Security.Cryptography.RNGCryptoServiceProvider]
$random =
      new-object System.Security.Cryptography.RNGCryptoServiceProvider
   
  
# Get an array of all characters that can be used in the password
   [string] $choice = Get-CharacterChoice -useSymbols $useSymbols
   
  
$randomPassword = $null
   if ((
$minLength -le $maxLength) -and
      (
$minLength -ge 6))
   {
     
# Allocate a byte array of dimension 1
      $randomNumber = new-object byte[] 1
      if (
$minLength -eq $maxLength)
      {
         [int]
$length = $minLength
      }
      else
      {
        
# Calculate a random length between minLength and maxLength
         $random.GetBytes($randomNumber)
         [int]
$length = $minLength + $randomNumber[0] %
                     (
$maxLength - $minLength + 1)
      }
 
     
# Allocate a byte array of dimension $length
      $randomSequence = new-object byte[] $length
     
$hasUCase = $hasLCase = $hasNum = $false
      while(!
$hasUCase -or !$hasLCase -or !$hasNum)
      {
        
# Generate random sequence of bytes
         $random.GetBytes($randomSequence)
         
        
# Ensure that there is at least one number, uppercase
         # character and lowercase character in the sequence.
         $hasUCase = $hasLCase = $hasNum = $false
         foreach(
$b in $randomSequence)
         {
            [char]
$char = $choice[$b % $choice.Length]
            if (
$char -ge 'A' -and $char -le 'Z')
            {
              
$hasUCase = $true
            }
            
            if (
$char -ge 'a' -and $char -le 'z')
            {
              
$hasLCase = $true
            }
            
            if (
$char -ge '0' -and $char -le '9')
            {
              
$hasNum = $true
            }
         }
      }
 
      if (
$asSecureString)
      {
        
$randomPassword = new-object System.Security.SecureString
      }
      else
      {
         [string]
$randomPassword = ''
      }
      
     
# Assign the password from the sequence of random bytes
      foreach($b in $randomSequence)
      {
         [char]
$char = $choice[$b % $choice.Length]
         if (
$asSecureString)
         {
           
$randomPassword.AppendChar($char)
         }
         else
         {
           
$randomPassword += $char
         }
      }
   }
   
   return
$randomPassword
}

# Outputs an array of all the characters that the generated password
# can be made up of.
function Get-CharacterChoice
(
   [bool]
$useSymbols = $true
)
{
   if (
$useSymbols)
   {
      [string]
$choice = '!"#$%&''()*+,-./'
   }
   else
   {
      [string]
$choice = ''
   }
   
  
$choice += '0123456789'
   if (
$useSymbols)
   {
     
$choice += ':;<=>?@'
   }
   
  
$choice += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
   if (
$useSymbols)
   {
     
$choice += '[\]^_`'
   }
   
  
$choice += 'abcdefghijklmnopqrstuvwxyz'
   if (
$useSymbols)
   {
     
$choice += '{|}~'
   }
   
   return
$choice
}

Parameters

The parameters that can be passed into function Create-RandomPassword are given below. All of the parameters are optional with default values

Parameter Default Description
minLength 12 The minimum length required for the generated password
maxLength 20 The maximum length required for the generated password. The actual length will be a random number between minLength and maxLength
useSymbols $true If $true then the password will be generated to contain symbols, otherwise the password will not contain symbols. The symbol characters are defined as
!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
asSecureString $true If $true then the return value of this function will be a secure string; otherwise the return value will be a normal string that will hold the password in plain text

Output

The return value of the function will either be a string or a secure string holding the generated password depending on the input parameter asSecureString. The length of the password will be a random number between minLength and maxLength. To generate a password with a fixed length set minLength = maxLength. The function will return $null if it cannot produce a password.

Examples

I have taken the sample code above and pasted it into a PowerShell script file named Sample.code.ps1 and used this script file to generate the results below

Example 1

Creates a password as a plain text string with a length between 12 and 20. Symbol characters are included in the generated password.

PS C:\Blog> . .\Sample.code.ps1
PS C:\Blog> $password = Create-RandomPassword -asSecureString $false
PS C:\Blog> $password
Wi_#c*F8hJsG\zV5G6.
PS C:\Blog> $password.Length
19
PS C:\Blog>

Example 2

Creates a password as a secure string with a length between 12 and 20. Symbol characters are included in the generated password.

PS C:\Blog> . .\Sample.code.ps1
PS C:\Blog> $password = Create-RandomPassword
PS C:\Blog> $password
System.Security.SecureString
PS C:\Blog> $password.Length
20
PS C:\Blog>

Example 3

Creates a password as a plain text string with a fixed length equal to 15. Symbol characters are not included in the generated password.

PS C:\Blog> . .\Sample.code.ps1
PS C:\Blog> $password = Create-RandomPassword -asSecureString $false -useSymbols $false -minLength 15 -maxLength 15
PS C:\Blog> $password
Z1LLue73A8XbuRq
PS C:\Blog> $password.Length
15
PS C:\Blog>

One Response to “Generating strong random passwords in PowerShell”

  1. Thanks for the post helped me a lot.

    It isn’t a problem really but you are introducing a slight bias on your selection because of this operation “[char]$char = $choice[$b % $choice.Length] ” (by chance or design only when using symbols).
    When using symbols you can choose from a total of 94 characters and you have 256 possible byte values. 256%94=68 and 256/94=4 therefore the first 68 characters in your $choice array have a probability 5/256 of occurring at any given iteration whereas the remaining characters have a probability of 4/256.
    I’m not sure but I think the solution would be to ignore any random byte value greater or equal to (256/$choice.length)*$choice.length.
    But of course this is nitpicking.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: