Password Generator
Generate strong passwords offline — free script, never sent to a website.
- Platforms
- Windows, Linux, macOS
- Needs
- Nothing to install. PowerShell is built into Windows; the bash version reads from /dev/urandom on Linux and macOS.
- Files
- New-SecurePassword.ps1 (PowerShell) · password-gen.sh (bash)
- Licence
- MIT — read, modify, redistribute
- Verified
- Ran on our test machine on 2026-10-17
- Last reviewed
- 2026-10-17
The problem it solves
Every account deserves a unique password, and your password manager needs a strong master one. Generate them offline, on your own machine, without handing a website the input.
Example output
Here is what the script printed when we ran it on a test computer:
h[!7c4BE8}^=NjeT*BnJ V]BFTnp5B3}cH/hPg_J- %Jr6!3kw@EVvWrE#ADEk Tip: generated in memory only. Paste directly into your password manager; do not save it to a text file.
Usage
Download the file, then run it from a terminal. On Windows (PowerShell):
powershell -NoProfile -ExecutionPolicy Bypass -File .\New-SecurePassword.ps1 -Length 24 -Count 5On Linux or macOS (bash):
bash password-gen.sh 24 5Options
-Length <n>- password length, 8 to 64 (default 20)
-Count <n>- how many to generate, 1 to 20 (default 5)
-Passphrase- hyphen-separated word passphrase instead
-Words <n>- words in a passphrase, 3 to 10 (default 6)
What it does
- Uses the Windows cryptographic random number generator (CSPRNG), not Math.random()-style pseudo-randomness
- Guarantees at least one lowercase, uppercase, digit and symbol, then shuffles with Fisher-Yates
- Passphrase mode from a built-in word list
- Prints to the console only; nothing is written to disk or sent anywhere
Limitations
Know these before you rely on it:
- It generates - it does not store. Copy the result straight into a password manager
- The built-in passphrase word list is about 150 words: fine for a memorable secondary password, weaker than a proper Diceware list (8,000+ words) for a master password. For a master password prefer a long random string, or use the KeePassXC generator
- No strength meter; you choose the length
- It does not detect a compromised password - that is what a breach-checking manager is for
Security notes — is this safe to run?
- Generate on the machine where the password will be used, or copy it directly into your manager
- Be aware of clipboard managers, terminal scrollback and screenshots when the output is on screen
- Never use an online "random password generator" website - you have no idea who is logging the input
- A password is only as safe as the store behind it: pair it with an offline manager such as KeePassXC
Compare the checksum of the file you downloaded against this value to be sure it was not tampered with en route:
| File | SHA-256 |
|---|---|
New-SecurePassword.ps1 | fef44a05c81a85dfa61fe208b0d8ff9c4fcb718d8ca493084fefae5714588ba8 |
password-gen.sh | bb2354cf162ac5b625d1767421573f7373d13b1476c7c9d64833fddd1f1597ce |
Verify with Get-FileHash .\New-SecurePassword.ps1 -Algorithm SHA256 on Windows, sha256sum New-SecurePassword.ps1 on Linux or macOS.
Alternatives
- KeePassXC built-in generator — best choice if you already use KeePassXC - generates and stores in one step
- Bitwarden generator — inside the manager, with breach checking
- pwgen — long-standing Linux CLI generator
- 1Password / Bitwarden CLI — scriptable generation for automation
Other tools on this site
- System Report — Check your PC specs: CPU, RAM, disk space and network — one free script, no install.
- Disk Usage — C drive full? Find which folder is using the most disk space on Windows, Mac or Linux.
- Large Files — Find the largest files on your PC — forgotten downloads, old backups, duplicate videos.
In context
This tool is one of the steps in the how to generate strong passwords offline workflow — the guide explains what order to run things in and how to interpret the results.
Source
The complete PowerShell source, so you can audit it before running:
<#
.SYNOPSIS
New-SecurePassword - generates strong passwords or passphrases offline.
.DESCRIPTION
Uses the Windows cryptographic random number generator (CSPRNG). Prints to
the console only: nothing is written to disk, nothing is sent anywhere.
Paste the result straight into your password manager.
.EXAMPLE
powershell -ExecutionPolicy Bypass -File .\New-SecurePassword.ps1 -Length 24 -Count 5
.EXAMPLE
powershell -ExecutionPolicy Bypass -File .\New-SecurePassword.ps1 -Passphrase -Words 6
#>
[CmdletBinding()]
param(
[ValidateRange(8,64)]
[int]$Length = 20,
[ValidateRange(1,20)]
[int]$Count = 5,
[switch]$Passphrase,
[ValidateRange(3,10)]
[int]$Words = 6
)
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
# Unbiased random index via rejection sampling (never plain modulo).
function Get-RandomIndex([uint32]$UpperBound) {
$limit = [uint64]4294967296 - ([uint64]4294967296 % $UpperBound)
$buf = New-Object byte[] 4
do {
$rng.GetBytes($buf)
$value = [BitConverter]::ToUInt32($buf, 0)
} while ([uint64]$value -ge $limit)
return [int]($value % $UpperBound)
}
function Get-RandomChar([string]$set) { $set[(Get-RandomIndex $set.Length)] }
$lower = 'abcdefghijkmnopqrstuvwxyz'
$upper = 'ABCDEFGHJKLMNPQRSTUVWXYZ'
$digits = '23456789'
$symbols = '!@#$%^&*()-_=+[]{};:,.?/'
$all = $lower + $upper + $digits + $symbols
$wordList = @(
'able','acid','also','area','army','away','baby','back','bake','ball','band','bank','base','bath','bear',
'beat','been','bell','belt','bend','best','bird','blue','boat','body','boil','bomb','bond','bone','book',
'boom','born','boss','both','bowl','bulk','burn','busy','cafe','cage','cake','calm','camp','card','care',
'case','cash','cast','cave','chat','chip','city','club','coal','coat','code','cold','come','cook','cool',
'cope','copy','core','cost','crew','crop','dark','data','date','dawn','dead','deal','dear','debt','deep',
'desk','dial','diet','disk','dive','dock','does','dog','done','door','dose','down','draw','drop','drug',
'dual','duck','dust','duty','each','east','easy','echo','edge','else','even','ever','face','fact','fair',
'fall','farm','fast','fear','feed','feel','feet','fell','felt','file','fill','film','find','fine','fire',
'firm','fish','five','flat','flow','food','foot','form','four','free','fuel','full','game','gate','gave',
'gear','gift','girl','give','glad','goal','goes','gold','golf','gone','good','gray','grew','grow','hair',
'half','hall','hand','hard','harm','hate','have','head','hear','heat','held','hell','help','here','hero',
'high','hill','hire','hold','hole','home','hope','hour','huge','hang','iron','item','join','jump','just',
'keep','kept','kick','kind','king','knew','know','lack','lady','lake','lamp','land','last','late','lead',
'left','leg','less','life','lift','like','line','link','lion','list','live','load','loan','lock','long',
'look','lose','loss','love','made','mail','main','make','male','many','mark','mass','meal','mean','meat',
'meet','menu','mere','mess','mile','milk','mind','mine','miss','mode','mood','more','most','move','much',
'must','name','near','neck','need','news','next','nice','note','okay','once','only','onto','open','over',
'pace','pack','page','pain','pair','park','part','past','path','peak','pick','pink','pipe','plan','play',
'plot','plus','poem','pool','poor','port','pose','pull','pure','push','race','rail','rain','rank','rare',
'rate','read','real','rest','rice','rich','ride','ring','rise','risk','road','rock','role','roll','roof',
'room','rose','rule','rush','safe','sail','sale','same','sand','save','seat','seed','seek','seem','seen',
'sell','send','shop','shot','show','side','sign','sing','sink','site','size','slow','snow','soft','soil',
'some','song','soon','sort','soul','star','stay','step','stop','such','talk','tall','task','team','tell',
'term','test','text','than','that','them','then','thin','this','time','tiny','tool','tour','town','tree',
'trip','true','turn','type','unit','upon','user','vary','vast','view','vote','wait','wake','walk','wall',
'want','warm','wash','wave','ways','weak','wear','week','well','went','were','west','what','when','wide',
'wife','wild','will','wind','wine','wish','wood','word','wore','work','yard','year','your','zone'
)
for ($i = 0; $i -lt $Count; $i++) {
if ($Passphrase) {
$picked = @()
while ($picked.Count -lt $Words) {
$w = $wordList[(Get-RandomIndex $wordList.Count)]
if ($picked -notcontains $w) { $picked += $w }
}
Write-Host ($picked -join '-')
} else {
# Guarantee one of each class, then shuffle with Fisher-Yates.
$chars = [System.Collections.Generic.List[char]]::new()
$chars.Add((Get-RandomChar $lower))
$chars.Add((Get-RandomChar $upper))
$chars.Add((Get-RandomChar $digits))
$chars.Add((Get-RandomChar $symbols))
while ($chars.Count -lt $Length) { $chars.Add((Get-RandomChar $all)) }
for ($j = $chars.Count - 1; $j -gt 0; $j--) {
$k = Get-RandomIndex ($j + 1)
$tmp = $chars[$j]; $chars[$j] = $chars[$k]; $chars[$k] = $tmp
}
Write-Host (-join $chars)
}
}
Write-Host ''
Write-Host 'Tip: generated in memory only. Paste directly into your password manager; do not save it to a text file.'The bash version:
#!/usr/bin/env bash
# password-gen.sh - generates strong passwords or passphrases offline.
# Uses the OS CSPRNG (/dev/urandom). Prints to the terminal only;
# nothing is written to disk. Paste straight into your password manager.
# Run: bash password-gen.sh [length] [count]
# bash password-gen.sh -p [words] [count]
set -u
MODE="password"
if [ "${1:-}" = "-p" ] || [ "${1:-}" = "--passphrase" ]; then
MODE="passphrase"
WORDS="${2:-6}"
COUNT="${3:-5}"
else
LENGTH="${1:-20}"
COUNT="${2:-5}"
fi
# Safety floor: never generate fewer characters than the guaranteed classes.
if [ "$MODE" = "password" ] && [ "$LENGTH" -lt 8 ]; then
LENGTH=8
fi
rand_below() {
# Unbiased random below $1 via rejection sampling on /dev/urandom.
local limit=$((4294967296 - 4294967296 % $1))
local v
while :; do
v=$(od -An -N4 -tu4 < /dev/urandom | tr -d ' ')
[ "$v" -lt "$limit" ] && break
done
echo $((v % $1))
}
CHARS_LOWER="abcdefghijkmnopqrstuvwxyz"
CHARS_UPPER="ABCDEFGHJKLMNPQRSTUVWXYZ"
CHARS_DIGIT="23456789"
CHARS_SYMBOL='!@#$%^&*()-_=+[]{};:,.?/'
ALL="${CHARS_LOWER}${CHARS_UPPER}${CHARS_DIGIT}${CHARS_SYMBOL}"
WORDS_LIST="able acid also area army away baby back bake ball band bank base bath bear beat been bell belt bend best bird blue boat body boil bomb bond bone book boom born boss both bowl bulk burn busy cafe cage cake calm camp card care case cash cast cave chat chip city club coal coat code cold come cook cool cope copy core cost crew crop dark data date dawn dead deal dear debt deep desk dial diet disk dive dock does dog done door dose down draw drop drug dual duck dust duty each east easy echo edge else even ever face fact fair fall farm fast fear feed feel feet fell felt file fill film find fine fire firm fish five flat flow food foot form four free fuel full game gate gave gear gift girl give glad goal goes gold golf gone good gray grew grow hair half hall hand hard harm hate have head hear heat held hell help here hero high hill hire hold hole home hope hour huge hang iron item join jump just keep kept kick kind king knew know lack lady lake lamp land last late lead left leg less life lift like line link lion list live load loan lock long look lose loss love made mail main make male many mark mass meal mean meat meet menu mere mess mile milk mind mine miss mode mood more most move much must name near neck need news next nice note okay once only onto open over pace pack page pain pair park part past path peak pick pink pipe plan play plot plus poem pool poor port pose pull pure push race rail rain rank rare rate read real rest rice rich ride ring rise risk road rock role roll roof room rose rule rush safe sail sale same sand save seat seed seek seem seen sell send shop shot show side sign sing sink site size slow snow soft soil some song soon sort soul star stay step stop such talk tall task team tell term test text than that them then thin this time tiny tool tour town tree trip true turn type unit upon user vary vast view vote wait wake walk wall want warm wash wave ways weak wear week well went were west what when wide wife wild will wind wine wish wood word wore work yard year your zone"
pick_char() { printf '%s' "$1" | cut -c "$(( $(rand_below ${#1}) + 1 ))"; }
if [ "$MODE" = "passphrase" ]; then
echo "Passphrases ($WORDS words, hyphen-separated):" picked=()
total_words=$(echo $WORDS_LIST | wc -w)
while [ "${#picked[@]}" -lt "$WORDS" ]; do
idx=$(rand_below "$total_words")
w=$(echo $WORDS_LIST | cut -d' ' -f$((idx + 1)))
skip=0
for p in "${picked[@]}"; do
[ "$p" = "$w" ] && skip=1 && break
done
[ "$skip" -eq 0 ] && picked+=("$w")
done
(IFS='-'; echo "${picked[*]}")
else
echo "Passwords ($LENGTH characters):"
i=0
while [ "$i" -lt "$COUNT" ]; do
# Guarantee one of each class first.
chars=("$(pick_char "$CHARS_LOWER")" "$(pick_char "$CHARS_UPPER")" "$(pick_char "$CHARS_DIGIT")" "$(pick_char "$CHARS_SYMBOL")")
while [ "${#chars[@]}" -lt "$LENGTH" ]; do
chars+=("$(pick_char "$ALL")")
done
# Fisher-Yates shuffle using the CSPRNG.
n=${#chars[@]}
while [ "$n" -gt 1 ]; do
n=$((n - 1))
k=$(rand_below $((n + 1)))
tmp="${chars[$n]}"
chars[$n]="${chars[$k]}"
chars[$k]="$tmp"
done
echo "${chars[*]}" | tr -d ' '
i=$((i + 1))
done
fi
echo ""
echo "Tip: generated in memory only. Paste directly into your password manager; do not save it to a text file."