﻿# 使用 PowerShell 快速建立安全密碼的幾種方法 (密碼產生器)

最近看到有人在影片中利用 PowerShell 產生亂數密碼，但是為了簡單產生個密碼，都要打長長的命令也不太實用了吧。所以我打算整理幾個密碼產生器的 PS1 函式，並加入到 `$PROFILE` 設定檔中，如此一來，日後產生密碼就方便多了！ 👍

### 使用自訂字元亂數產生密碼

-   單行命令
    
    ```bash
    ("!@#$%^&*0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".ToCharArray() | sort {Get-Random})[0..8] -join ''
    ```
    
-   自訂函式
    
    ```powershell
    function New-RandomPassword {
        param(
            [Parameter()]
            [int]$MinimumPasswordLength = 8,
            [Parameter()]
            [int]$MaximumPasswordLength = 12,
            [Parameter()]
            [switch]$ConvertToSecureString
        )
        $length = Get-Random -Minimum $MinimumPasswordLength -Maximum $MaximumPasswordLength
        $password = ("!@#$%^&*0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".ToCharArray() | sort {Get-Random})[0..$length] -join ''
        if ($ConvertToSecureString.IsPresent) {
            ConvertTo-SecureString -String $password -AsPlainText -Force
        } else {
            $password
        }
    }
    ```
    
-   使用方式
    
    產生長度 8 ~ 12 字元的密碼
    
    ```powershell
    New-RandomPassword
    ```
    
    產生長度 8 ~ 12 字元的密碼，並使用 [ConvertTo-SecureString](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/convertto-securestring?view=powershell-7.1&WT.mc_id=DT-MVP-4015686) 轉成一組**安全字串**([SecureString](https://learn.microsoft.com/en-us/dotnet/api/system.security.securestring?view=net-5.0&WT.mc_id=DT-MVP-4015686))
    
    ```powershell
    New-RandomPassword -MinimumPasswordLength 10 -MaximumPasswordLength 15 -ConvertToSecureString
    ```
    
-   基本限制
    
    簡單、易用、跨平台，唯一缺點是 **亂數強度** 比較沒那麼高，但應該大部分的使用情境下應該都沒問題，當成個人密碼已經綽綽有餘。
    
-   參考資料
    
    -   [\[SOLVED\] Generate random password - PowerShell - Spiceworks](https://community.spiceworks.com/topic/2130057-generate-random-password)
    -   [Get-Random (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Docs](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-random?view=powershell-7.1&WT.mc_id=DT-MVP-4015686)
    -   [排序物件 - PowerShell | Microsoft Docs](https://learn.microsoft.com/zh-tw/powershell/scripting/samples/sorting-objects?view=powershell-7.1&WT.mc_id=DT-MVP-4015686)

### 使用 System.Web.Security.Membership 靜態類別來建立密碼

-   單行命令
    
    ```bash
    Add-Type -AssemblyName 'System.Web'; [System.Web.Security.Membership]::GeneratePassword(12, 3)
    ```
    
-   自訂函式
    
    ```powershell
    function GeneratePassword {
        param(
            [Parameter()]
            [int]$MinimumPasswordLength = 8,
            [Parameter()]
            [int]$MaximumPasswordLength = 12,
            [Parameter()]
            [int]$NumberOfAlphaNumericCharacters = 5,
            [Parameter()]
            [switch]$ConvertToSecureString
        )
    
        Add-Type -AssemblyName 'System.Web'
        $length = Get-Random -Minimum $MinimumPasswordLength -Maximum $MaximumPasswordLength
        $password = [System.Web.Security.Membership]::GeneratePassword($length,$NumberOfAlphaNumericCharacters)
        if ($ConvertToSecureString.IsPresent) {
            ConvertTo-SecureString -String $password -AsPlainText -Force
        } else {
            $password
        }
    }
    ```
    
-   使用方式
    
    產生長度 8 ~ 12 字元的密碼，其所產生密碼中的**非英數字元數**下限預設為 5 碼 (例如 @、#、!、%、& 等等)。
    
    ```powershell
    GeneratePassword
    ```
    
    產生長度 8 ~ 12 字元的密碼，其所產生密碼中的**非英數字元數**下限為 6 碼 (例如 @、#、!、%、& 等等)。
    
    ```powershell
    GeneratePassword -NumberOfAlphaNumericCharacters 6
    ```
    
    產生長度 10 ~ 15 字元的密碼，並使用 [ConvertTo-SecureString](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/convertto-securestring?view=powershell-7.1&WT.mc_id=DT-MVP-4015686) 轉成一組**安全字串**([SecureString](https://learn.microsoft.com/en-us/dotnet/api/system.security.securestring?view=net-5.0&WT.mc_id=DT-MVP-4015686))
    
    ```powershell
    GeneratePassword -MinimumPasswordLength 10 -MaximumPasswordLength 15 -ConvertToSecureString
    ```
    
-   基本限制
    
    簡單、易用、亂數強度高，唯一缺點是**僅限 Windows 可用**，而且**不支援PowerShell Core** (`pwsh.exe`) 環境，因為 `System.Web` 只有 Windows 平台的 .NET Framework 才有。
    
-   參考資料
    
    -   [How to Create a Random Password Generator](https://adamtheautomator.com/random-password-generator/)
    -   [Membership 類別 (System.Web.Security) | Microsoft Docs](https://learn.microsoft.com/zh-tw/dotnet/api/system.web.security.membership?view=netframework-4.8&viewFallbackFrom=netcore-3.1&WT.mc_id=DT-MVP-4015686)
    -   [Membership.GeneratePassword(Int32, Int32) 方法 (System.Web.Security) | Microsoft Docs](https://learn.microsoft.com/zh-tw/dotnet/api/system.web.security.membership.generatepassword?view=netframework-4.8&WT.mc_id=DT-MVP-4015686#System_Web_Security_Membership_GeneratePassword_System_Int32_System_Int32_)
    -   [ConvertTo-SecureString (Microsoft.PowerShell.Security) - PowerShell | Microsoft Docs](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/convertto-securestring?view=powershell-7.1&WT.mc_id=DT-MVP-4015686)

### 批次產生多組密碼

-   自訂函式
    
    ```powershell
    function New-Password {
        <#
        .SYNOPSIS
            Generate a random password.
        .DESCRIPTION
            Generate a random password.
        .NOTES
            Change log:
                27/11/2017 - faustonascimento - Swapped Get-Random for System.Random.
                                                Swapped Sort-Object for Fisher-Yates shuffle.
                17/03/2017 - Chris Dent - Created.
        #>
    
        [CmdletBinding()]
        [OutputType([String])]
        param (
            # The length of the password which should be created.
            [Parameter(ValueFromPipeline)]
            [ValidateRange(8, 255)]
            [Int32]$Length = 10,
    
            # The character sets the password may contain. A password will contain at least one of each of the characters.
            [String[]]$CharacterSet = ('abcdefghijklmnopqrstuvwxyz',
                                      'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
                                      '0123456789',
                                      '!$%&^.#;'),
    
            # The number of characters to select from each character set.
            [Int32[]]$CharacterSetCount = (@(1) * $CharacterSet.Count),
            [Parameter()]
            [switch]$ConvertToSecureString
        )
    
        begin {
            $bytes = [Byte[]]::new(4)
            $rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
            $rng.GetBytes($bytes)
    
            $seed = [System.BitConverter]::ToInt32($bytes, 0)
            $rnd = [Random]::new($seed)
    
            if ($CharacterSet.Count -ne $CharacterSetCount.Count) {
                throw "The number of items in -CharacterSet needs to match the number of items in -CharacterSetCount"
            }
    
            $allCharacterSets = [String]::Concat($CharacterSet)
        }
    
        process {
            try {
                $requiredCharLength = 0
                foreach ($i in $CharacterSetCount) {
                    $requiredCharLength += $i
                }
    
                if ($requiredCharLength -gt $Length) {
                    throw "The sum of characters specified by CharacterSetCount is higher than the desired password length"
                }
    
                $password = [Char[]]::new($Length)
                $index = 0
    
                for ($i = 0; $i -lt $CharacterSet.Count; $i++) {
                    for ($j = 0; $j -lt $CharacterSetCount[$i]; $j++) {
                        $password[$index++] = $CharacterSet[$i][$rnd.Next($CharacterSet[$i].Length)]
                    }
                }
    
                for ($i = $index; $i -lt $Length; $i++) {
                    $password[$index++] = $allCharacterSets[$rnd.Next($allCharacterSets.Length)]
                }
    
                # Fisher-Yates shuffle
                for ($i = $Length; $i -gt 0; $i--) {
                    $n = $i - 1
                    $m = $rnd.Next($i)
                    $j = $password[$m]
                    $password[$m] = $password[$n]
                    $password[$n] = $j
                }
    
                $password = [String]::new($password)
                if ($ConvertToSecureString.IsPresent) {
                    ConvertTo-SecureString -String $password -AsPlainText -Force
                } else {
                    $password
                }
            } catch {
                Write-Error -ErrorRecord $_
            }
        }
    }
    ```
    
-   使用方式
    
    產生長度 10 字元的密碼 (預設長度為 10 個字元)
    
    ```powershell
    New-Password
    ```
    
    產生長度 12 字元的密碼
    
    ```powershell
    New-Password -Length 12
    ```
    
    產生長度 12 個字元的密碼，但指定 2 個小寫字母、3 個大寫字母、5 個數字、2 個特殊符號
    
    ```powershell
    New-Password -Length 12 -CharacterSetCount 2,3,5,2
    ```
    
    產生長度 15 字元的密碼，並使用 [ConvertTo-SecureString](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/convertto-securestring?view=powershell-7.1&WT.mc_id=DT-MVP-4015686) 轉成一組**安全字串**([SecureString](https://learn.microsoft.com/en-us/dotnet/api/system.security.securestring?view=net-5.0&WT.mc_id=DT-MVP-4015686))
    
    ```powershell
    New-Password -Length 15 -ConvertToSecureString
    ```
    
    產生 20 組長度 12 個字元的密碼
    
    ```powershell
    @(12) * 20 | New-Password
    ```
    
-   基本限制
    
    簡單、易用、跨平台、多功能，唯一缺點是**不能產生變動長度的密碼**。
    
-   參考資料
    
    -   [PowerShell random password generator.](https://gist.github.com/indented-automation/2093bd088d59b362ec2a5b81a14ba84e)

### 相關連結

-   [PowerShell\_Password\_Generator.ps1](https://gist.github.com/doggy8088/4bf8a3189460071acece2518f1fa01e4)
