The case of GeneratePassword() and special characters

(You can find a German language version of this post here )

Sometimes you might need to quickly generate a complex passwords in a script or an application. Of course, there are tons of functions available on the Web, but isn’t there something out of the box? Turns out, there is: [System.Web.Security.Membership]::GeneratePassword() to the rescue!

At first, the result of

Add-Type -AssemblyName System.Web
$pwlen = 12
$nachars = 4
[System.Web.Security.Membership]::GeneratePassword($pwlen, $nachars)

looks promising. But if you call the method often, you’ll start noticing that there are sometimes more non-alphanumeric characters in the generated string than have been specified in the second parameter. WTF? Let’s investigate.

According to the documentation linked above, the password length (first argument) may vary from 1 to 128. The second argument, which is the required count of non-alpahnumeric characters, can take any integer value from 0 up to the password length requested. So we can iterate through all possible combination and see how the non-alphanumeric character count behaves in each.

For this, let’s generate 1000 passwords for every combination and count the cases where the actual non-alphanumeric character count differs from the requested. In the end, we get a metric that can vary from 0 to 1, closer to zero is better. Here’s the script (not optimized for performance in any way):

Add-Type -AssemblyName System.Web
function Count-NonAlphaNum {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)][string]$InString
    )
    return (Select-String '[^A-Za-z0-9]' -Input $InString -AllMatches).Matches.Count
}
$outfile = "c:\temp\pwd-nonalpha.csv"
$nloops = 1000
$maxplen = 128
'"PwdLen";"{0}"' -f ((0..$maxplen)-join '";"') | Set-Content $outfile -Force
for ($plen = 1; $plen -le $maxplen; $plen++) {
    $line = @($plen)
    for ($nnan = 0; $nnan -le $plen; $nnan ++) {
        $ndiff = 0
        for ($i = 1;$i -le $nloops; $i++) {
            $pwd = [System.Web.Security.Membership]::GeneratePassword($plen,$nnan)
            $nnac = Count-NonAlphaNum -InString $pwd
            if ($nnac -ne $nnan) { $ndiff++ }
        }
        $line += [Math]::Round($ndiff / $nloops,2)
    }
    for ($nnan = $plen + 1; $nnan -le $maxplen; $nnan++) {
        $line += ''
    }
    '"{0}"' -f ($line -join '";"') | Add-Content $outfile
}

The results of a sample run can be downloaded here: pwd-nonalpha . In short, the more non-alphanumeric chars you request (relative to the total length), the better your chance to actually get that number delivered. Colourised for visualisation purposes, my sample run looks like this:

So not very encouraging if you’re only required to include one or two special chars in your random passwords – you’ll most certainly end up with more than you request!

Happy password generating!