Generating a random password; why isn't this portable?

I want to generate a random password, and am doing it like so:

</dev/urandom tr -dc [:print:] | head -c 64

On my laptop, which runs Ubuntu, this produces only printable characters, as intended. But when I ssh into my school’s server, which runs Red Hat Enterprise Linux, and run it there, I get outputs like 3!ri�b�GrӴ��1�H�<�oM����&�nMC[�Pb�|L%MP�����9��fL2q���IFmsd|l�K, which won’t do at all. What might be going wrong here?

Asked By: Taymon

||

It’s your locale and tr problem.

Currently, GNU tr fully supports only single-byte characters. So in locales using multibyte encodings, the output can be weird:

$ </dev/urandom LC_ALL=vi_VN.tcvn tr -dc '[:print:]' | head -c 64
`�pv���Z����c�ox"�O���%�YR��F�>��췔��ovȪ������^,<H ���>

The shell will print multi-byte characters correctly, but GNU tr will remove bytes it think non-printable.

If you want it to be stable, you must set the locale:

$ </dev/urandom LC_ALL=C tr -dc '[:print:]' | head -c 64
RSmuiFH+537z+iY4ySz`{Pv6mJg::RB;/-2^{QnKkImpGuMSq92D(6N8QF?Y9Co@
Answered By: cuonglm

What about

tr -dc [:print:] < /dev/urandom | head -c 64 | strings

strings should print the output of urandom in a printable format

Answered By: Blindstealer

Other people already pointed out that locale determines what [:print:] means. However, not all printable characters are suitable for passwords (not even in ascii). You really don’t want spaces, tabs, and #$%^? in your password – it’s not just difficult to remember, it’s also potentially dangerous to the underlying authentication system, may be impossible to enter in an input field, and so on. In this case, you should just manually select “sane” characters:

LC_ALL=C </dev/urandom tr -dc '[:alnum:]_' | head -c 64

or simply

</dev/urandom tr -dc 'A-Za-z0-9_' | head -c 64

Or even better, use base64 as suggested in other answers.

Answered By: orion

Consider instead

$ dd if=/dev/urandom bs=48 count=1 status=none | base64
imW/X60Sk9TQzl+mdS5PL7sfMy9k/qFBWWkzZjbYJttREsYpzIguWr/uRIhyisR7

This has two advantages:

  • You read only 48 bytes from the random device, not ~8KB; if other processes on the same host need random numbers, 8KB drained all at once can be a serious problem. (Yes, arguably nobody should be using the blocking random device, but people do.)

  • The output of base64 contains almost no characters with special meanings. (For none at all, tack | tr +/ -_ on the end, and (as in the example) make sure the number of bytes input to base64 is a multiple of 3.)

A password generated this way has exactly 384 bits of entropy, which is somewhat less than what you were doing (log2 9664 ≈ 421.4), but more than enough for most purposes (256 bits of entropy is safely in “still guessing when the Sun burns out” territory except for RSA keys, AFAIK).

Answered By: zwol

I don’t know if there’s any reason why you use /dev/random to generate the password, but I would recommend you using pwgen in order to ease your pain.

$ pwgen -s 10 1

Where 10 is the length of the password.

http://man.cx/pwgen

Answered By: Daniel
#Chars allowed in password (I don't like l,o,O, etc):
P="0123456789ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz"

#Or such:
#P="a-zA-Z0-9"

head -c 8 < /dev/urandom | tr '00-377' "$P$P$P$P$P"
echo

This method IMHO is smarter when consume data from /dev/urandom
The string pasted as $P$P$P… must be at least 256 chars long.

Answered By: Alexander Kelner