高品质,简单的随机密码生成器

时间:2011-09-20 02:34:17

标签: python security random passwords

我有兴趣创建一个非常简单,高(加密)质量的随机密码生成器。有更好的方法吗?

import os, random, string

length = 13
chars = string.ascii_letters + string.digits + '!@#$%^&*()'
random.seed = (os.urandom(1024))

print ''.join(random.choice(chars) for i in range(length))

27 个答案:

答案 0 :(得分:45)

密码的难点在于使它们足够强大并且仍然能够记住它们。如果密码不是人类想要记住的,那么它实际上不是密码。

你使用Python的os.urandom():这很好。对于任何实际目的(甚至加密),os.urandom()的输出与真正的alea无法区分。然后你将它用作random中的种子,这不太好:一个是非加密的PRNG,它的输出可能会展示一些不会在统计测量工具中注册的结构,但可能会被智能攻击者。您应该一直使用os.urandom()。为简单起见:选择长度为64的字母表,例如字母(大写和小写),数字和两个额外的标点字符(例如'+'和'/')。然后,对于每个密码字符,从os.urandom()获取一个字节,减少模64的值(这是无偏的,因为64除以256)并将结果用作chars数组中的索引。

使用长度为64的字母表,每个字符得到6位熵(因为2 6 = 64)。因此,对于13个字符,您将获得78位熵。在所有情况下,这并不是最终的强大,但已经非常强大(它可能被预算打败,预算将以数月和数十亿美元计算,而不仅仅是数百万美元)。

答案 1 :(得分:38)

XKCD可以很好地解释为什么您认为是强密码

http://xkcd.com/936/

  

对任何了解信息理论和安全并且处于信息理论中的人   与没有的人(可能涉及的人)激起争论   混合情况),我真诚地道歉。 - Randall Munroe

如果您不理解math behind what this illustration is explaining,请不要尝试编写任何应该加密安全的内容,因为它不会。只需将鼠标放下并远离键盘即可。

答案 2 :(得分:13)

就在两天前,Kragen Javier Sitaker在http://lists.canonical.org/pipermail/kragen-hacks/2011-September/000527.html发布了一个程序来实现这一目标(现在不见了 - 试试https://github.com/jesterpm/bin/blob/master/mkpasswd

生成一个随机的,可记忆的密码:http://xkcd.com/936/

示例运行:

kragen无情:〜/ devel / inexorable-misc $ ./mkpass.py 5 12您的密码是“学习损坏已保存的住宅阶段”。这相当于一个60位密钥。

假设对MS-Cache哈希进行离线攻击,该密码需要2.5e + 03 CPU年才能破解我的便宜Celeron E1200,这是常用的最差密码哈希算法,比偶数差一点简单的MD5。

目前最常见的密码散列算法是FreeBSD的迭代MD5;破解这样的哈希需要5.2e + 06 CPU-years。

但现代GPU可以快速破解250倍,因此相同的迭代MD5将落在2e + 04 GPU年。

2011年,GPU的运行成本约为每天1.45美元,因此破解密码的成本约为3美元+ 09美元。

我开始使用以这种方式生成的密码代替9-printable- ASCII字符随机密码,这同样强大。 Munroe断言这些密码更容易记忆是正确的。然而,仍然存在一个问题:因为每个字符的熵比例少很多(大约1.7而不是6.6),密码中存在大量冗余,因此诸如ssh定时信道攻击(Song, Wagner和Tian Herbivore的攻击,我在凌晨一年多的凌晨时分在BagdadCafé中从Bram Cohen那里学到了很多东西)和键盘录音攻击有更好的机会获取足够的信息以使密码可攻击。

我对草食动物攻击的对策,它与9个字符的密码配合得很好但是我的新密码非常烦人,就是输入字符之间延迟半秒的密码,这样定时通道就不会带来很多有关所用实际字符的信息。此外,9个字符密码的较低长度固有地为草食动物提供了更少的信息来咀嚼。

其他可能的对策包括使用Emacs shell-mode,当它识别出密码提示时会在本地提示您输入密码,然后立即发送整个密码,并从其他地方复制并粘贴密码。

正如您所期望的那样,输入此密码还需要一段时间:大约6秒而不是大约3秒。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import random, itertools, os, sys

def main(argv):
    try:
        nwords = int(argv[1])
    except IndexError:
        return usage(argv[0])

    try:
        nbits = int(argv[2])
    except IndexError:
        nbits = 11

    filename = os.path.join(os.environ['HOME'], 'devel', 'wordlist')
    wordlist = read_file(filename, nbits)
    if len(wordlist) != 2**nbits:
        sys.stderr.write("%r contains only %d words, not %d.\n" %
                         (filename, len(wordlist), 2**nbits))
        return 2

    display_password(generate_password(nwords, wordlist), nwords, nbits)
    return 0

def usage(argv0):
    p = sys.stderr.write
    p("Usage: %s nwords [nbits]\n" % argv0)
    p("Generates a password of nwords words, each with nbits bits\n")
    p("of entropy, choosing words from the first entries in\n")
    p("$HOME/devel/wordlist, which should be in the same format as\n")
    p("<http://canonical.org/~kragen/sw/wordlist>, which is a text file\n")
    p("with one word per line, preceded by its frequency, most frequent\n")
    p("words first.\n")
    p("\nRecommended:\n")
    p("    %s 5 12\n" % argv0)
    p("    %s 6\n" % argv0)
    return 1

def read_file(filename, nbits):
    return [line.split()[1] for line in
            itertools.islice(open(filename), 2**nbits)]

def generate_password(nwords, wordlist):
    choice = random.SystemRandom().choice
    return ' '.join(choice(wordlist) for ii in range(nwords))

def display_password(password, nwords, nbits):
    print 'Your password is "%s".' % password
    entropy = nwords * nbits
    print "That's equivalent to a %d-bit key." % entropy
    print

    # My Celeron E1200
    # (<http://ark.intel.com/products/34440/Intel-Celeron-Processor-E1200-(512K-Cache-1_60-GHz-800-MHz-FSB)>)
    # was released on January 20, 2008.  Running it in 32-bit mode,
    # john --test (<http://www.openwall.com/john/>) reports that it
    # can do 7303000 MD5 operations per second, but I’m pretty sure
    # that’s a single-core number (I don’t think John is
    # multithreaded) on a dual-core processor.
    t = years(entropy, 7303000 * 2)
    print "That password would take %.2g CPU-years to crack" % t
    print "on my inexpensive Celeron E1200 from 2008,"
    print "assuming an offline attack on a MS-Cache hash,"
    print "which is the worst password hashing algorithm in common use,"
    print "slightly worse than even simple MD5."
    print

    t = years(entropy, 3539 * 2)
    print "The most common password-hashing algorithm these days is FreeBSD’s"
    print "iterated MD5; cracking such a hash would take %.2g CPU-years." % t
    print

    # (As it happens, my own machines use Drepper’s SHA-2-based
    # hashing algorithm that was developed to replace the one
    # mentioned above; I am assuming that it’s at least as slow as the
    # MD5-crypt.)

    # <https://en.bitcoin.it/wiki/Mining_hardware_comparison> says a
    # Core 2 Duo U7600 can do 1.1 Mhash/s (of Bitcoin) at a 1.2GHz
    # clock with one thread.  The Celeron in my machine that I
    # benchmarked is basically a Core 2 Duo with a smaller cache, so
    # I’m going to assume that it could probably do about 1.5Mhash/s.
    # All common password-hashing algorithms (the ones mentioned
    # above, the others implemented in John, and bcrypt, but not
    # scrypt) use very little memory and, I believe, should scale on
    # GPUs comparably to the SHA-256 used in Bitcoin.

    # The same mining-hardware comparison says a Radeon 5870 card can
    # do 393.46 Mhash/s for US$350.

    print "But a modern GPU can crack about 250 times as fast,"
    print "so that same iterated MD5 would fall in %.1g GPU-years." % (t / 250)
    print

    # Suppose we depreciate the video card by Moore’s law,
    # i.e. halving in value every 18 months.  That's a loss of about
    # 0.13% in value every day; at US$350, that’s about 44¢ per day,
    # or US$160 per GPU-year.  If someone wanted your password as
    # quickly as possible, they could distribute the cracking job
    # across a network of millions of these cards.  The cards
    # additionally use about 200 watts of power, which at 16¢/kWh
    # works out to 77¢ per day.  If we assume an additional 20%
    # overhead, that’s US$1.45/day or US$529/GPU-year.
    cost_per_day = 1.45
    cost_per_crack = cost_per_day * 365 * t
    print "That GPU costs about US$%.2f per day to run in 2011," % cost_per_day
    print "so cracking the password would cost about US$%.1g." % cost_per_crack

def years(entropy, crypts_per_second):
    return float(2**entropy) / crypts_per_second / 86400 / 365.2422

if __name__ == '__main__':
    sys.exit(main(sys.argv))

答案 3 :(得分:10)

实施@Thomas Pornin解决方案

import M2Crypto
import string

def random_password(length=10):
    chars = string.ascii_uppercase + string.digits + string.ascii_lowercase
    password = ''
    for i in range(length):
        password += chars[ord(M2Crypto.m2.rand_bytes(1)) % len(chars)]
    return password

答案 4 :(得分:7)

我知道这个问题是在2011年发布的,但是对于那些现在在2014年及以后的人来说,我有一件事要说:抵制重建轮子的危险。

在这些情况下,最好的办法是搜索开源软件,例如,将搜索限制为github结果。到目前为止,我发现了最好的东西:

<强> https://github.com/redacted/XKCD-password-generator

答案 5 :(得分:7)

XKCD方法的另一个实现:

#!/usr/bin/env python
import random
import re

# apt-get install wbritish
def randomWords(num, dictionary="/usr/share/dict/british-english"):
  r = random.SystemRandom() # i.e. preferably not pseudo-random
  f = open(dictionary, "r")
  count = 0
  chosen = []
  for i in range(num):
    chosen.append("")
  prog = re.compile("^[a-z]{5,9}$") # reasonable length, no proper nouns
  if(f):
    for word in f:
      if(prog.match(word)):
        for i in range(num): # generate all words in one pass thru file
          if(r.randint(0,count) == 0): 
            chosen[i] = word.strip()
        count += 1
  return(chosen)

def genPassword(num=4):
  return(" ".join(randomWords(num)))

if(__name__ == "__main__"):
  print genPassword()

示例输出:

$ ./randompassword.py
affluent afford scarlets twines
$ ./randompassword.py
speedboat ellipse further staffer

答案 6 :(得分:4)

生成密码时,您无法信任python的伪随机数生成器。它不一定是加密随机的。您正在从os.urandom播种伪随机数生成器,这是一个良好的开端。但是之后你依赖python的生成器。

更好的选择是random.SystemRandom()类,它从urandom获取来自同一来源的随机数。根据python文档应该足够好用于加密使用。 SystemRandom类为您提供主要随机类所做的所有操作,但您不必担心伪随机性。

使用random.SystemRandom(适用于Python 2.6)的示例代码:

import random, string
length = 13
chars = string.ascii_letters + string.digits + '!@#$%^&*()'

rnd = random.SystemRandom()
print ''.join(rnd.choice(chars) for i in range(length))

注意:您的里程可能会有所不同 - Python文档说random.SystemRandom可用性因操作系统而异。

答案 7 :(得分:3)

在2020年后遇到此问题的任何人仅供参考。 Python 3.6+具有专门用于此目的的secrets模块:

import secrets

password_length = 13
print(secrets.token_urlsafe(password_length))

答案 8 :(得分:3)

考虑您的评论,

  

我只需要能够生成比我脑子里想的更安全的密码。

似乎您希望使用您的程序生成密码,而不是仅仅将其编写为练习。最好使用现有的实现,因为如果你犯了错误,输出可能会受到损害。阅读random number generator attacks;特别是,Debian中一个众所周知的RNG漏洞暴露了人们的SSL私钥。

相反,请考虑使用pwgen。它提供了几个选项,您应该根据计划使用密码的内容进行选择。

答案 9 :(得分:2)

实施@Thomas Pornin解决方案:(无法评论@Yossi不准确答案)

import string, os
chars = string.letters + string.digits + '+/'
assert 256 % len(chars) == 0  # non-biased later modulo
PWD_LEN = 16
print ''.join(chars[ord(c) % len(chars)] for c in os.urandom(PWD_LEN))

答案 10 :(得分:2)

import random


r = random.SystemRandom()


def generate_password(words, top=2000, k=4, numbers=None, characters=None,
                      first_upper=True):
    """Return a random password based on a sorted word list."""
    elements = r.sample(words[:top], k)

    if numbers:
        elements.insert(r.randint(1, len(elements)), r.choice(numbers))
    if characters:
        elements.insert(r.randint(1, len(elements)), r.choice(characters))
    if first_upper:
        elements[0] = elements[0].title()

    return ''.join(elements)


if __name__ == '__main__':
    with open('./google-10000-english-usa.txt') as f:
        words = [w.strip() for w in f]
    print(generate_password(words, numbers='0123456789', characters='!@#$%'))
  • 生成您能记住的密码
  • 使用os.urandom()
  • 处理现实世界的规则,例如添加数字,大写,字符。

当然可以改进,但这就是我使用的。

答案 11 :(得分:1)

构建我自己的CLI回答当前主题(以下URL的完整源代码):

http://0netenv.blogspot.com/2016/08/password-generator-with-argparse.html

使用argparse写了一个密码生成器。 希望这有助于某人(建立密码生成器或使用argparse)!

无论哪种方式,构建都很有趣!

$ ./pwgen.py -h
usage: pwgen.py [-h] [-c COUNT] [-a] [-l] [-n] [-s] [-u] [-p]

 Create a random password
 Special characters, numbers, UPPERCASE -"Oscar",
 and lowercase -"lima" to avoid confusion.
 Default options (no arguments): -c 16 -a
                Enjoy! --0NetEnv@gmail.com

optional arguments:
  -h, --help            show this help message and exit
  -c COUNT, --count COUNT
                        password length
  -a, --all             same as -l -n -s -u
  -l, --lower           include lowercase characters
  -n, --number          include 0-9
  -s, --special         include special characters
  -u, --upper           include uppercase characters
  -p, --license         print license and exit

以下是代码:

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

license = """
#  pwgen -- the pseudo-random password generator 
#
#  This software is distributed under the MIT license.
#    
#  The MIT License (MIT)
#
#  Copyright (c) 2016 0NetEnv 0netenv@gmail.com
#  Permission is hereby granted, free of charge, to any 
#  person obtaining a copy of this software and associated 
#  documentation files (the "Software"), to deal in the 
#  Software without restriction, including without 
#  limitation the rights to use, copy, modify, merge, 
#  publish, distribute, sublicense, and/or sell copies 
#  of the Software, and to permit persons to whom the 
#  Software is furnished to do so, subject to the following 
#  conditions:
#
#  The above copyright notice and this permission notice 
#  shall be included in all copies or substantial portions 
#  of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 
#  ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
#  TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
#  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 
#  SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
#  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
#  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 
#  IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
#  DEALINGS IN THE SOFTWARE.
#  
#  NOTE:
#  This software was tested on Slackware 14.2, Raspbian, & 
#  Mac OS X 10.11
#
"""

import string
import random
import sys
# first time using argparse library
import argparse
# wanted to change the formatting of the help menu a little bit, so used RawTextHelpFormatter directly
from argparse import RawTextHelpFormatter

typo = ''
c = 16
counter = 0
line = '-' * 40

# CREATE FUNCTION for PWGEN
def pwgen(z, t):
    # EMPTY SET OF CHARACTERS
    charsset = ''
    # UPPERCASE -"O"
    U = 'ABCDEFGHIJKLMNPQRSTUVWXYZ'
    # lowercase -"l"
    L = 'abcdefghijkmnopqrstuvwxyz'
    N = '0123456789'
    S = '!@#$%^&*?<>'

    # make sure we're using an integer, not a char/string
    z = int(z)
    for type in t:
        if 'u' in t:
            charsset = charsset + U
        if 'l' in t:
            charsset = charsset + L
        if 'n' in t:
            charsset = charsset + N
        if 's' in t:
            charsset = charsset + S
        if 'a' == t:
            charsset = charsset + U + L + N + S

    return ''.join(random.choice(charsset) for _ in range(0, int(z)))

# GET ARGUMENTS using ARGPARSE
parser = argparse.ArgumentParser(description='\n Create a random password\n\
 Special characters, numbers, UPPERCASE -"Oscar",\n\
 and lowercase -"lima" to avoid confusion.\n\
 Default options (no arguments): -c 16 -a\n\
 \t\tEnjoy! --0NetEnv@gmail.com', formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("-c", "--count", dest="count", action="store", help="password length")
parser.add_argument("-a", "--all", help="same as -l -n -s -u", action="store_true")
parser.add_argument("-l", "--lower", help="include lowercase characters", action="store_true")
parser.add_argument("-n", "--number", help="include 0-9", action="store_true")
parser.add_argument("-s", "--special", help="include special characters", action="store_true")
parser.add_argument("-u", "--upper", help="include uppercase characters", action="store_true")
parser.add_argument("-p", "--license", help="print license and exit", action="store_true")

# COLLECT ARGPARSE RESULTS
results = args = parser.parse_args()

# CHECK RESULTS
# Check that a length was given.
# If not, gripe and exit.
if args.count == '0':
    print ("Input error:\nCannot create a zero length password.\nExiting")
    exit (0)
# check character results and add to counter if 
# selection is made.
if args.lower:
    typo = typo + 'l'
    counter = counter + 1
    #print "lower"
if args.number:
    typo = typo + 'n'
    counter = counter + 1
    #print "number"
if args.special:
    typo = typo + 's'
    counter = counter + 1
    #print "special"
if args.upper:
    typo = typo + 'u'
    counter = counter + 1
    #print "upper"
if args.all:
    typo = 'a'
    counter = counter + 1
    #print "all"
if args.license:
    print (license)
    exit (1)

# CHECK COUNTER
# Check our counter and see if we used any command line 
# options. We don't want to error out.
# try it gracefully. If no arguments are given, 
# use defaults and tell the user.
# args.count comes from argparse and by default requires
# an input to '-c'. We want to get around that for the 
# sake of convenience.
# Without further adieu, here's our if statement:
if args.count:
    if counter == 0:
        typo = 'a'
        print ("defaulting to '--all'")
    print (line)
    print (pwgen(results.count,typo))
else:
    if counter == 0:
        typo = 'a'
        print ("defaulting to '--count 16 --all'")
    print (line)
    print (pwgen(c,typo))
print (line)
#print typo

答案 12 :(得分:1)

这种方式有效。这很好。如果您有其他规则,例如排除字典单词,那么您可能也想要包含这些过滤器,但是使用该设置随机生成字典单词的可能性非常小。

答案 13 :(得分:1)

我喜欢语言学,在我的方法中,我通过交替辅音创建具有高水平熵的令人难忘的伪词。元音。

  • 不易受字典攻击
  • 发音,因此很有机会成为 难忘
  • 具有良好实力的短密码
  • 可选参数,用于添加随机数字以实现兼容性(不太令人难忘,但符合使用旧密码安全思维构建的应用程序,例如需要数字)

Python代码:

import random
import string


def make_pseudo_word(syllables=5, add_number=False):
    """Create decent memorable passwords.

    Alternate random consonants & vowels
    """
    rnd = random.SystemRandom()
    s = string.ascii_lowercase
    vowels = 'aeiou'
    consonants = ''.join([x for x in s if x not in vowels])
    pwd = ''.join([rnd.choice(consonants) + rnd.choice(vowels)
               for x in range(syllables)]).title()
    if add_number:
        pwd += str(rnd.choice(range(10)))
    return pwd


>>> make_pseudo_word(syllables=5)
'Bidedatuci'
>>> make_pseudo_word(syllables=5)
'Fobumehura'
>>> make_pseudo_word(syllables=5)
'Seganiwasi'
>>> make_pseudo_word(syllables=4)
'Dokibiqa'
>>> make_pseudo_word(syllables=4)
'Lapoxuho'
>>> make_pseudo_word(syllables=4)
'Qodepira'
>>> make_pseudo_word(syllables=3)
'Minavo'
>>> make_pseudo_word(syllables=3)
'Fiqone'
>>> make_pseudo_word(syllables=3)
'Wiwohi'

缺点:

  • 适用于拉丁语和日耳曼语的人和熟悉英语的人
  • 应该使用主要与应用程序用户或焦点小组进行调整的语言的元音和辅音

答案 14 :(得分:1)

很容易:)

def codegenerator():
    alphabet = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    pw_length = 8
    mypw = ""

    for i in range(pw_length):
        next_index = random.randrange(len(alphabet))
        mypw = mypw + alphabet[next_index]
    return mypw

和do:

print codegenerator()

感谢http://xkcd.com/936/

答案 15 :(得分:1)

您的实施存在一些问题:

random.seed = (os.urandom(1024))

这不会使随机数生成器播种;它用bytestring替换seed函数。您需要致电seed,例如random.seed(…)

print ''.join(random.choice(chars) for i in range(length))

Python的默认PRNG是Mersenne Twister,它不是加密强PRNG,因此我担心将其用于加密目的。 random模块包括random.SystemRandom,至少在大多数* nix系统上,应使用CSPRNG。 然而

random.choice(chars)

...实现为......

def choice(self, seq):
    """Choose a random element from a non-empty sequence."""
    return seq[int(self.random() * len(seq))]  # raises IndexError if seq is empty

...在 Python 2 中。不幸的是,self.random这里是一个C函数,所以很难看到;这里的代码味道是这个代码几乎肯定不会统一选择。代码在Python 3中已完全改变,并且在确保一致性方面做得更好。适用于randrange的Python 3文档,

  

在版本3.2中更改:randrange()对于生成分布均匀的值更为复杂。以前它使用类似int(random()*n)的样式,可能会产生稍微不均匀的分布。

randrangechoice都会在引擎盖下调用相同的方法(_randbelow)。

在Python 3中,choice很好;在Python 2中,它只是 close 到统一分布,但不保证它。由于这是加密,我依靠&#34;不要冒险&#34;围栏的一侧,并希望有这种保证。

答案 16 :(得分:0)

有点偏离主题,但我做了这个,也使用TKinter。希望它有所帮助:

import os, random, string
from tkinter import *

def createPwd():
    try:
        length = int(e1.get())
    except ValueError:
        return
    chars = string.ascii_letters + string.digits + '!@#$%^&*()?\/'
    random.seed = (os.urandom(1024))
    e2.config(state=NORMAL)
    e2.delete(0,'end')
    e2.insert(0,''.join(random.choice(chars) for i in range(length)))
    e2.config(state="readonly")

mainWindow = Tk()
mainWindow.title('Password generator')

mainWindow.resizable(0,0)

f0 = Frame(mainWindow)

f0.pack(side=TOP,pady=5,padx=5,fill=X,expand=1)

Label(f0,text="Length: ",anchor=E).grid(row=0,column=0,sticky=E)

e1 = Entry(f0)
e1.insert(0,'12')
e1.grid(row=0,column=1)

btn = Button(f0,text="Generate")
btn['command'] = lambda: createPwd()
btn.grid(row=0,column=2,rowspan=1,padx=10,ipadx=10)

Label(f0,text="Generated password: ",anchor=E).grid(row=1,column=0,sticky=E)
e2 = Entry(f0)
e2.grid(row=1,column=1)

createPwd()

#starting main window
mainWindow.mainloop()

答案 17 :(得分:0)

Base64让我们以人类可读/可写模式对二进制数据进行编码,而不会丢失数据。

import os
random_bytes=os.urandom(12)
secret=random_bytes.encode("base64")

答案 18 :(得分:0)

这是一个简单的小程序,针对那些无法为自己的公共帐户找出安全密码的人。

只需在命令控制台上运行程序并传入一堆看起来很熟悉的字母,它将根据您插入的内容生成一系列符号。

当然,该程序不支持多序列生成。

您可以从我的github pull下载代码:https://github.com/abdechahidely/python_password_generator

from string import ascii_lowercase, ascii_uppercase, digits, punctuation
from random import randint, choice, shuffle
from math   import ceil
from re     import finditer

lower_cases  = ascii_lowercase
upper_cases  = ascii_uppercase
lower_upper  = dict(zip(lower_cases, upper_cases))
upper_lower  = dict(zip(upper_cases, lower_cases))
punctuations = '#$%&@!?.'
space        = ' '

class PunctOrDigit():

    def __init__(self, number_of_punctuations, number_of_digits):
        self.puncts = number_of_punctuations
        self.digits = number_of_digits
        self.dupl_puncts = self.puncts
        self.dupl_digits = self.digits

    def PorD(self):
        symbol_type = choice('pd')
        if symbol_type == 'p':
            if self.puncts == 0:
                return 'd'
            else:
                self.puncts -= 1
                return symbol_type
        if symbol_type == 'd':
            if self.digits == 0:
                return 'p'
            else:
                self.digits -= 1
                return symbol_type

    def reset(self):
        self.puncts = self.dupl_puncts
        self.digits = self.dupl_digits

def is_empty(text):
    for symbol in text:
        if symbol != space:
            return False
    return True

def contain_unauthorized_symbols(text):
    for symbol in text:
        if symbol in punctuation or symbol in digits:
            return True
    return False

def user_input():
    user_input = input('-- Sentence to transform: ')
    while is_empty(user_input) or len(user_input) < 8 or contain_unauthorized_symbols(user_input):
        user_input = input('-- Sentence to transform: ')
    return user_input

def number_of_punctuations(text):
    return ceil(len(text) / 2) - 3

def number_of_digits(text):
    return ceil(len(text) / 2) - 2

def total_symbols(text):
    return (number_of_digits(text) + number_of_punctuations(text), 
            number_of_punctuations(text),
            number_of_digits(text))

def positions_to_change(text):
    pos_objct = PunctOrDigit(number_of_punctuations(text), number_of_digits(text))
    positions = {}
    while len(positions) < total_symbols(text)[0]:
        i = randint(0,len(text)-1)
        while i in positions:
            i = randint(0,len(text)-1)
        positions[i] = pos_objct.PorD()
    pos_objct.reset()
    return positions

def random_switch(letter):
    if letter in lower_cases:
        switch_or_pass = choice('sp')
        if switch_or_pass == 's': return lower_upper[letter]
        else:                     return letter
    if letter in upper_cases:
        switch_or_pass = choice('sp')
        if switch_or_pass == 's': return upper_lower[letter]
        else:                     return letter

def repeated(text):
    reps = {}
    for letter in set(list(text)):
        indexs = [w.start() for w in finditer(letter, text)]
        if letter != ' ':
            if len(indexs) != 1:
                reps[letter] = indexs
    return reps

def not_repeated(text):
    reps = {}
    for letter in set(list(text)):
        indexs = [w.start() for w in finditer(letter, text)]
        if letter != ' ':
            if len(indexs) == 1:
                reps[letter] = indexs
    return reps

def generator(text, positions_to_change):
    rep     = repeated(text)
    not_rep = not_repeated(text)
    text    = list(text)

    for x in text:
        x_pos = text.index(x)
        if x not in positions_to_change:
            text[x_pos] = random_switch(x)

    for x in rep:
        for pos in rep[x]:
            if pos in positions_to_change:
                if positions_to_change[pos] == 'p':
                    shuffle(list(punctuations))
                    text[pos] = choice(punctuations)
                if positions_to_change[pos] == 'd':
                    shuffle(list(digits))
                    text[pos] = choice(digits)
    for x in not_rep:
        for pos in not_rep[x]:
            if pos in positions_to_change:
                if positions_to_change[pos] == 'p':
                    shuffle(list(punctuations))
                    text[pos] = choice(punctuations)
                if positions_to_change[pos] == 'd':
                    shuffle(list(digits))
                    text[pos] = choice(digits)

    text = ''.join(text)
    return text

if __name__ == '__main__':
    x = user_input()
    print(generator(x, positions_to_change(x)))

答案 19 :(得分:0)

以下是我研究此主题后的随机密码生成器:

`import os, random, string
   #Generate Random Password
   UPP = random.SystemRandom().choice(string.ascii_uppercase)
   LOW1 = random.SystemRandom().choice(string.ascii_lowercase)
   LOW2 = random.SystemRandom().choice(string.ascii_lowercase)
   LOW3 = random.SystemRandom().choice(string.ascii_lowercase)
   DIG1 = random.SystemRandom().choice(string.digits)
   DIG2 = random.SystemRandom().choice(string.digits)
   DIG3 = random.SystemRandom().choice(string.digits)
   SPEC = random.SystemRandom().choice('!@#$%^&*()')
   PWD = None
   PWD = UPP + LOW1 + LOW2 + LOW3 + DIG1 + DIG2 + DIG3 + SPEC
   PWD = ''.join(random.sample(PWD,len(PWD)))
   print(PWD)`

这将生成一个随机密码,其中包含1个随机大写字母,3个随机小写字母,3个随机数字和1个随机特殊字符 - 可以根据需要进行调整。然后它组合每个随机字符并创建随机顺序。我不知道这是否被认为是“高质量”,但它完成了工作。

答案 20 :(得分:0)

import uuid
print('Your new password is: {0}').format(uuid.uuid4())

答案 21 :(得分:0)

我的解决方案基于@Thomas Pornin的回答(更新)

import os, string

def get_pass(password_len=12):
  new_password=None
  symbols='+!'
  chars=string.ascii_lowercase+\
        string.ascii_uppercase+\
        string.digits+\
        symbols

  while new_password is None or \
        new_password[0] in string.digits or \
        new_password[0] in symbols:
     new_password=''.join([chars[ord(os.urandom(1)) % len(chars)] \
                             for i in range(password_len)])
  return new_password

print(get_pass())

此函数返回一个随机密码(密码开头没有数字或符号)。

答案 22 :(得分:0)

我最近才开始学习python,这是我今天写的。希望这会有所帮助。

import random

characters = 'abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^()}{/<>'
print('Password Length: ')
passwordLength = int(input())
password = ''

for i in range(passwordLength):
    password += random.choice(characters)
print(password)

答案 23 :(得分:0)

import serial
serial.__file__

此代码也是CodeH测验的答案(如果有)!!!

答案 24 :(得分:0)

这是另一个实现(python 2;需要一些小的重写以使其在3中工作),这比OJW快得多,它似乎在每个单词的字典中循环,尽管相反的评论/含义。 OJW脚本在我的机器上的时间,带有80,000 IOP SSD:

real    0m3.264s
user    0m1.768s
sys     0m1.444s

以下脚本将整个字典加载到列表中,然后根据随机选择的索引值选择单词,使用OJW的正则表达式进行过滤。

这也会生成10个密码短语集,允许传递命令行参数来调整单词数,并添加数字和符号填充(也可调整长度)。

此脚本的采样时间:

real    0m0.289s
user    0m0.176s
sys     0m0.108s

用法:xkcdpass-mod.py 2 4(例如;这些是默认值)。

它在输出中打印空格以便于阅读,虽然我几乎从未遇到允许使用它们的在线服务,所以我会忽略它们。这肯定可以用argparse或getopt清理,并允许开关包含或不包括空格,包括/不包括符号,大写字母等,再加上一些额外的重构,但我还没有达到这个目的。所以,不用多说:

#!/usr/bin/env python
#Copyright AMH, 2013; dedicated to public domain.
import os, re, sys, random
from sys import argv

def getargs():
    if len(argv) == 3:
        numwords = argv[1]
        numpads = argv[2]
        return(numwords, numpads)
    elif len(argv) == 2:
        numwords = argv[1]
        numpads = 4
        return (numwords, numpads)
    else:
        numwords = 2
        numpads = 4
        return (numwords, numpads)

def dicopen(dictionary="/usr/share/dict/american-english"):
    f = open(dictionary, "r")
    dic = f.readlines()
    return dic

def genPassword(numwords, numpads):
    r = random.SystemRandom()
    pads = '0123456789!@#$%^&*()'
    padding = []
    words = dicopen()
    wordlist = []
    for i in range (0,int(numpads)):
        padding.append(pads[r.randint(0,len(pads)-1)])
    #initialize counter for only adding filtered words to passphrase
    j = 0
    while (j < int(numwords)):
        inclusion_criteria = re.compile('^[a-z]{5,10}$')
        #Select a random number, then pull the word at that index value, rather than looping through the dictionary for each word
        current_word = words[r.randint(0,len(words)-1)].strip()
        #Only append matching words
        if inclusion_criteria.match(current_word):
            wordlist.append(current_word)
            j += 1
        else:
        #Ignore non-matching words
            pass
    return(" ".join(wordlist)+' '+''.join(padding))

if(__name__ == "__main__"):
    for i in range (1,11):
       print "item "+str(i)+"\n"+genPassword(getargs()[0], getargs()[1])

示例输出:

[✗]─[user@machine]─[~/bin]
└──╼ xkcdpass-mod.py
item 1
digress basketball )%^)
item 2
graves giant &118
item 3
impelled maniacs ^@%1

选择完整的“正确的马电池主食”(CHBS),没有填充:

┌─[user@machine]─[~/bin]
└──╼ xkcdpass-mod.py 4 0
item 1
superseded warred nighthawk rotary 
item 2
idealize chirruping gabbing vegan 
item 3
wriggling contestant hiccoughs instanced 

根据https://www.grc.com/haystack.htm,出于所有实际目的,假设每秒100万亿次猜测(即100 TH / s),较短的版本将花费大约5千万至6千万个世纪来破解;完整的CHBS = 1.24万亿亿个世纪;增加填充量,15.51万亿亿亿个世纪。

即使参与整个比特币采矿网络(截至撰写本文时约为2500 TH / s),短版本仍可能需要2.5至3亿年才能破解,这对于大多数用途而言可能足够安全。

答案 25 :(得分:-1)

是的,没有哪个黑客会破解该密码。现在,在此之后,我建议继续您的随机密码生成器项目,并使用Tkinter或Flask创建UI或GUI界面,以便其他人可以使用它。例如,我只是通过搜索“密码生成器python UI”找到了这个不错的小项目。 https://passwordgenerator.pythonanywhere.com/

也许您想做类似上面的事情?了解如何将python实施到Web开发中是一项很好的技能。

祝你好运。

和平

答案 26 :(得分:-3)

这比任何事都更有趣。 在passwordmeter.com中得分很高,但不可能记住。

#!/usr/bin/ruby

puts (33..126).map{|x| ('a'..'z').include?(x.chr.downcase) ?
                       (0..9).to_a.shuffle[0].to_s + x.chr :
                       x.chr}.uniq.shuffle[0..41].join[0..41]