小新的技术天地

Make It Works !

博客园 首页 新随笔 联系 订阅 管理

原作者:Kevin Stewart

文章出处:http://www.codeproject.com/csharp/pwdgen.asp


Introduction

This article illustrates how to create a very simple password generator using C#. Password generators are useful in many applications:

  • Registration/membership systems for Web sites
  • Auto-creation of passwords according to a specified rule
  • Securing application-specific data

介绍

 

这篇文章举例说明怎么使用C#来创建一个简单的密码产生器。密码产生器在很多软件中都很有用:

  • 网站的 注册/会员 系统
  • 根据特定规则的密码自动创建器
  • 为软件的特定数据提供保护

The PasswordGenerator class is fairly simple. It exposes several properties that control how the password will be generated.

  • Exclusions: Specifies the set of characters to exclude in password generation.
  • Minimum: Specifies the minimum length of the generated password.
  • Maximum: Specifies the maximum length of the generated password.
  • ConsecutiveCharacters: Controls generation of consecutive characters in the generated password.
  • RepeatingCharacters: Controls generation of repeating characters in the generated password.
  • ExcludeSymbols: Excludes symbols from the set of characters used to generate the password

 PasswordGenerator类很简单。它使用了一些属性,这些属性控制密码怎么产生。

  • Exclusions: 指定排除在密码产生器中的特定字符集和。
  • Minimum: 指定获得密码的最小长度。
  • Maximum: 指定获得密码的最大长度。
  • ConsecutiveCharacters: 控制获得的密码是连续字符。
  • RepeatingCharacters: 控制获得的密码是使重复的字符。
  • ExcludeSymbols: 排除用来获得密码的的字符集和中的一些字符。

After setting your desired properties, call the Generate() method to create your new password.

在设置好你所需要的属性以后,调用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();                                        
    }

}


The previous version of this class was intended for use in a much larger project (I will be publishing articles on various components of that project soon.). Due to many factors, that code was rushed and proved to be inefficient. In fact, I wish I could go back in time and unpublish it! While this version is definitely better, there is still room for improvement. The generation algorithm can still be optimized. Also, it would be interesting to use regular expressions to both define and validate the passwords we wish to generate. I would have done this, but it's been a long time since I wrote a parser. Maybe for the next version...


这个类的前一个版本本来想用在一个更大的项目中(我会陆续发表关于这个项目的各种组成成分的文章)。由于许多因素,那个类写得太匆忙了,而且被证实是没有效率的。事实上,我希望我能回到过去,不发表它该多好!尽管这个版本明显好多了,其实还有改进的空间。密码产生算法还可以优化。如果能用正则表达式定义和验证我们得到的密码那不是很有趣嘛。我将完成这个功能,但可能会很久因为我在写一个剖析器(parser)。也许在下一个版本吧


In previous articles, I have used the NAnt tool as my build solution. Unfortunately, that team has not produced a stable release that integrates NUnit 2.0. I COULD get the source from the CVS tree, but I'm way too lazy for that. Instead, I have decided to go back to Visual Studio .NET as my development environment. I'm also getting used to test-driven development with NUnit 2.0. If you aren't using this tool for unit testing, I highly recommend you give it a try http://www.nunit.org/. Its use of attributes and reflection to specify test suites, test fixtures and tests, and is quite remarkable and easy to use. I've included my unit test fixture with the source code. Also, try the NUnit Addin for Visual Studio .NET; it's very handy for running your tests within the IDE.


在先前的文章里,我使用了NAnt工具作为我的创建方案。不幸的是,那个队伍没有发布一个稳定的版本来整合NUnit2.0。我可以从CVS树里获得源码,但我太懒没做那事。反而,我决定返回去把Visual Studio .NET作为我的开发环境。我也习惯于NUnit2.0test-driven的开发方式。如果你现在没有用这个工具来做测试,我强烈建议你去http://www.nunit.org/下载它。它使用属性和反射来指定测试套件(?),测试属性(?)和测试,而且非常不错,易于使用。我已经把我的单元测试属性(?)包含在源码中了。也可以试试用一下为Visual Studio .NET 开发的NUnit插件;这个在使用IDE做测试的时候非常方便。


The demo project is a simple Windows Forms UI that allows one to configure the password generator's properties. I must say that while VS.NET is fairly complete and powerful, I just don't like the feel of the forms designer. However, it definitely does the job.


演示程序是一个简单的Windows窗体用户界面,允许你配置密码生成器的属性。我不得不说VS.NET是这么的完善和强大,我只是不太喜欢窗体设计器的感觉。当然,它明确地完成了任务。


Many thanks to Mike Asher and Julian Roberts for their feedback on the first version of the password generator. Julian was kind enough to test the code in an ASP.NET project and confirmed that it performs much better. Also, I reverted to my old C++ bracing style just to make Nish happy...hope you appreciate the sacrifice! :-)


十分感谢(Many thanks,呵呵,第一次听到这种说法)Mike Asher Julian Roberts对第一版的密码产生器所作的建议。Julian十分友善地在ASP.NET项目中作了测试,证实它表现地好多了。还有,我回归了以前的C++ bracing风格,只是为了让Nish能够高兴(呵呵)希望你能赏识我的奉献!:-)

posted on 2004-10-15 23:45  小新0574  阅读(2363)  评论(3编辑  收藏  举报