|
One other reason...
I think it's a pretty fascinating way to compare the languages. Take two pieces of code that functionally do the same thing one in C# one in VB and look at the differences. If you are on the edge of using C# or VB and want to see some comparisons there is now a short concise example burried away in your article. For me when I look at the VB conversion my stomach starts to cramp but hey... to each their own. It might help someone on the edge of indecision.
|
|
|
|
|
Other than that just saying thanks for the article. Insightful and handy and ... and ... and...
|
|
|
|
|
Thank you so much! I appreciate that!
Kevin
"Semicolons in a programming language are like mother's milk."
|
|
|
|
|
Kevin,
What's the copyright status of this class? I'd like to use it, rather than re-invent the wheel.
|
|
|
|
|
The copyright status is (c) 2005 Kevin Stewart. As mentioned on the "Submit an Article" page:
If you post to CodeProject then you retain copyright of your article and code. You also give CodeProject permission to use it in a fair manner and also permit other developers to use the sourcecode associated with your articles in their own applications as long as they do not remove your copyright notices or try and take credit for your work.
I would love for you to use my code, in fact I encourage it. I haven't touched this class in awhile as I've been experimenting with Ruby lately. I do have some ideas for a new version. I may do it in Ruby first and follow up with a C# version. Glad you find this class useful!
Kevin
"Semicolons in a programming language are like mother's milk."
|
|
|
|
|
I sure hope it is OK for me to use this in an application I'm working on
I was just adding some comments to the class for the sake of documentation (NDoc rules !) and while trying to explain the behaviours of the Minimum and Maximum properties I ran into this issue:
Assuming that
private const int DefaultMinimum = 6;
private const int DefaultMaximum = 10;
then what if one sets minimum to 12 and then maximum to 10 ? In that case the code
---------------------------------------------------------------
public int Maximum
{
get { return this.maxSize; }
set
{
this.maxSize = value;
if ( this.minSize >= this.maxSize )
{
this.maxSize = PasswordGenerator.DefaultMaximum;
}
}
}
---------------------------------------------------------------
will see that minSize >= maxSize (12 >= 10) and thus set maxSize to defaultMaximum which is 10. Then you have minSize=12 and maxSize=10 - what happens then ?
Anyway, I've added Dirk Vandenheuvel's validate method to my version of the class and thrown in some commens if anybody wants it (I had to turn off HTML tags in this post to preserve the xml comments):
---------------------------------------------------------------
using System;
using System.Security.Cryptography;
using System.Text;
namespace mynamespace
{
/// <summary>
/// The PasswordGenerator class is a general utility class for generating password
/// based on a number of properties. Once one has set these properties, a random
/// password that complys with these can be generated.
/// </summary>
/// <remarks>
/// The PasswordGenerator class is originally created by Kevin Stewart as posted on
/// http://www.codeproject.com/csharp/pwdgen.asp
/// </remarks>
/// <example>
/// <code>
/// // Create new password by using PasswordGenerator class
/// PasswordGenerator generator = new PasswordGenerator();
/// generator.ConsecutiveCharacters = true;
/// generator.Exclusions = "`'~^|{}\\[](),.\";:<>/"; // Allowed special chars are: !@#$%&*-_=+
/// generator.Minimum = 8;
/// generator.Maximum = 8;
/// generator.RepeatCharacters = true;
/// string randomPassword = generator.Generate();
/// </code>
/// </example>
public class PasswordGenerator
{
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 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()-_=+[]{}\\|;:'\",<.>/?".ToCharArray();
/// <summary>
/// Default constructor
/// </summary>
public PasswordGenerator()
{
this.Minimum = DefaultMinimum;
this.Maximum = DefaultMaximum;
this.ConsecutiveCharacters = false;
this.RepeatCharacters = true;
this.ExcludeSymbols = false;
this.Exclusions = null;
rng = new RNGCryptoServiceProvider();
}
/// <summary>
/// Generates a random number between the specified bounds
/// </summary>
/// <param name="lBound">Lower bound</param>
/// <param name="uBound">Upper bound</param>
/// <returns>The generated random number</returns>
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;
}
/// <summary>
/// Generates a random character from the specified valid range of characters
/// based on a generated random number between the character arrays bounds
/// </summary>
/// <returns>A random character within the allowed range</returns>
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;
}
/// <summary>
/// Generates the random password based on the specified properties
/// </summary>
/// <returns>The random password</returns>
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;
}
}
/// <summary>
/// The class will by default generate passwords with the characters
/// a-z, A-Z, 0-9 and these special characters:
/// `~!@#$%^&*()-_=+[]{}\\|;:'\",<.>/?
/// By specifying an Exclusions string, one can exclude some of these
/// special characters
/// </summary>
public string Exclusions
{
get { return this.exclusionSet; }
set { this.exclusionSet = value; }
}
/// <summary>
/// Defines the minimum length of the generated password
/// </summary>
/// <remarks>
/// Note that the absolute minimum size is 6. Setting a value smaller than
/// 6 will have no effect
/// </remarks>
public int Minimum
{
get { return this.minSize; }
set
{
this.minSize = value;
if ( PasswordGenerator.DefaultMinimum > this.minSize )
{
this.minSize = PasswordGenerator.DefaultMinimum;
}
}
}
/// <summary>
/// Defines the maximum length of the generated password
/// </summary>
/// <remarks>
/// Note that if the maximum size is set to something smaller than
/// <see cref="Minimum"/>, the maximum will be set to 10
/// </remarks>
public int Maximum
{
get { return this.maxSize; }
set
{
this.maxSize = value;
if ( this.minSize >= this.maxSize )
{
this.maxSize = PasswordGenerator.DefaultMaximum;
}
}
}
/// <summary>
/// Defines whether the generated password may contain any special characters
/// at all, or just letters and numbers
/// </summary>
public bool ExcludeSymbols
{
get { return this.hasSymbols; }
set { this.hasSymbols = value;}
}
/// <summary>
/// Defines whether the generated password may contain the same character
/// more than once
/// </summary>
public bool RepeatCharacters
{
get { return this.hasRepeating; }
set { this.hasRepeating = value;}
}
/// <summary>
/// Defines whether the generated password may contain the same character
/// repeated after itself
/// </summary>
public bool ConsecutiveCharacters
{
get { return this.hasConsecutive; }
set { this.hasConsecutive = value;}
}
/// <summary>
/// Validates a password against the defined properties
/// </summary>
/// <remarks>
/// The Validate(...) method is originally posted by Dirk Vandenheuvel as a
/// comment to the original article.
/// </remarks>
/// <param name="password">The password to validate</param>
/// <returns>True if the password is valid, otherwise false</returns>
public bool Validate(string password)
{
int iCount = 0;
if (password.Length < minSize || password.Length > maxSize)
return(false);
// check for Consecutive characters
if (!hasConsecutive) // cannot have consecutive characters
{
for (iCount = 0; iCount < password.Length-1; iCount++)
{
if (password[iCount] == password[iCount+1])
return(false);
}
}
if (!hasRepeating) // cannot have repeating characters
{
for (iCount = 0; iCount < password.Length; iCount++)
{
int index = password.IndexOf(password[iCount]);
while (index != -1)
{
if (index != iCount)
return(false);
index = password.IndexOf(password[iCount]);
}
}
}
if (Exclusions != null) // cannot have characters from exclusion string
{
for (iCount = 0; iCount < password.Length; iCount++)
{
if (Exclusions.IndexOf(password[iCount]) != -1)
return(false);
}
}
if (ExcludeSymbols) // cannot contain 'symbols'
{
for (iCount = UBoundDigit; iCount < pwdCharArray.GetUpperBound(0); iCount++)
{
if (password.IndexOf(pwdCharArray[iCount]) != -1)
return(false);
}
}
return(true);
}
}
}
---------------------------------------------------------------
<span style="font-family:verdana; font-size:10px">Do you know why it's important to make fast decisions? Because you give yourself more time to correct your mistakes, when you find out that you made the wrong one. </span><span style="font-family:verdana; font-size:10px"><i>Chris Meech on deciding whether to go to his daughters graduation or a Neil Young concert</i></span>
|
|
|
|
|
Jan, thank you for adding the comments for NDOC & Kevin thank you for the nice code!
Danny Crowell
www.crowsol.com
|
|
|
|
|
You can treat a string as a character array, which means you don't need to go through the process of dropping it into an array..
instead of...
private char[] pwdCharArray = "abcdefghijklmnopqrstuvwxyzABCDEFG" +
"HIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()-_=+[]{}\\|;:'\",<" +
".>/?".ToCharArray();
private string pwdChars = ....; //no .ToCharArray()
and higher up, you can use pwdChars[index] the same as pwdCharArray[index] .. this is just an fyi, and a relatively small notation.. nice piece of code.
--
Michael J. Ryan - tracker1(at)theroughnecks(dot)com - www.theroughnecks.net
icq: 4935386 - AIM/AOL: azTracker1 - Y!: azTracker1 - MSN/Win: (email)
|
|
|
|
|
I believe that if the RepeatCharacters bool is set to false (do not allow repeat chars) and the password length is greater than the length of available chars, then the generate code enters an infinite loop.
A quick fix would be to ignore the RepeatCharacters value and continue reusing those characters after that point, as in the code below: (or just return the results thus far)
if ( false == this.RepeatCharacters )
{
string temp = pwdBuffer.ToString();
int duplicateIndex = temp.IndexOf(nextCharacter);
int upperBound = pwdCharArray.GetUpperBound(0);
if ( true == this.ExcludeSymbols )
{
upperBound = PasswordGenerator.UBoundDigit;
}
if (pwdLength <= upperBound)
{
while ( -1 != duplicateIndex )
{
nextCharacter = GetRandomCharacter();
duplicateIndex = temp.IndexOf(nextCharacter);
}
}
}
Although this takes into account the upperBound of the available chars, it does not include the count of the excluded chars list.
.frihani
|
|
|
|
|
Here's my alternative, shorter version:
public class AlphaNumericPasswordGenerator
{
public string Generate()
{
return MD5Hash(this.secondsSinceEpoch().ToString());
}
private int secondsSinceEpoch()
{
TimeSpan timeSpan = (DateTime.UtcNow - new DateTime(1970, 1, 1));
return (int) timeSpan.TotalSeconds;
}
private string MD5Hash(string Data)
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] hash = md5.ComputeHash( Encoding.ASCII.GetBytes(Data) );
StringBuilder stringBuilder = new StringBuilder();
foreach( byte b in hash )
{
stringBuilder.AppendFormat("{0:x2}", b);
}
return stringBuilder.ToString();
}
}
www.sloppycode.net
|
|
|
|
|
Nice! Thanks for sharing your approach.
Kevin
"Semicolons in a programming language are like mother's milk."
|
|
|
|
|
Worth noting that mine doesn't work unless you Thread.Sleep(100) and use the random number generator, if you're generating inside a loop!
|
|
|
|
|
Thread.Sleep() for the same amount of time each time will make this predictable after enough generations. If you want to use md5 to get alphanumerics, use input data like so:
rndBytes = new byte[arraySize];<br />
rng = new RNGCryptoServiceProvider();<br />
md5 = new MD5CryptoServiceProvider();<br />
<br />
rng.GetBytes(rndBytes);
byte[] outArray = md5.ComputeHash(rndBytes);<br />
StringBuilder stringBuilder = new StringBuilder();<br />
foreach( byte b in outArray ) {<br />
stringBuilder.AppendFormat("{0:x2}", b);<br />
}<br />
return stringBuilder.ToString();
|
|
|
|
|
public bool Validate(string password)
{
int iCount = 0;
if (password.Length < minSize || password.Length > maxSize)
return(false);
if (!hasConsecutive)
{
for (iCount = 0; iCount < password.Length-1; iCount++)
{
if (password[iCount] == password[iCount+1])
return(false);
}
}
if (!hasRepeating)
{
for (iCount = 0; iCount < password.Length; iCount++)
{
int index = password.IndexOf(password[iCount]);
while (index != -1)
{
if (index != iCount)
return(false);
index = password.IndexOf(password[iCount]);
}
}
}
if (Exclusions != null)
{
for (iCount = 0; iCount < password.Length; iCount++)
{
if (Exclusions.IndexOf(password[iCount]) != -1)
return(false);
}
}
if (ExcludeSymbols)
{
for (iCount = UBoundDigit; iCount < pwdCharArray.GetUpperBound(0); iCount++)
{
if (password.IndexOf(pwdCharArray[iCount]) != -1)
return(false);
}
}
return(true);
}
|
|
|
|
|
Thanks, Dirk! I should've implemented validation in the original code, but I'm a slacker. However, I have been considering a new version where you can specify a regular expression as a way to generate the password. Then, you could use the same input regex to validate the output. I'll post it once I get around to writing it!
Kevin
"Semicolons in a programming language are like mother's milk."
|
|
|
|
|
Is it my imagination...or does GetCryptographicRandomNumber not seem to return an integer between the lbound and ubound!
|
|
|
|
|
nope...it was my imagination after all!
|
|
|
|
|
Hello. I am very new in C programing. i have to do a class project and would really love to include a password requester in it. Where, when someone presses a set of keys, another screen will pop out and request for a password to shut the program down. With out the right pass the pop out will close and return to the original page.
A few things you might want to know.
1)The user cannot use the mouse to shut the program down.
2)I was wondering if i could include the if and else commands on the pop out for the password page.
like
if(password is right)
{
Allow shut down.
}
else
{
pop up closes
}
But was thinking, it won't be case sensitive.
So, i really need all of your expertise. Please... I need it badly. PLease help. I might get lost on this new found site. So if possible, you can reply to me via e-mail at goddime@yahoo.ca
Or else, i would try to find this forum again. thanks alot for your kind help.
Oh yah.. since i am so new, i really hope you can include the command from starting to end. Which includes #include <stdio.h> and ends with
system("pause");
return 0
}
Cos i keep getting confused with the codes on this site. Finally, I need the code in C program. Not other compilers. Teacher's rule. I hope you can include lots of details. So i know why and how the code is working, and created. thanks alot.
|
|
|
|
|
private char[] pwdCharArray = {<br />
'a','b','c','d','e','f','g','h','i','j','k','l','m',<br />
'n','o','p','q','r','s','t','u','v','w','x','y','z',<br />
'A','B','C','D','E','F','G','H','I','J','K','L','M',<br />
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',<br />
'0','1','2','3','4','5','6','7','8','9','`','~','!',<br />
'@','#','$','%','^','&','*','(',')','-','_','=','+',<br />
'[',']','{','}','\\','|',';',':','\'','"',',','<',<br />
'.','>','/','?'<br />
}
can be simplified (for ease of typing, and perhaps also reading) using the string's ToCharArray() method, to:
<br />
private char[] pwdCharArray = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()-_=+[]{}\\|;:'\",<.>/?".ToCharArray();<br />
|
|
|
|
|
Good suggestion! Might make it into my next update. Thanks!
Kevin
"Semicolons in a programming language are like mother's milk."
|
|
|
|
|
Interesting article, but there is a not so small gotcha.
A rather large bias is introduced in this line in GetCryptographicRandomNumber()
int number = ( Int32.Parse(rndnum[0].ToString()) % uBound )
The problem is that the modulo operator "%" will produce an uneven distribution, especially where the operand is only a byte (0-255). The bias is variable depending on the value of upperBound and can significantly skew the distributions of characters. If you increase the random number range to 32 bits it won't eliminate the bias but it becomes trivial.
Also this section has a significant bias to return lBound whenever lBound is >0.
if ( lBound > number )
{
number = lBound;
}
-Marty
|
|
|
|
|
Here's a possible hack:
protected int GetCryptographicRandomNumber(int lBound, int uBound)
{
byte[] rndnum = new Byte[4];
rng.GetBytes(rndnum);
uint urndnum = System.BitConverter.ToUInt32(rndnum,0);
return (int)(urndnum % (uBound-lBound)) + lBound;
}
-Marty
|
|
|
|
|
You made a couple of very good points, Marty.
Alternatively, you can simply reject values outside the requested range. This removes any bias that is not in the random number generator itself, including the 0.00001% that's left in your solution.
protected int GetCryptographicRandomNumber(int lBound, int uBound)
{
byte[] rndnum = new Byte[1];
int number;
do
{
rng.GetBytes(rndnum);
number = Convert.ToInt32(rndnum[0]) & 0x7F;
} while (number < lBound || number > uBound);
return number;
} Note that the "& 0x7F " should be removed if uBound can be larger than 127.
If you're concerned about performance, you can work with a buffer of random bytes to minimize calls to GetBytes and/or reduce your random byte to the smallest range 0 to 2^n-1 that fits lBound and uBound. However, since we're not generating millions of passwords here, I don't think that's much of a concern.
Jeffrey
Everything should be as simple as possible, but not simpler. -- Albert Einstein
http://www.extremeoptimization.com/
|
|
|
|
|
I was wondering how long it would take for someone to grab that residual bias "bait" Chuckle.
It does show how subtle flaws can be in these sorts of things. I also concur about the linear congruential generator being flawed. The rand() in clib is so badly biased that Monte Carlo simulations of a craps game materially diverges (p <.001) from it's estimate after 10^10 passes. I went to a crypto level rnd gen as a result. It's amazing what can be done with "microprocessors" these days.
-Marty
|
|
|
|
|
Now I see what happens when you sleep through probability and statistics.
You both make excellent points and I will incorporate one of your proposed solutions in the next update, which will probably be today or tomorrow since it looks like the article posting got messed up a bit. Thanks for the feedback!
Kevin
"Semicolons in a programming language are like mother's milk."
|
|
|
|
|