Jelszó létrehozása (GenerateComplexPassword)

Rengeteg jelszógeneráló PowerShell függvényt találni az interneten, de igazán olyat nem találtam, ami nekem tetszene, így készítettem egyet magamnak. Nem vagyok titkosítási szakember, így nem tudom megítélni, hogy mennyire biztonságosak az így generált jelszavak, de kiindulásnak mindenképpen jók szerintem.

Nézzük, mik az én elvárásaim:

Legyen kimondható, azaz például a „z!1rwteqwwuhd5” nem ilyen

Ne legyenek benne összekeverhető karakterek, azaz például 0 és O (ez most itt egy nulla és egy nagy O betű), meg I l (nagy i és kis L)

Lehessen pszeudo-véletlen jelszót is generálni, azaz egy adott bemeneti szöveghez ugyanazt a jelszót generálja, ezt meg lehessen „sózni”, azaz bele lehessen keverni egy olyan adatot, aminek hiányában hiába ismert az algoritmus, mégsem lehet visszafejteni ezt az pszeudo-véletlen jelszót

 

Nézzük az első verziót, egyelőre még pszeudo és só nélkül:

function GenerateComplexPassword1 {

param(

    [validaterange(9,30)][System.UInt16] $length = 15

)  

 

    $sublength = [math]::Ceiling([math]::Sqrt($length))

 

    $seed = -join ([char[]]"abcdefghijklmnopqrstuvwxyz0123456789-" |

                Get-Random -Count ($sublength * 2))

 

    $first = [int[]][char[]]($seed + "abcdefghijklmnopqrstuvwxyzabcd").Substring(0,$sublength)

    $extstr = "123456789012345678901234567890" + $seed

    $second = [int[]][char[]]$extstr.Substring(($extstr.Length - $sublength),$sublength)

   

    $nums = foreach($e in $first){

                for($i = $sublength-1; $i -ge 0; $i--){

                    $e -bxor $second[$i]

                }

            }

 

    for($i = 0; $i -lt $length; $i++){

        $nums[$i] = $nums[$i] % 20

    }

 

    function sa {param($x)

        ("aeiouaeiouaeiouaeiou" -split "(?<=.)(?=.)")[$x]

    }

    function sb {param($x)

        ("bcdfghjkmnpqrstvwxyz" -split "(?<=.)(?=.)")[$x]

    }

    function la {param($x)

        ("aeuaeuaeuaeuaeuaeuae".ToUpper() -split "(?<=.)(?=.)")[$x]

    }

    function lb {param($x)

        ("bcdfghjkmnpqrstvwxyz".ToUpper() -split "(?<=.)(?=.)")[$x]

    }

 

    function n {param($x)

        ("12345678912345678912" -split "(?<=.)(?=.)")[$x]

    }

    function s {param($x)

        ("!@#$%*-+!@#$%*-+!@#$" -split "(?<=.)(?=.)")[$x]

    }

 

    $rest1 = 1 + [math]::Ceiling(($length - 4) / 2)

 

    $chars = @(lb $nums[0])

    $chars += switch(1..($rest1-1)){

            {$_%2}    {sa $nums[$_]}

            {!($_%2)} {sb $nums[$_]}

        }

    $chars += s $nums[$rest1]

    $chars += la $nums[$rest1 + 1]

 

    $chars += switch(1..($length-$rest1-3)){

            {$_%2}    {sb $nums[$rest1 + $_]}

            {!($_%2)} {sa $nums[$rest1 + $_]}

        }

   

    $chars += n $nums[$length-1]

    $chars -join ""

}

 Hogyan is működik ez? Az egésznek az alapja a $nums változó értékadásánál van középtájon. Itt a $first és $second tömbökben található számokat XOR-olom össze, mintha két focicsapat tagjai kezet fognának egymással. Minden kézfogás eredményez egy számot a két fél kezének a XOR-olásából.  A XOR, azaz a „kizáró vagy” olyan művelet, aminek az eredményéből nem lehet visszafejteni az eredeti adatokat, csak ha vagy az első, vagy a második argumentumot ismerjük. Viszont ekkor ki tudjuk számolni a másik argumentumot.

A jelszó hossza $length lesz. Ahhoz, hogy ennyi kézfogás szülessen a csapatoknak legalább négyzetgyök $length tagja kell legyen, ez a $sublength. A $seed egy $sublength duplája hosszú véletlen karakterekből álló szöveg. Miért szöveg a $seed, és miért nem számok tömbje? Azért, mert már gondolok a pszeudo-véletlen jelszavakra és ott majd a $seed-et paraméternek fogjuk megadni és egy csomó számot sokkal nehezebb lenne begépelni, mint egy 9-30 karakter hosszúságú szöveget.

Szóval a $seed első feléből képzem a $first, azaz az első csapatot, a második csapat, a $second meg a $seed második feléből lesz. Ezután jönnek a kézfogások (XOR), aminek az eredménye a $nums-ba kerül. Mivel a nekem olyan számok kellenek, amik kevesebbek, mint 20, így mindegyiken végzek egy 20-as maradékos osztást.

Miután kimondható jelszavakat szeretnék, ezért magánhangzókat és mássalhangzókat váltakozva szeretnék a jelszóba tenni, valamint kell nekem külön kisbetű, nagybetű, szám és szimbólum is. Ezek generálására külön függvényeket definiáltam, rövid névvel, hogy ne kelljen sokat gépelni, amikor meghívom őket. Az „sa” kisbetűs magánhangzókat, az „sb” kisbetűs mássalhangzókat, az „la”, „lb” nagybetűs magán- és mássalhangzókat, az „n” egy számot, az „s” egy szimbólumot fog adni.

A jelszó két félből fog állni, az eleje nagybetűs mássalhangzóval kezdődik, majd felváltva kisbetűs magánhangzók és mássalhangzók. Majd a felénél lesz egy szimbólum. A második fele nagybetűs magánhangzóval kezdődik, majd felváltva mássalhangzó-magánhangzó, végén meg egy szám.

A $rest1 adja meg, hogy hány karakternél van a jelszó fele. Ez ugye egy kis kerekítést is igényel. Ezután már csak össze kell rakni a karamtereket, minden egyes szám a $nums-ban a megfelelő rövid nevű függvény segítségével kiad egy-egy karaktert. A két switch kifejezés adja felváltva az magán- és mássalhangzókat. A legvégén még egy számjegy és az egész összefűzve szöveggé a kimenet.

Nézzük, hogyan néznek ki ezek a véletlen jelszavak:

PS C:\> . C:\PSKönyv\Projektek\Vegyes\GenerateComplexPassword1.ps1

PS C:\> 1..10 | ForEach-Object {GenerateComplexPassword1 -length 9}

Fihu!Ava1

Jega+Esa3

Xuka%Eke3

Pire-Esi2

Giki@Ecu8

Cozi!Epa4

Hepu#Uqo8

Qesu%Udi5

Hiyi$Ewe6

Kisu@Uto2

PS C:\> 1..10 | ForEach-Object {GenerateComplexPassword1}

Qobexad%Epucij5

Kesenoq$Aficat4

Tocacit@Abovun7

Cojojuc#Uhiduq4

Kajuvit%Afedat7

Wuqeqeg!Ayihax7

Jepuyuy%Arebok3

Magajep-Epijir1

Deguder%Ekuneh3

Cujoxad@Uqorir2

Egész jópofa szörnyneveket lehetne ezekből kreálni a Gyűrűk ura egy újabb kötetébe! Jega és Kajuvit! Giki és Arebok! J

Az első verziójú függvényt egészítsük ki a sózási lehetőséggel és ne csak véletlenül generálódó szöveg lehessen a jelszó alapja, azaz a $seed, hanem lehessen ezt paraméterként is megadni:

function GenerateComplexPassword {

param(

    [string] $seed,

    [string] $salt,

    [validaterange(9,30)][System.UInt16] $length = 15

)  

 

    $sublength = [math]::Ceiling([math]::Sqrt($length))

 

    if($seed -and $seed.Length -lt $sublength*2){

        Write-Error "The length of seed must be at least $($sublength * 2) characters long if the password length is $length" -ErrorAction Stop

    }

 

    if(!$seed){

        $seed = -join ([char[]]"abcdefghijklmnopqrstuvwxyz0123456789-" |

                    Get-Random -Count ($sublength * 2))

    }

 

    if($salt){

        if($salt.Length -gt $length){

            Write-Error "Length of salt must not be longer than the length of the password to be generated!" -ErrorAction Stop

        }

        $i = 1

        while($salt.Length -lt $length){

            $salt += -join @([char[]][int[]]([int[]][char[]]$salt | ForEach-Object {$_ + $i}))

            $i++

        }

        $salt = ($salt * ([math]::Ceiling($length / $salt.Length))).substring(0,$length)

    }

 

    $first = [int[]][char[]]($seed).Substring(0,$sublength)

    $second = [int[]][char[]]$seed.Substring(($seed.Length - $sublength),$sublength)

   

    $nums = foreach($e in $first){

                for($i = $sublength-1; $i -ge 0; $i--){

                    $e -bxor $second[$i]

                }

            }

 

    for($i = 0; $i -lt $length; $i++){

        if($salt){

            $nums[$i] = $nums[$i] -bxor [int][char]$salt[$i]

        }

        $nums[$i] = $nums[$i] % 20

    }

 

    function sa {param($x)

        ("aeiouaeiouaeiouaeiou" -split "(?<=.)(?=.)")[$x]

    }

    function sb {param($x)

        ("bcdfghjkmnpqrstvwxyz" -split "(?<=.)(?=.)")[$x]

    }

    function la {param($x)

        ("aeuaeuaeuaeuaeuaeuae".ToUpper() -split "(?<=.)(?=.)")[$x]

    }

    function lb {param($x)

        ("bcdfghjkmnpqrstvwxyz".ToUpper() -split "(?<=.)(?=.)")[$x]

    }

 

    function n {param($x)

        ("12345678912345678912" -split "(?<=.)(?=.)")[$x]

    }

    function s {param($x)

        ("!@#$%*-+!@#$%*-+!@#$" -split "(?<=.)(?=.)")[$x]

    }

 

    $rest1 = [math]::Ceiling($length / 2) - 1

 

    $chars = @(lb $nums[0])

    $chars += switch(1..($rest1-1)){

            {$_%2}    {sa $nums[$_]}

            {!($_%2)} {sb $nums[$_]}

        }

    $chars += s $nums[$rest1]

    $chars += la $nums[$rest1 + 1]

 

    $chars += switch(1..($length-$rest1-3)){

            {$_%2}    {sb $nums[$rest1 + $_]}

            {!($_%2)} {sa $nums[$rest1 + $_]}

        }

   

    $chars += n $nums[$length-1]

    $chars -join ""

}

Lett tehát két új paraméterünk, a $seed és a $salt. Elsőként, ha megadunk $seed-et, akkor arra van egy minimális megkövetelt hossz, ha azt nem teljesíti, akkor a függvény hibával leáll. Ha nem adunk meg paraméterként $seed-et, akkor a korábbi módon ő generál nekünk egyet véletlenül.

Ha $salt-ot is megadunk, akkor arra egy maximális hossz van követelményként, azaz nem lehet hosszabb, mint a generálandó jelszó hossza. Ha rövidebb ennél, akkor a függvény megnöveli olyan módon, hogy az eredeti $salt karaktereit eggyel eltolja és ezt hozzátoldja. Mindezt egészen addig csinálja, amíg nem kapjuk meg a kívánt hosszat. Például, ha 15 karakteres a kért jelszó, de csak azt adjuk meg „sónak”, hogy „abc”, akkor belül ő erre egészíti ki azt: abcbcdcdedefdef.

Az így képzett só akkor kerül bele az „ételbe”, amikor a $seed-ből kijövő számokat össze-XOR-oltük és utána egy 0 és 20 közötti számmá alakítjuk 20-as maradékos osztással. Merthogy itt még ezelőtt a $salt karaktereiből képzett számmal össze-XOR-oljuk.

Nézzük, hogyan működik most a függvényünk:

PS C:\> GenerateComplexPassword -seed "Ez egy kiindulás"

Tuconad+Afowad1

PS C:\> GenerateComplexPassword -seed "Ez egy kiindulás" -salt kakukk

Sijuyad@Aforif1

PS C:\> GenerateComplexPassword -seed "Ez egy kiindulás" -salt blabla

Garicax#Anisat7

 Látható, hogy nagyon más lesz a jelszó, ha használunk sót, és akkor is nagyon más lesz, ha más a $salt.



Word To HTML Converter