pwgen 随机密码生成工具

缘起

今天在看一个开源项目的时候,启动shell脚本里面因不存在命令 pwgen 而报错,查了下资料,这个命令是用来生成可读的密码。

pwgen - generate pronounceable passwords

深入去思考,使用命令工具生成的密码,应该有一套密码生成的规则,比如是否包含大小写字母,密码的长度是多少,哪些特殊符号可用,密码中必须包含多少大小写等。

具体的规则有了,生成代码逻辑也很容易完成。我们看下 pwgen 的源码,看下他有什么规则以及源码中有什么值得借鉴学习的内容。

pwgen

帮助文档

分析源码前,我们先看下 pwgen 的官方文档,了解他的功能及文档中提及的架构设计。文档地址为: https://linux.die.net/man/1/pwgen,也可以在命令行中执行 man pwgen 查看。

文档描述部分,说了这些(P.S. 老外,在解释概念方面,确实有一套,准确而具体)

  • 能生成容易记忆的密码,也可以生成完全随机但不容易记忆的密码
  • 在交互模式和shell脚本调用时,pwgen 的行为是不同的,交互模式能生成多个密码,shell脚本中,只生成一个密码

命令使用及参数

在命令行执行下运行命令

ubuntu@yun:~$ pwgen -h      # 打印命令的帮助信息
Usage: pwgen [ OPTIONS ] [ pw_length ] [ num_pw ]

Options supported by pwgen:
  -c or --capitalize
        Include at least one capital letter in the password
  -A or --no-capitalize
        Don't include capital letters in the password
  -n or --numerals
        Include at least one number in the password
  -0 or --no-numerals
        Don't include numbers in the password
  -y or --symbols
        Include at least one special symbol in the password
  -s or --secure
        Generate completely random passwords
  -B or --ambiguous
        Don't include ambiguous characters in the password
  -h or --help
        Print a help message
  -H or --sha1=path/to/file[#seed]
        Use sha1 hash of given file as a (not so) random generator
  -C
        Print the generated passwords in columns
  -1
        Don't print the generated passwords in columns
  -v or --no-vowels
        Do not use any vowels so as to avoid accidental nasty words
ubuntu@yun:~$ pwgen     # 生成了 8 * 20 个密码
oat0Ieph oo7Shumo aeBeegh2 oor7muPo pho6ooNg aecuuGh1 ohne2Kef Iec1Shoh
...
ij6guoSh yiCh5Ieg Jee3Aigo aavae8Ra Po5poh3U Ohj3Raiz eegh8aiR Geil4Aab
ubuntu@yun:~$ pwgen -1   # 一个密码
Bohba7Ji
ubuntu@yun:~$ pwgen 12 1  # 生成长度为12,1个密码
aexahque5Ohb

pwgen 的参数有两个,分别控制密码的长度和生成密码的个数

  • pw_length 密码的长度,默认为 8
  • num_pw 生成密码的个数

对于选项,则控制密码中包含那种字符,如何打印等。

控制密码组成的选项

  • -0, --no-numerals 不包含数字
  • -A, --no-capitalize 不包含大写字母
  • -B, --ambiguous 不使用容易混淆,分不清的字母,如数字 1 和 字母 l,这些字母有 B8G6I1l0OQDS5Z2
  • -c, --capitalize 至少包含一个大写字母,这个在 tty 设备是默认的
  • -n, --numerals 至少包含一个数字
  • -s, --secure 生成完全随机,不易记忆的密码
  • -v, --no-vowels 密码中不包含元音,即这些字母 01aeiouyAEIOUY
  • -y, --symbols 至少包含一个特殊符号
  • -H, --sha1=/path/to/file[#seed] 使用指定文件的 sha1 值生成密码

控制输出的选项

  • -1 一行打印一个密码
  • -C 按列打印密码, 在 tty 设备执行该命令,默认选项
  • -N, --num-passwords=num 生成多少个密码,P.S. 参数中有控制密码个数,如果同时出现,以参数为准

下载编译源码

知道如何使用后,我们看下他的源码. pwgen 是用 C 语言编写的,当前开发和维护者为 Theodore Ts'o,在他之前,还有别的开发者。根据作者和程序名称,在 Github 上找到了项目 tytso/pwgen 的源码。

如果使用 Debain 或者 Ubuntu 系统,可以通过如下命令下载源码.

sudo apt-get source pwgen

我下载了 Github 上的 tytso/pwgen,项目中有文件configure.ac缺少 Makefile,需要自己的平台编译,以 Ubuntu 为例,首先下载编译的工具 gccautoconf

 sudo apt install autoconf

然后在源码目录执行命令

ubuntu@yun:~/github/pwgen$ autoscan
ubuntu@yun:~/github/pwgen$ autoconf     # 生成文件  configure
ubuntu@yun:~/github/pwgen$ ls
autom4te.cache  configure     configure.scan  depfix.sed  Makefile.in  pwgen.c  pw_phonemes.c  randnum.c  sha1.h     wordwrap.pl
autoscan.log    configure.ac  debian          install-sh  pwgen.1      pwgen.h  pw_rand.c      sha1.c     sha1num.c
ubuntu@yun:~/github/pwgen$ ./configure  # 生成文件 Makefile
....
ubuntu@yun:~/github/pwgen$ make   # 编译源码
gcc -c  -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE_URL=\"\" -DHAVE_GETOPT_LONG=1 -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_GETOPT_H=1  -g -O2 -Wall -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wwrite-strings -Wpointer-arith -Wcast-qual -Wcast-align -pedantic   pwgen.c -o pwgen.o
...

如果想安装到本机的化,执行 make install 即可.

源码分析

程序的执行示意图如下:

img

生成密码的策略有两种,分别基于

  • 随机密码,即随机的字符,比如大小写,数字,特殊符号等
  • phoneme rules 音素规则,比如元音,辅音

我们重点看下随机密码,随机密码可以使用的字母如下:

// from file pw_rand.c
const char *pw_digits = "0123456789";                   // 数字
const char *pw_uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";   // 大写字母
const char *pw_lowers = "abcdefghijklmnopqrstuvwxyz";   // 小写字母
const char *pw_symbols = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; // 特殊字母
const char *pw_ambiguous = "B8G6I1l0OQDS5Z2";           // 易混淆的字母
const char *pw_vowels = "01aeiouyAEIOUY";               // 元音

组合流程的示意图如下

img

按照程序的输入选项,包含那种字母,然后将可供选择的字母拼成字符串 chars,在组合密码时,先随机产生一个正整数 a(a 的上线为 chars 的长度),字符串 chars 第 a 位的字符作为可供选择的字符,依次选择字符,如果不符合规则,则重新进行选择,只到符合规则为止。

实现中,有调用函数pw_number(int max)产生随机数字,该函数对应的实现为 randnum.c 文件中的函数 pw_random_number(int),文件通过读取系统的文件/dev/urandom(如果失败,则调用/dev/random)前4个字节,获取随机值,然后对最大值进行取余,即得到随机数值。

关于文件 /dev/random,wiki 上有这段描述:

In Unix-like operating systems, /dev/random, /dev/urandom and /dev/arandom are special files that serve as pseudorandom number generators.

/dev/random, /dev/urandom, /dev/arandom 是特殊的文件,可以当作伪随机序列生成器的作用。

你可能感兴趣的:(pwgen 随机密码生成工具)