Crypt::Eksblowfish::Bcrypt - Blowfish-based Unix crypt() password hash
Veröffentlicht von Thomas Fahle am (Permalink)
Crypt::Eksblowfish::Bcrypt
Das CPAN-Modul Crypt::Eksblowfish::Bcrypt - Blowfish-based Unix crypt() password hash von Andrew Main (Zefram) implementiert den Blowfish-basierten Unix crypt()-Passwort-Hash-Algorithmus, bekannt als bcrypt.
Dieser Hash verwendet eine Variante von Blowfish, bekannt als Eksblowfish, die so modifiziert wurde, dass sie eine besonders teure Schlüsselplanung hat. Eksblowfish und bcrypt wurden von Niels Provos und David Mazieres für OpenBSD entwickelt. Das Design ist in USENIX 99 - A Future-Adaptable Password Scheme beschrieben.
Hauptanwendungsgebiet ist das sichere, verschlüsselte Speichern von Passwörtern - also so, dass das Original-Passwort nicht wiederhergestellt werden kann.
Funktionen
Crypt::Eksblowfish::Bcrypt stellt vier Funktionen zur Verfügung:
- bcrypt_hash(), die eigentliche Hash-Funktion
- bcrypt(), eine Funktion zur Erzeugung von crypt() kompatiblen Strings aus bcrypt-Hashes
- en_base64(), Base64-Kodierung der Octets/Bytes
- de_base64(), welche die Base64-Kodierung umkehrt
Beispiel Hash berechnen mit bcrypt_hash()
Das folgende Beispiel verwendet die Funktion bcrypt_hash() um den Bcrypt-Hash eines Passwortes aus einem zufälligem Salt (exakt 16 Byte) zu erzeugen. Über den Parameter cost wird die Anzahl der Rechenrunden beeinflußt. key_nul dient der Rückwärtskompatibilität und sollte für aktuelle Implementierungen stets auf 1 gesetzt werden.
#!/usr/bin/perl use strict; use warnings; use feature 'say'; use Crypt::URandom qw( urandom ); use Crypt::Eksblowfish::Bcrypt qw(bcrypt_hash en_base64); my $password = 'supercalifragilisticexpialidocious'; # Non-negative integer controlling the cost of the hash function. # The number of operations is proportional to 2^cost. my $cost = 5; # Exactly sixteen octets of salt from a strong non-blocking random source my $salt = urandom(16); my $hash = bcrypt_hash( { key_nul => 1, cost => $cost, salt => $salt, }, $password ); # Encodes the octet string textually using the form of base 64 # that is conventionally used with bcrypt. my $encoded = en_base64($hash); say $encoded; # Encodes the octet string as a hex string (high nybble first). my $hex = unpack( 'H*', $hash ); say $hex;
Das Programm liefert z.B. folgende Ausgabe:
.EgtpXKVjY3XPuD0NulV5GK5vA2kDYe 0068afad931795ae594701763f09d7ec833bc42e2615a8
Bcrypt Hashes als Strings speichern
Die so errechneten Hashes werden als Strings im Password Storage, z.B. /etc/shadow, zusammen mit dem Cost Factor und dem Salt gespeichert. Diese Zeichenkette hat die allgemeine Form:
$2[a|b|y]$[cost]$[22 character salt][31 character hash]
Beispiel:
$2y$10$GI7ziJuT2wADu1BQMhnDKOJL5dYnpJj2zoGWwS2MrKgdgmGYHHzrq \__/\/ \____________________/\_____________________________/ Alg Cost Salt Hash
Hier bedeutet
- $2a$ oder $2b$ oder $2y$: The hash algorithm identifier (bcrypt)
- 10: Cost factor (2^10 ==> 1,024 rounds)
- GI7ziJuT2wADu1BQMhnDKO: 16-byte (128-bit) salt, base64-encoded to 22 characters
- JL5dYnpJj2zoGWwS2MrKgdgmGYHHzrq: 24-byte (192-bit) hash, base64-encoded to 31 characters
Beispiel bcrypt() - Bcrypt Hashes als Strings
Das folgende Beispiel nutzt die Funktion bcrypt(PASSWORD, SETTINGS) zur Erzeugung eines crypt()-kompatiblen Strings.
Die Funktion erwartet das Passwort und einen speziell konstruierten String von Settings, der u.a. cost und salt enthält.
Leider unterstützt bcrypt() nur den Identifier a als Hash Algorithmus Identifier. Die ebenfalls gültigen Identifier b und y erzeugen eine Fehlermeldung. (Einen möglichen Workaround/Patch findet man in Crypt::Eksblowfish::Bcrypt doesnt support 2y.)
#!/usr/bin/perl use strict; use warnings; use feature 'say'; use Crypt::URandom qw( urandom ); use Crypt::Eksblowfish::Bcrypt qw(bcrypt en_base64); my $password = 'supercalifragilisticexpialidocious'; # Non-negative integer controlling the cost of the hash function. # The number of operations is proportional to 2^cost. my $cost = 5; # Exactly sixteen octets of salt from a strong non-blocking random source my $salt = urandom(16); my $salt_encoded = en_base64($salt); # Assemble settings string my $settings; # "$2", optional "a", $ # only a is supported - b or y will yield an error $settings .= '$2a$'; # cost as two digits (leading zero) $cost = sprintf( "%02d", $cost ); $settings .= $cost; # $ $settings .= '$'; # 22 base 64 digits (salt) $settings .= $salt_encoded; my $bycrypt_storage_string = bcrypt( $password, $settings ); # Debug say "Settings: $settings"; say "Bcrypt string: $bycrypt_storage_string";
Das Programm liefert z.B. folgende Ausgabe:
Settings: $2a$05$aI5KrNhn5/CHtUHT/ZaHou Bcrypt string: $2a$05$aI5KrNhn5/CHtUHT/ZaHouLpAGo8PA3Yclpv1Y4mNehB1fandFE6O
Passwort Check
Um das vom User eingebene Passwort zu prüfen, wird aus diesem Passwort erneut ein Hash mit exakt denselben Parametern (cost,salt) berechnet und Base64-kodiert. Anschließend werden die beiden Hashes verglichen. Stimmen sie überein, gilt das eingegeben Passwort als korrekt.
Die Parameter cost und salt werden aus der im Password Storage hinterlegten Zeichenkette (s.o.) ermittelt.
Das nachfolgende Bespiel verdeutlicht die Vorgehensweise.
#!/usr/bin/perl use strict; use warnings; use feature "say"; use Crypt::Eksblowfish::Bcrypt "en_base64", "de_base64", "bcrypt_hash"; # Cleartext password from user input my $pass_input = "12345678"; # bcrypt hash string from password storage or other application # e.g. my $bcrypt_hash_string = qx{php -r 'echo password_hash($pass_input, PASSWORD_BCRYPT);'}; my $bcrypt_hash_string = '$2y$10$GI7ziJuT2wADu1BQMhnDKOJL5dYnpJj2zoGWwS2MrKgdgmGYHHzrq'; my ( $unused, $algorithm, $cost, $salt_and_pass ) = split /\$/, $bcrypt_hash_string; # 22 character salt (base64 encoded) my $salt = substr $salt_and_pass, 0, 22; # 31 character bcrypt hash (base64 encoded) my $encoded_pass_hash = substr $salt_and_pass, 22, 31; # Debug say "$algorithm, $cost, $salt_and_pass, $salt, $encoded_pass_hash"; # Comput bcrypt hash using identified cost and salt my $perl_bcrypt_hash = en_base64( bcrypt_hash( { cost => $cost, key_nul => 1, salt => de_base64($salt) }, $pass_input ) ); # Method 1 # compare supplied bcrypt hash string from password storage # with computed (Perl) bcrypt hash string # Assemble (Perl) bcrypt hash string my $perl_bcrypt_hash_string = sprintf '$%s$%d$%s%s', $algorithm, $cost, $salt, $perl_bcrypt_hash; # Debug say ""; say "Other $bcrypt_hash_string"; say "Perl $perl_bcrypt_hash_string"; if ( $bcrypt_hash_string eq $perl_bcrypt_hash_string ) { say "Okay. Password hash strings match."; } else { say "Sorry. Password hash strings do not match"; } # Method 2 # or compare encoded hashes # Debug say ""; say "Other $encoded_pass_hash"; say "Perl $perl_bcrypt_hash"; if ( $encoded_pass_hash eq $perl_bcrypt_hash ) { say "Okay. Password hashes match."; } else { say "Sorry. Password hashes do not match"; }
Siehe auch
- Crypt::Eksblowfish::Bcrypt
- Digest::Bcrypt
- Crypt::URandom
- Crypt::URandom - nahezu betriebssystemunabhängige und nicht-blockierende Zufälligkeit
- Perlmonks: use Crypt::Eksblowfish::Bcrypt to create a password the same as password_hash in PHP
- ETOOBUSY - Bcrypt password hashing
- man 5 crypt
- USENIX 99 - A Future-Adaptable Password Scheme
- bcrypt password hashing for your software and your servers
- Wikipedia bcrypt
- Hashing in Action: Understanding bcrypt
- bcrypt.online - Bcrypt Hash Generator & Verifier
- Bcrypt-Generator.com - Online Bcrypt Hash Generator & Checker