Password Generator tool icon

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
Download New-SecurePassword.ps1Download password-gen.shRead the sourceSee the full workflow

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:

Real terminal output
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 5

On Linux or macOS (bash):

bash password-gen.sh 24 5

Options

-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

Limitations

Know these before you rely on it:

Security notes — is this safe to run?

Compare the checksum of the file you downloaded against this value to be sure it was not tampered with en route:

FileSHA-256
New-SecurePassword.ps1fef44a05c81a85dfa61fe208b0d8ff9c4fcb718d8ca493084fefae5714588ba8
password-gen.shbb2354cf162ac5b625d1767421573f7373d13b1476c7c9d64833fddd1f1597ce

Verify with Get-FileHash .\New-SecurePassword.ps1 -Algorithm SHA256 on Windows, sha256sum New-SecurePassword.ps1 on Linux or macOS.

Alternatives

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:

New-SecurePassword.ps1
<#
.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:

password-gen.sh
#!/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."

← All tools