Skip to content
 

Many ways to encrypt passwords

Specifically, using crypt(3). One typical use case is, you have a plaintext password and need the damn full thing to put into the /etc/shadow file, which nowadays is usually something like:

$5$sOmEsAlT$pKHkGjoFXUgvUv.UYQuekdpjoZx7mqXlIlKJj6abik7   # sha-256

or

$6$sOmEsAlT$F3DN61SEKPHtTeIzgzyLe.rpctiym/qxz5xQz9YM.PyTdH7R13ZDXj6sDMeZg5wklbYJYSqDBXcH4UnAWQrRN0   # sha-512

The input to the crypt(3) library function is a cleartext password and a salt. Here we assume the salt is provided, but it's easy to generate a random one (at least one that's "good enough").

In the case of sha-256 and sha-512 hashes (identified respectively by the $5$ and $6$ in the first field, which are also the only ones supported by Linux along with the old md5 which uses code $1$) the salt can be augmented by prepending the rounds=<N>$ directive, to change the default number of rounds used by the algorithm, which is 5000. So for example we could supply a salt like

rounds=500000$sOmEsAlT

and thus use 500000 rounds (this is called stretching and is used to make brute force attacks harder). If the rounds= argument is specified, the output of crypt() includes it as well, since its value must be known every time the hash is recalculated.

It seems there's no utility to directly get the hash string (there used to be a crypt(1) command which however had troubles related to the export of cryptographic software, which made it so weak that many distros stopped shipping it). So we'll have to find some command that calls the crypto(3) function.

In the following examples, we assume the algorithm number, the salt and the password are stored in the shell variables $alg, $salt and $password respectively:

alg=6
salt='rounds=500000$sOmEsAlT'
password='password'

This way, the code doesn't hardcode anything and can be reused.

Perl

$ perl -e 'print crypt($ARGV[1], "\$" . $ARGV[0] . "\$" . $ARGV[2]), "\n";' "$alg" "$password" "$salt"
$6$rounds=500000$sOmEsAlT$Rf3.xi9RRiCW/FTh4gp67TSLyKotq1QkGkbn0O6cYDYEExwrFE30zeKGDIaZ3TZ.RDwiNya5nKlPDRTA0U4E8/

Python

# python 2/3
$ python -c 'import crypt; import sys; print (crypt.crypt(sys.argv[2],"$" + sys.argv[1] + "$" + sys.argv[3]))' "$alg" "$password" "$salt"
$6$rounds=500000$sOmEsAlT$Rf3.xi9RRiCW/FTh4gp67TSLyKotq1QkGkbn0O6cYDYEExwrFE30zeKGDIaZ3TZ.RDwiNya5nKlPDRTA0U4E8/

MySQL

Yes, MySQL has a built-in function that uses crypt(3):

$ mysql -B -N -e "select encrypt('$password', '\$$alg\$$salt');" 
$6$rounds=500000$sOmEsAlT$Rf3.xi9RRiCW/FTh4gp67TSLyKotq1QkGkbn0O6cYDYEExwrFE30zeKGDIaZ3TZ.RDwiNya5nKlPDRTA0U4E8/

Obviously, extra care should be taken with this one if $password or $salt contain quotes or other characters that are special to MySQL.

Php

$ php -r 'echo crypt($argv[2], "\$" . $argv[1] . "\$" . $argv[3]) . "\n";' "$alg" "$password" "$salt"
$6$rounds=500000$sOmEsAlT$Rf3.xi9RRiCW/FTh4gp67TSLyKotq1QkGkbn0O6cYDYEExwrFE30zeKGDIaZ3TZ.RDwiNya5nKlPDRTA0U4E8/

Ruby

$ ruby -e 'puts ARGV[1].crypt("$" + ARGV[0] + "$" + ARGV[2]);' "$alg" "$password" "$salt"
$6$rounds=500000$sOmEsAlT$Rf3.xi9RRiCW/FTh4gp67TSLyKotq1QkGkbn0O6cYDYEExwrFE30zeKGDIaZ3TZ.RDwiNya5nKlPDRTA0U4E8/

mkpasswd

This utility comes with the whois package (at least in Debian). Here it's better to introduce another separate variable to hold the number of rounds:

# password as before
rounds=500000
salt=sOmEsAlT

(and of course the other examples can be adapted to use the three variables instead of two). Then it can be used as follows:

$ mkpasswd -m sha-512 -R "$rounds" -S "$salt" "$password"
$6$rounds=500000$sOmEsAlT$Rf3.xi9RRiCW/FTh4gp67TSLyKotq1QkGkbn0O6cYDYEExwrFE30zeKGDIaZ3TZ.RDwiNya5nKlPDRTA0U4E8/

If using the standard number of rounds the -R option can be omitted, of course. Here the algorithm is specified by name, so the $alg variable is not used.