Create cryptographically secure and pseudorandom data with PowerShell

From Svendsen Tech PowerShell Wiki
Jump to: navigation, search

The PowerShell cmdlet New-RandomData can be used to generate both pseudorandom and cryptographically secure random data. The default is writing one or more ASCII encoded text files with \r\n line endings, but you can also use it to generate passwords, keys, and random numbers to store in a variable, etc., with the -RandomChar and/or -StreamToSTDOUT parameters. I will demonstrate its use.

Should work with PowerShell version 2 and up. The testing has been done with PowerShell version 4.

New-RandomData-example-of-random-data.png



This image shows that it takes about 2.2 seconds to generate a 1 MiB large pseudorandom file on my (2009 vintage) system.

New-RandomData-pseudorandom-example-1MB-verbose.png

This image shows that it took almost exactly 24 seconds to generate a 1 MiB cryptographically secure file on my (2009 vintage) system, using the default random character set that has no overhead.

New-RandomData-cryptography-example-1MB-verbose.png

Download

New-RandomData.ps1.txt - right-click and download, rename, remember to unblock. Dot-source (. .\New-RandomData.ps1), and use the New-RandomData cmdlet.

RandomData.zip - Module version. Exports the function New-RandomData. Download, remember to unblock before extracting (Unblock-File in PSv3 and up), copy the directory containing the module files to a PowerShell module folder. See $Env:PSModulePath. Also published to the PowerShell gallery (see below).

  • 2017-01-21: Uploading a module version, 1.2, which is identical to the existing script, except it's been given the mandatory scaffolding to be a module. I published it to the PowerShell gallery for convenient access (see below). Adding the zip file to the wiki as well.
  • 2016-03-12: Small optimizations. Generating two less random characters per line when writing a file, where \r\n will occupy two bytes, which means retrieving fewer random cryptography numbers, or fewer pseudorandom numbers, plus less appending to the string builder.
    • Speed for a 1 MiB file of pseudorandom data went from about 2.2 to 1.5 seconds on my system. Cryptography difference was not really noticeable.
  • 2016-02-18: Changed the string manipulation to be done with System.Text.StringBuilder. Cut run time down to less than half for the 1 MiB pseudorandom test case - and about 40 % faster with -Cryptography specified for 1 MiB.

Earlier versions: File:New-RandomData.ps1.txt. Earlier module versions: File:RandomData.zip.

If you have Windows Management Framework 5 or higher (WMF 5 is available for Windows 7 and up), you can install my RandomData module from the PowerShell gallery, a Microsoft site and online repository for scripts.

To install with WMF 5 and up (to get the latest RandomData module version available), simply run this command (requires an internet connection):

Install-Module -Name RandomData

Generating a random 100-digit number

We specify a random char array containing 0-9, a size and line length of 100, and off we go. Add -StreamToSTDOUT and assign to a variable. I realize this example is a bit odd in retrospect. See the password generation below as well.

A convenient way to specify for instance “a-z” as a char array for either -RandomChar or -RandomCharExclude is using this syntax: -RandomChar ([char]’a’ .. [char]‘z’)

PS C:\dir> New-RandomData -Size 100 -LineLength 100 -Cryptography `
    -RandomChar ([char[]][string[]](0..9)) -Verbose
VERBOSE: Random char array: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
VERBOSE: Random char array size: 10
VERBOSE: Duplicating array of length 10 completely 25 times, to fit a byte-sized array as closely as possible.
VERBOSE: Random char array size after duplication: 250.
VERBOSE: Probability of generating an unusable cryptography number (overhead): 2.34 %.
VERBOSE: Wrote 'C:\dir\random_file-1.txt'. 02/16/2016 23:45:51.
VERBOSE: Had to get 101 cryptography numbers.

PS C:\dir> gc .\random_file-1.txt
64922890229615691206867542730396091467023299366934980519254582563151943335669369766529859002962524

PS C:\dir> gc .\random_file-1.txt | %{ $_.Length } # \r\n take up 2 bytes
98

PS C:\dir> ls .\random_file-1.txt | ft -AutoSize length, name

Length Name             
------ ----             
   100 random_file-1.txt

Generating a random password

There are simpler solutions for this, but New-RandomData can absolutely be used to generate random passwords (or keys), even cryptographically secure if you specify the -Cryptography parameter.

Use the -StreamToSTDOUT parameter and set the size and line length to the same, desired password length.

PS C:\> $RandomPassword = New-RandomData -Size 12 -LineLength 12 -StreamToSTDOUT

PS C:\> $RandomPassword
Fg7WWlKL0Wc1 

You can also specify which characters to use for the password via the -RandomChar parameter. A convenient way to specify for instance "a-z" and "0-9" as a char array for either -RandomChar or -RandomCharExclude is using the syntax "-RandomChar ([char]'a' .. [char]'z' + [char]'0' .. [char]'9')".

Cryptographically secure random data

The parameter -Cryptography turns on the feature that uses a cryptographically secure random number generator to index into the equally weighted, possibly repeated, as-close-as-possible-to-byte-sized random char array. This uses the .NET System.Security.Cryptography random number generator to generate random numbers to index into the array.

If you do not use a random characters array by which 256 is evenly divisible, there will be some overhead. The -Verbose parameter will (among other things) tell you how large the overhead in potentially wastefully generated unusable numbers is estimated to be, as a percentage.

The default set of random characters is a-z, A-Z, 0-9, "-", and "_". This is 64 characters which means it can be repeated four times to perfectly fit a byte-sized array, causing no overhead.

PS C:\> New-RandomData -Size 1024 -LineLength 64 -StreamToSTDOUT `
    -Cryptography -Verbose
VERBOSE: Random char array: a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, 
w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, 0, 1
, 2, 3, 4, 5, 6, 7, 8, 9, -, _
VERBOSE: Random char array size: 64
VERBOSE: Duplicating array of length 64 completely 4 times, to fit a byte-sized array as close
ly as possible.
VERBOSE: Random char array size after duplication: 256.
VERBOSE: Probability of generating an unusable cryptography number (overhead): 0.00 %.
QJbSxK6GToqchRkMc9wkaIb35nuLTBMZpMX9OpI1QTGT5UkRJ4p8_Bbzp6braBcT
5dqfUcH8Q6_Vow2dqDNOSlluyxaoY5Pa4mdTMidJdFe1ttUNNxF7OtCypSJPksPt
rojkcVpCnWViwZVz3Ca6kTfx8Mh-Gazissr6OD9FGxXZFDoRrByXIbbk8gqWcabS
GjASgnvfjAo1fY3hP1HddRuR_Y8lKU_0T9Iqwk1EjvIhv9NwFPD5SiEYYziMTmds
_4VMXxdHNjmvdCwyaF-V2aE2n4sp01UOlsI3iJWD_aR0vnWn1a6NGbOhYudLK1rc
jNslYVyeXdn2uvzkPWSRLD2Xm1qhrb-MK_gZme0qNIU8K-zY6c0aFWDhRNyXYv7F
43RcFy7RZBZjnNZaFUz-oji7wC3_mQkvVySR1s8RDyTfaLZDJqulTvK0butX1d1D
MHMncSkaQwtO2zIS2PIC5KRQvQWLwW_ReqGUEfGfzDtshfGIhC7Th6WBojT6Redc
kH00UPZ8ILeZOtbHtwEzXwjdQGRF8hdlDFd2YFvcvpXGGEe6KyRXgTo8kKfogO6a
XMUGcTJJW5aILtACJ2nF1M55YU_E86Pd9ETm0sSqJJce7zMSceJU5E5NOKE8ayZB
KheXnhV9vqr2-cIG5W28Z2HwLzJtRGdbNsxTFNwEZKrE1zgjJHRozp0LT9ZffLsQ
sojF9ZlWzOrTyq9BVbS23zEFWBUot1YJ4tdB--oOhJNmnvbdlJ92eTeMiMTLOVBJ
82Ue2jEcyBv_-OcTsnSeoyRWwAx68cm3mJ0UwIptv7ZHVXGK2alHIb6PVdcAnQCP
qnYaucoDEFjlodGmO0zKo9B9AsvjuWdGQbJzlytefm0HmLVs1R31T5kaLB2vlIY8
qxoz5Q0awtI_18AKxczuCRVUvzHGi97wCyonx-ajT__ouEh2J9K08ZhhdJRNn78C
E_s1m6hL7lG6CX5FpJD1kw6KAvIvGSPTEVLKeu4opux6uro0psKxdk-8y_VSG4R7
VERBOSE: Had to get 1024 cryptography numbers.

PS C:\>  

Generating 1000 random data files

The -Count parameter is simple enough and specifies the number of files to create, with zero-padded numbers added. If you provide the -RandomFileNameGUID switch parameter, a GUID will be appended to the file base name, which by default is “random_file-“. The extension (default .txt) is thrown at the end.

It took about 17-18 seconds to generate 1000 files of size 8192 bytes (8 KiB of data). Still on my desktop computer from 2009.

PS C:\temp\dir> Get-ChildItem

PS C:\temp\dir> . C:\Dropbox\PowerShell\New-RandomFile\New-RandomData.ps1

PS C:\temp\dir> Measure-Command { New-RandomData -Path (Get-Location) -Count 1000 `
  -Size 8192 -LineLength 256 } | % { 'Total seconds: ' + $_.TotalSeconds } 
Total seconds: 17.8322021

PS C:\temp\dir> (Get-ChildItem).count 
1000

PS C:\temp\dir> ls | select -first 3

    Directory: C:\temp\dir


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2016-03-26     01:52       8192 random_file-0001.txt
-a---        2016-03-26     01:52       8192 random_file-0002.txt
-a---        2016-03-26     01:52       8192 random_file-0003.txt


PS C:\temp\dir> ls | select -last 3

    Directory: C:\temp\dir


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2016-03-26     01:53       8192 random_file-0998.txt
-a---        2016-03-26     01:53       8192 random_file-0999.txt
-a---        2016-03-26     01:53       8192 random_file-1000.txt

PS C:\temp\dir> 

If we look at a random file and its random data (the last -replace just splits it into 64 characters per line), we can see that the last line, the end of the 256 characters long line, has 2 bytes less than the specified line length of 256 to make room for a carriage return and a newline in the file. With -StreamToSTDOUT, this does not happen.

PS C:\temp\dir> gc .\random_file-0888.txt | select -first 1 | 
    %{ $_ -replace '(.{64}(?!$))', "`${1}`n" }
Zop6BY37rt9YJxpl_eaHC0AMNoFNKyO4V1hNO0UDbdbmgVL9N4Me_j5_ZPsnspw4
QedUpOIlwIFyYnG4PIaRXK5M7iZ_HHkYSjHbS2U_vAUCPGmLiAJWpRhglCYg0JQ0
yTSPSElZ0avw2C8B8n2h9AIbbB1pdNkXW5e8ZJ5QoW7ovWqVGxkMTI39kQNrP7Vm
2JL8x_Nyum5nFuSrkgVSDJeobhYi7OasaUxXktyHVXsHPQD1-vgImdUsFx6pmE 

Parameters

These are the parameters for New-RandomData.

Path Directory to store random file in. Default is current working directory ($Pwd.Path). Use "(Get-Location)" for current directory, not ".", because that defaults to the profile directory inside the cmdlet.
BaseName If generating one file, it is the file name plus a 1 plus the extension. If generating more than one file, it is the base name for the file plus a possibly zero-padded count number. If you specify -RandomFileNameGUID, a GUID will be appended to this base name instead of the count number, making the file name also pseudorandom. The default is "random_file-".
Extension The extension used for the generated files.
Count Number of files to generate. The default is 1.
Size File size in bytes. Default 1024 B. If you specify a line length which the size is not evenly divisible by, the size can be off by +/- 1 byte.
LineLength Number of bytes per line, including \r\n if writing a file. If you specify a line length which the specified size is not evenly divisible by, the size can be off by +/- 1 byte. LineLength cannot be greater than the specified size.
NoClobber Do not overwrite existing files with the same name.
RandomChar Random char array of chars to use for generating the pseudorandom or cryptographically secure random data. Default: [A-Za-z0-9_-]. 64 characters. Fits perfectly in a byte-sized array when repeated four times, making it as efficient as possible when generating cryptographically secure data, while preserving equal weighting of characters.
RandomCharExclude Characters to exclude from the default set of characters (or the supplied set).
NoRandomData Use fsutil and generate a file filled with zero bytes (`0 or \0) extremely fast. NB! No random data with this option.
RandomFileNameGUID Append a GUID to the file base name, making the file name (also) pseudorandom.
Cryptography Use a cryptographically secure random number generator to pick random data from the char array rather than a pseudorandom number generator.
StreamToSTDOUT Do not write any files, but stream random data to STDOUT.