65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 密码生成器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.58/5 (28投票s)

2002年6月6日

3分钟阅读

viewsIcon

460549

downloadIcon

11107

一篇关于在 C# 中实现简单密码生成器类的文章

引言

这篇文章演示了如何使用 C# 创建一个非常简单的密码生成器。密码生成器在许多应用程序中都很有用

  • 网站的注册/会员系统
  • 根据指定的规则自动创建密码
  • 保护特定于应用程序的数据

PasswordGenerator 类相当简单。它公开了几个属性来控制密码的生成方式。

  • Exclusions: 指定在密码生成中要排除的字符集。
  • Minimum: 指定生成的密码的最小长度。
  • Maximum: 指定生成的密码的最大长度。
  • ConsecutiveCharacters: 控制在生成的密码中生成连续的字符。
  • RepeatingCharacters: 控制在生成的密码中生成重复的字符。
  • ExcludeSymbols: 从用于生成密码的字符集中排除符号

设置好您想要的属性后,调用 Generate() 方法来创建您的新密码。

namespace WorkingCode.CodeProject.PwdGen
{
    using System;
    using System.Security.Cryptography;
    using System.Text;

    public class PasswordGenerator
    {
        public PasswordGenerator() 
        {
            this.Minimum               = DefaultMinimum;
            this.Maximum               = DefaultMaximum;
            this.ConsecutiveCharacters = false;
            this.RepeatCharacters      = true;
            this.ExcludeSymbols        = false;
            this.Exclusions            = null;

            rng = new RNGCryptoServiceProvider();
        }		
		
        protected int GetCryptographicRandomNumber(int lBound, int uBound)
        {   
            // Assumes lBound >= 0 && lBound < uBound
            // returns an int >= lBound and < uBound
            uint urndnum;   
            byte[] rndnum = new Byte[4];   
            if (lBound == uBound-1)  
            {
                // test for degenerate case where only lBound can be returned
                return lBound;
            }
                                                              
            uint xcludeRndBase = (uint.MaxValue -
                (uint.MaxValue%(uint)(uBound-lBound)));   
            
            do 
            {      
                rng.GetBytes(rndnum);      
                urndnum = System.BitConverter.ToUInt32(rndnum,0);      
            } while (urndnum >= xcludeRndBase);   
            
            return (int)(urndnum % (uBound-lBound)) + lBound;
        }

        protected char GetRandomCharacter()
        {            
            int upperBound = pwdCharArray.GetUpperBound(0);

            if ( true == this.ExcludeSymbols )
            {
                upperBound = PasswordGenerator.UBoundDigit;
            }

            int randomCharPosition = GetCryptographicRandomNumber(
                pwdCharArray.GetLowerBound(0), upperBound);

            char randomChar = pwdCharArray[randomCharPosition];

            return randomChar;
        }
        
        public string Generate()
        {
            // Pick random length between minimum and maximum   
            int pwdLength = GetCryptographicRandomNumber(this.Minimum,
                this.Maximum);

            StringBuilder pwdBuffer = new StringBuilder();
            pwdBuffer.Capacity = this.Maximum;

            // Generate random characters
            char lastCharacter, nextCharacter;

            // Initial dummy character flag
            lastCharacter = nextCharacter = '\n';

            for ( int i = 0; i < pwdLength; i++ )
            {
                nextCharacter = GetRandomCharacter();

                if ( false == this.ConsecutiveCharacters )
                {
                    while ( lastCharacter == nextCharacter )
                    {
                        nextCharacter = GetRandomCharacter();
                    }
                }

                if ( false == this.RepeatCharacters )
                {
                    string temp = pwdBuffer.ToString();
                    int duplicateIndex = temp.IndexOf(nextCharacter);
                    while ( -1 != duplicateIndex )
                    {
                        nextCharacter = GetRandomCharacter();
                        duplicateIndex = temp.IndexOf(nextCharacter);
                    }
                }

                if ( ( null != this.Exclusions ) )
                {
                    while ( -1 != this.Exclusions.IndexOf(nextCharacter) )
                    {
                        nextCharacter = GetRandomCharacter();
                    }
                }

                pwdBuffer.Append(nextCharacter);
                lastCharacter = nextCharacter;
            }

            if ( null != pwdBuffer )
            {
                return pwdBuffer.ToString();
            }
            else
            {
                return String.Empty;
            }	
        }
            
        public string Exclusions
        {
            get { return this.exclusionSet;  }
            set { this.exclusionSet = value; }
        }

        public int Minimum
        {
            get { return this.minSize; }
            set	
            { 
                this.minSize = value;
                if ( PasswordGenerator.DefaultMinimum > this.minSize )
                {
                    this.minSize = PasswordGenerator.DefaultMinimum;
                }
            }
        }

        public int Maximum
        {
            get { return this.maxSize; }
            set	
            { 
                this.maxSize = value;
                if ( this.minSize >= this.maxSize )
                {
                    this.maxSize = PasswordGenerator.DefaultMaximum;
                }
            }
        }

        public bool ExcludeSymbols
        {
            get { return this.hasSymbols; }
            set	{ this.hasSymbols = value;}
        }

        public bool RepeatCharacters
        {
            get { return this.hasRepeating; }
            set	{ this.hasRepeating = value;}
        }

        public bool ConsecutiveCharacters
        {
            get { return this.hasConsecutive; }
            set	{ this.hasConsecutive = value;}
        }

        private const int DefaultMinimum = 6;
        private const int DefaultMaximum = 10;
        private const int UBoundDigit    = 61;

        private RNGCryptoServiceProvider    rng;
        private int 			minSize;
        private int 			maxSize;
        private bool			hasRepeating;
        private bool			hasConsecutive;
        private bool            hasSymbols;
        private string          exclusionSet;
        private char[] pwdCharArray = "abcdefghijklmnopqrstuvwxyzABCDEFG" +
            "HIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()-_=+[]{}\\|;:'\",<" + 
            ".>/?".ToCharArray();                                        
    }
}

这个类的早期版本是为更大项目准备的(我很快会发布关于该项目的各个组件的文章)。由于许多因素,该代码很匆忙,并且被证明效率低下。事实上,我希望我能回到过去并取消发布它!虽然这个版本肯定更好,但仍然有改进的空间。生成算法仍然可以优化。此外,使用正则表达式来定义和验证我们希望生成的密码会很有趣。我会这样做,但我写解析器已经很久了。也许在下一个版本中...

在之前的文章中,我使用 NAnt 工具作为我的构建方案。不幸的是,该团队尚未发布一个集成 NUnit 2.0 的稳定版本。我可以从 CVS 树中获取源代码,但我太懒了。相反,我决定回到 Visual Studio .NET 作为我的开发环境。我也开始习惯使用 NUnit 2.0 进行测试驱动开发。如果您没有使用此工具进行单元测试,我强烈建议您试一试 http://www.nunit.org/。它使用属性和反射来指定测试套件、测试夹具和测试,非常出色且易于使用。我已经将我的单元测试夹具包含在源代码中。另外,尝试一下 Visual Studio .NET 的 NUnit 插件;它对于在 IDE 中运行您的测试非常方便。

演示项目是一个简单的 Windows Forms UI,它允许您配置密码生成器的属性。我必须说,虽然 VS.NET 相当完整且强大,但我就是不喜欢 Forms 设计器的感觉。但是,它绝对能完成工作。

非常感谢 Mike Asher 和 Julian Roberts 对密码生成器第一个版本的反馈。Julian 慷慨地在一个 ASP.NET 项目中测试了该代码,并确认它的性能好多了。此外,我恢复了我的旧 C++ 括号风格只是为了让 Nish 开心...希望你欣赏这种牺牲!:-)

更改日志

版本 1.2

  • 更新到 .NET Framework 1.1
  • 删除了 FirstCharacterLastCharacter 属性; Exclusions 适用于所有字符
  • Generate() 方法替换了 Password 属性
  • 删除了 PwdMaskFlags;使用 Exclusions 属性和/或 ExcludeSymbols 属性
  • 使用 RNGCryptoServiceProvider 代替 Random 进行随机数生成
  • 更新了演示应用程序

(设计中的)完美并非在无物可添加时达到,而是在无物可移除时达到。 -Antoine de Saint-Exupéry

版本 1.1

  • 改进的密码生成算法

版本 1.0

  • 初始版本
© . All rights reserved.