Password maker in c#Deck of cards with shuffle and sort functionalityPassword hashingSecure password hashing implementationForgotten password / password resetI'm in your .zips crackin' your passwordsSimple command-line password generatorPassword generator and hashingRandomizer App as Decision MakerEncrypted safeboxSecurity Helper Class According To NIST SP800-63B GuidelinesExcel Multiplication Table Maker

Why are Hobbits so fond of mushrooms?

What are the steps/action plan to introduce Test Automation in a company?

Would letting a multiclass character rebuild their character to be single-classed be game-breaking?

I have a ruthless DM and I'm considering leaving the party. What are my options to minimize the negative impact to the rest of the group?

Referring to different instances of the same character in time travel

Why would guns not work in the dungeon?

Robbers: The Hidden OEIS Substring

Keep milk (or milk alternative) for a day without a fridge

Do native speakers use ZVE or CPU?

When did the Roman Empire fall according to contemporaries?

QGIS Welcome page: What is 'pin to list' for?

'rm' (delete) thousands of files selectively

Replacements for swear words

How to know whether a Tamron lens is compatible with Canon EOS 60D?

Why did the Japanese attack the Aleutians at the same time as Midway?

Professor falsely accusing me of cheating in a class he does not teach, two months after end of the class. What precautions should I take?

How can I deal with a player trying to insert real-world mythology into my homebrew setting?

Is it rude to tell recruiters I would only change jobs for a better salary?

Is purchasing foreign currency before going abroad a losing proposition?

Shortest distance around a pyramid

How did the hit man miss?

What is this welding tool I found in my attic?

What does the standard say about char arrays as template arguments?

What's the minimum number of sensors for a hobby GPS waypoint-following UAV?



Password maker in c#


Deck of cards with shuffle and sort functionalityPassword hashingSecure password hashing implementationForgotten password / password resetI'm in your .zips crackin' your passwordsSimple command-line password generatorPassword generator and hashingRandomizer App as Decision MakerEncrypted safeboxSecurity Helper Class According To NIST SP800-63B GuidelinesExcel Multiplication Table Maker






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;








4












$begingroup$


I need to generate semi-complex throw-able password (just one use) on my application. I want it to be:



  • at least 8 char long;

  • contains at least 1 digit, 1 [a-z] char, 1 [A-Z] char;

I generate a few passwords a day, I don't mind about speed. I'm more looking for suggestions about code being maintainable (or not) and possible huge security flaw. Here's the code:



public static string GenerateSimplePassword(int length = 8)

length = Math.Max(length, 8);

var allowedLower = new[] 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' ;
var allowedDigits = new[] '1', '2', '3', '4', '5', '6', '7', '8', '9' ;
var allowedUpper = allowedLower.Select(x => char.ToUpper(x)).ToArray();

var r = new Random();
var generatedCode = "";
generatedCode += allowedLower[r.Next(0, allowedLower.Length)];

generatedCode += allowedDigits[r.Next(0, allowedDigits.Length)];

generatedCode += allowedUpper[r.Next(0, allowedUpper.Length)];

var arrays = new[] allowedLower, allowedUpper, allowedDigits ;
for (var i = 0; i < length - 3; i++)

var array = arrays[r.Next(0, arrays.Length)];
generatedCode += array[r.Next(0, array.Length)];


return String.Join("", generatedCode.OrderBy(x => r.Next()));










share|improve this question











$endgroup$











  • $begingroup$
    Are these password composition rules yours, or an external requirement? Consider that the number of possible 8-character passwords composed from "0 or more characters from each list" is actually smaller than the number of possible 8-character passwords composed from "at least 1 character from each list".
    $endgroup$
    – benj2240
    7 hours ago











  • $begingroup$
    How are these passwords used? Can several people share the same random password? Does a password need to expire the next day?
    $endgroup$
    – dfhwze
    7 hours ago










  • $begingroup$
    @benj2240 external requirement. I guess it avoid password with only lowercase char for example.
    $endgroup$
    – Thomas Ayoub
    6 hours ago










  • $begingroup$
    @dfhwze it's for OTP. No expiration here.
    $endgroup$
    – Thomas Ayoub
    6 hours ago






  • 1




    $begingroup$
    @pacmaninbw users are quite low tech, so yes, we avoid special char in this generator
    $endgroup$
    – Thomas Ayoub
    6 hours ago

















4












$begingroup$


I need to generate semi-complex throw-able password (just one use) on my application. I want it to be:



  • at least 8 char long;

  • contains at least 1 digit, 1 [a-z] char, 1 [A-Z] char;

I generate a few passwords a day, I don't mind about speed. I'm more looking for suggestions about code being maintainable (or not) and possible huge security flaw. Here's the code:



public static string GenerateSimplePassword(int length = 8)

length = Math.Max(length, 8);

var allowedLower = new[] 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' ;
var allowedDigits = new[] '1', '2', '3', '4', '5', '6', '7', '8', '9' ;
var allowedUpper = allowedLower.Select(x => char.ToUpper(x)).ToArray();

var r = new Random();
var generatedCode = "";
generatedCode += allowedLower[r.Next(0, allowedLower.Length)];

generatedCode += allowedDigits[r.Next(0, allowedDigits.Length)];

generatedCode += allowedUpper[r.Next(0, allowedUpper.Length)];

var arrays = new[] allowedLower, allowedUpper, allowedDigits ;
for (var i = 0; i < length - 3; i++)

var array = arrays[r.Next(0, arrays.Length)];
generatedCode += array[r.Next(0, array.Length)];


return String.Join("", generatedCode.OrderBy(x => r.Next()));










share|improve this question











$endgroup$











  • $begingroup$
    Are these password composition rules yours, or an external requirement? Consider that the number of possible 8-character passwords composed from "0 or more characters from each list" is actually smaller than the number of possible 8-character passwords composed from "at least 1 character from each list".
    $endgroup$
    – benj2240
    7 hours ago











  • $begingroup$
    How are these passwords used? Can several people share the same random password? Does a password need to expire the next day?
    $endgroup$
    – dfhwze
    7 hours ago










  • $begingroup$
    @benj2240 external requirement. I guess it avoid password with only lowercase char for example.
    $endgroup$
    – Thomas Ayoub
    6 hours ago










  • $begingroup$
    @dfhwze it's for OTP. No expiration here.
    $endgroup$
    – Thomas Ayoub
    6 hours ago






  • 1




    $begingroup$
    @pacmaninbw users are quite low tech, so yes, we avoid special char in this generator
    $endgroup$
    – Thomas Ayoub
    6 hours ago













4












4








4





$begingroup$


I need to generate semi-complex throw-able password (just one use) on my application. I want it to be:



  • at least 8 char long;

  • contains at least 1 digit, 1 [a-z] char, 1 [A-Z] char;

I generate a few passwords a day, I don't mind about speed. I'm more looking for suggestions about code being maintainable (or not) and possible huge security flaw. Here's the code:



public static string GenerateSimplePassword(int length = 8)

length = Math.Max(length, 8);

var allowedLower = new[] 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' ;
var allowedDigits = new[] '1', '2', '3', '4', '5', '6', '7', '8', '9' ;
var allowedUpper = allowedLower.Select(x => char.ToUpper(x)).ToArray();

var r = new Random();
var generatedCode = "";
generatedCode += allowedLower[r.Next(0, allowedLower.Length)];

generatedCode += allowedDigits[r.Next(0, allowedDigits.Length)];

generatedCode += allowedUpper[r.Next(0, allowedUpper.Length)];

var arrays = new[] allowedLower, allowedUpper, allowedDigits ;
for (var i = 0; i < length - 3; i++)

var array = arrays[r.Next(0, arrays.Length)];
generatedCode += array[r.Next(0, array.Length)];


return String.Join("", generatedCode.OrderBy(x => r.Next()));










share|improve this question











$endgroup$




I need to generate semi-complex throw-able password (just one use) on my application. I want it to be:



  • at least 8 char long;

  • contains at least 1 digit, 1 [a-z] char, 1 [A-Z] char;

I generate a few passwords a day, I don't mind about speed. I'm more looking for suggestions about code being maintainable (or not) and possible huge security flaw. Here's the code:



public static string GenerateSimplePassword(int length = 8)

length = Math.Max(length, 8);

var allowedLower = new[] 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' ;
var allowedDigits = new[] '1', '2', '3', '4', '5', '6', '7', '8', '9' ;
var allowedUpper = allowedLower.Select(x => char.ToUpper(x)).ToArray();

var r = new Random();
var generatedCode = "";
generatedCode += allowedLower[r.Next(0, allowedLower.Length)];

generatedCode += allowedDigits[r.Next(0, allowedDigits.Length)];

generatedCode += allowedUpper[r.Next(0, allowedUpper.Length)];

var arrays = new[] allowedLower, allowedUpper, allowedDigits ;
for (var i = 0; i < length - 3; i++)

var array = arrays[r.Next(0, arrays.Length)];
generatedCode += array[r.Next(0, array.Length)];


return String.Join("", generatedCode.OrderBy(x => r.Next()));







c# beginner security






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 7 hours ago









t3chb0t

36.6k7 gold badges58 silver badges137 bronze badges




36.6k7 gold badges58 silver badges137 bronze badges










asked 8 hours ago









Thomas AyoubThomas Ayoub

4283 silver badges13 bronze badges




4283 silver badges13 bronze badges











  • $begingroup$
    Are these password composition rules yours, or an external requirement? Consider that the number of possible 8-character passwords composed from "0 or more characters from each list" is actually smaller than the number of possible 8-character passwords composed from "at least 1 character from each list".
    $endgroup$
    – benj2240
    7 hours ago











  • $begingroup$
    How are these passwords used? Can several people share the same random password? Does a password need to expire the next day?
    $endgroup$
    – dfhwze
    7 hours ago










  • $begingroup$
    @benj2240 external requirement. I guess it avoid password with only lowercase char for example.
    $endgroup$
    – Thomas Ayoub
    6 hours ago










  • $begingroup$
    @dfhwze it's for OTP. No expiration here.
    $endgroup$
    – Thomas Ayoub
    6 hours ago






  • 1




    $begingroup$
    @pacmaninbw users are quite low tech, so yes, we avoid special char in this generator
    $endgroup$
    – Thomas Ayoub
    6 hours ago
















  • $begingroup$
    Are these password composition rules yours, or an external requirement? Consider that the number of possible 8-character passwords composed from "0 or more characters from each list" is actually smaller than the number of possible 8-character passwords composed from "at least 1 character from each list".
    $endgroup$
    – benj2240
    7 hours ago











  • $begingroup$
    How are these passwords used? Can several people share the same random password? Does a password need to expire the next day?
    $endgroup$
    – dfhwze
    7 hours ago










  • $begingroup$
    @benj2240 external requirement. I guess it avoid password with only lowercase char for example.
    $endgroup$
    – Thomas Ayoub
    6 hours ago










  • $begingroup$
    @dfhwze it's for OTP. No expiration here.
    $endgroup$
    – Thomas Ayoub
    6 hours ago






  • 1




    $begingroup$
    @pacmaninbw users are quite low tech, so yes, we avoid special char in this generator
    $endgroup$
    – Thomas Ayoub
    6 hours ago















$begingroup$
Are these password composition rules yours, or an external requirement? Consider that the number of possible 8-character passwords composed from "0 or more characters from each list" is actually smaller than the number of possible 8-character passwords composed from "at least 1 character from each list".
$endgroup$
– benj2240
7 hours ago





$begingroup$
Are these password composition rules yours, or an external requirement? Consider that the number of possible 8-character passwords composed from "0 or more characters from each list" is actually smaller than the number of possible 8-character passwords composed from "at least 1 character from each list".
$endgroup$
– benj2240
7 hours ago













$begingroup$
How are these passwords used? Can several people share the same random password? Does a password need to expire the next day?
$endgroup$
– dfhwze
7 hours ago




$begingroup$
How are these passwords used? Can several people share the same random password? Does a password need to expire the next day?
$endgroup$
– dfhwze
7 hours ago












$begingroup$
@benj2240 external requirement. I guess it avoid password with only lowercase char for example.
$endgroup$
– Thomas Ayoub
6 hours ago




$begingroup$
@benj2240 external requirement. I guess it avoid password with only lowercase char for example.
$endgroup$
– Thomas Ayoub
6 hours ago












$begingroup$
@dfhwze it's for OTP. No expiration here.
$endgroup$
– Thomas Ayoub
6 hours ago




$begingroup$
@dfhwze it's for OTP. No expiration here.
$endgroup$
– Thomas Ayoub
6 hours ago




1




1




$begingroup$
@pacmaninbw users are quite low tech, so yes, we avoid special char in this generator
$endgroup$
– Thomas Ayoub
6 hours ago




$begingroup$
@pacmaninbw users are quite low tech, so yes, we avoid special char in this generator
$endgroup$
– Thomas Ayoub
6 hours ago










2 Answers
2






active

oldest

votes


















4












$begingroup$

When calling your method like this:



 for (int i = 0; i < 10; i++)

Console.WriteLine(GenerateSimplePassword());



I get this result:



 88yuQg7H
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o


which is not so random.



The reason is the Random object being instantiated each time the method is called. Instead you must move the Random object outside the method as a static one time initialized field.




You could make the valid chars as a static field string:



const string chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int DigitStart = chars.IndexOf('0');
static int UpperStart = chars.IndexOf('A');



Instead of concatenate strings throughout the process it would be better to use:



char[] password = new char[length]



it will improve both performance and readability:



password[0] = chars[random.Next(0, DigitStart)];



All in all my suggestion would be something like:



public static class PasswordGenerator


static readonly Random random = new Random();
const string chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int DigitStart = chars.IndexOf('0');
static int UpperStart = chars.IndexOf('A');

public static string GenerateSimplePassword(int length = 8)

length = Math.Max(length, 8);

char[] password = new char[length];
password[0] = chars[random.Next(0, DigitStart)];
password[1] = chars[random.Next(DigitStart, UpperStart)];
password[2] = chars[random.Next(UpperStart, chars.Length)];

for (int i = 3; i < length; i++)

password[i] = chars[random.Next(0, chars.Length)];


return new string(password.OrderBy(_ => random.Next()).ToArray());







share|improve this answer











$endgroup$












  • $begingroup$
    Thanks for the answer. The string is shuffled with generatedCode.OrderBy(x => r.Next(), how the hacker could find out anything? But it gives a hint about bad code readability. I don't see how the simpler solution would assure that I get any of the 3 different char class. Does it?
    $endgroup$
    – Thomas Ayoub
    6 hours ago










  • $begingroup$
    @ThomasAyoub: Sorry, I missed the last line of code. But why do you want at least one of each?
    $endgroup$
    – Henrik Hansen
    6 hours ago










  • $begingroup$
    Which version of .NET you use? According to @Eric Lippert this problem with Random is resolved in later versions.
    $endgroup$
    – dfhwze
    5 hours ago










  • $begingroup$
    @dfhwze: I use 4.8. I've see he commented that somewhere, but can't remember where. Apparently it is not solved yet. Maybe in core? his blog: "Fortunately, I have just learned from an attentive reader that this problem has been fixed in some versions of the CLR; exactly which I am not sure..."?
    $endgroup$
    – Henrik Hansen
    5 hours ago











  • $begingroup$
    @Eric Lippert Eric's answer here talks about it: codereview.stackexchange.com/questions/223593/…
    $endgroup$
    – dfhwze
    5 hours ago


















3












$begingroup$

Fully Refactored Code: see this Gist



Example Usage:



static void Main(string[] args) 
var generator = new PasswordGenerator(new PasswordGeneratorOptions
MinimumNumberOfLowerCaseCharacters = 1,
MinimumNumberOfNumericCharacters = 1,
MinimumNumberOfUpperCaseCharacters = 1,
OutputLength = 8,
RandomNumberGenerator = new SecureRandom(),
SpecialCharacters = new char[0] ,
);

Console.WriteLine(generator.Next());



Review Comments:



First off, using Random as a source of entropy probably isn't the best idea since it is possible (however unlikely) that an attacker could gain the underlying seed; with it they would trivially be able to deduce all of the outputs used to generate our passwords. Let's implement a generator that uses a static instance of the RNGCryptoServiceProvider class instead:



// makes it easier to replace the implementation I demonstrate with something better
public interface ISecureRandom

uint Next(uint x, uint y);


// a possible implementation of a secure RNG, not exhaustively tested...
public sealed class SecureRandom : ISecureRandom

private static RandomNumberGenerator m_randomNumberGenerator = new RNGCryptoServiceProvider();

public SecureRandom(RandomNumberGenerator randomNumberGenerator)
if (null == randomNumberGenerator)
throw new ArgumentNullException(paramName: nameof(randomNumberGenerator));


m_randomNumberGenerator = randomNumberGenerator;

public SecureRandom() : this(new RNGCryptoServiceProvider())

public byte[] GetBytes(byte[] buffer)
m_randomNumberGenerator.GetBytes(buffer);

return buffer;

public byte[] GetBytes(int count) => GetBytes(new byte[count]);
public uint Next() => BitConverter.ToUInt32(GetBytes(sizeof(uint)), 0);
public uint Next(uint x, uint y)
if (x > y)
var z = x;

x = y;
y = z;


var range = (y - x);

if (range == 0)
return x;

else if (range == uint.MaxValue)
return Next();

else
return (Next(exclusiveHigh: range) + x);



private uint Next(uint exclusiveHigh)
var range = (uint.MaxValue - (((uint.MaxValue % exclusiveHigh) + 1) % exclusiveHigh));
var result = 0U;

do
result = Next();
while (result > range);

return (result % exclusiveHigh);




Regarding maintainability, we could start by creating a proper class to encapsulate all of our settings:



public sealed class PasswordGeneratorOptions

private static readonly char[] DefaultSpecialChars = new[] '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '=', '`', '~', '_', '+', ',', '.', ''', '"', ';', ':', '?', ';

public int MinimumNumberOfNumericCharacters get; set;
public int MinimumNumberOfLowerCaseCharacters get; set;
public int MinimumNumberOfSpecialCharacters get; set;
public int MinimumNumberOfUpperCaseCharacters get; set;
public int OutputLength get; set;
public ISecureRandom RandomNumberGenerator get; set;
public IReadOnlyList<char> SpecialCharacters get; set;

public PasswordGeneratorOptions()
MinimumNumberOfLowerCaseCharacters = 0;
MinimumNumberOfNumericCharacters = 0;
MinimumNumberOfSpecialCharacters = 0;
MinimumNumberOfUpperCaseCharacters = 0;
SpecialCharacters = DefaultSpecialChars;




Then we can declare a PasswordGenerator class that consumes our options:



public sealed class PasswordGenerator

private readonly PasswordGeneratorOptions m_options;

public PasswordGenerator(PasswordGeneratorOptions options)
if (options.OutputLength < (options.MinimumNumberOfLowerCaseCharacters + options.MinimumNumberOfNumericCharacters + options.MinimumNumberOfSpecialCharacters + options.MinimumNumberOfUpperCaseCharacters))
throw new ArgumentOutOfRangeException(message: "output length must be greater than or equal to the sum of all MinimumNumber* properties", actualValue: options.OutputLength, paramName: nameof(options.OutputLength));


m_options = options;




Now we can start thinking about implementation details, I personally like the idea of breaking things up into various categories so that we can provide more complex configuration options later. Let's try classifying your characters into lower-case, upper-case, number, and special. The ASCII encoding has largely taken care of all of this for us "for free" since three of these four classes have contiguous regions in the specification; we can write a simple generator for each like so:



var randomNumberGenerator = new SecureRandom();

Func<char> GetAsciiLetterLowerCase = () => ((char)randomNumberGenerator.Next(97, 123));
Func<char> GetAsciiLetterUpperCase = () => ((char)randomNumberGenerator.Next(65, 91));
Func<char> GetAsciiNumber = () => ((char)randomNumberGenerator.Next(48, 58));


Special characters aren't as easy so we'll resort to using a table of values:



var specialCharacters = new char[] '!', '@', '#', ;

Func<char> GetAsciiSpecial = () => specialCharacters[randomNumberGenerator.Next(0U, ((uint)specialCharacters.Count))];


The only remaining thing left to do is implement the generator function. This can be accomplished by more or less following the same strategy you already have in place. I chose to implement Fisher–Yates shuffle instead of using LINQ for the final randomization step:



public string Next() 
var index = 0;
var length = m_options.OutputLength;
var randomNumberGenerator = m_options.RandomNumberGenerator;
var result = new char[length];
var useSpecial = (0 < m_options.SpecialCharacters.Count);

for (var i = 0; (i < m_options.MinimumNumberOfLowerCaseCharacters); i++)
result[index++] = m_getAsciiLetterLowerCase();


for (var i = 0; (i < m_options.MinimumNumberOfNumericCharacters); i++)
result[index++] = m_getAsciiNumeric();


for (var i = 0; (i < m_options.MinimumNumberOfSpecialCharacters); i++)
result[index++] = m_getAsciiSpecial();


for (var i = 0; (i < m_options.MinimumNumberOfUpperCaseCharacters); i++)
result[index++] = m_getAsciiLetterUpperCase();


for (var i = index; (i < length); i++)
char c;

switch (randomNumberGenerator.Next(0U, (useSpecial ? 4U : 3U)))
case 3U:
c = m_getAsciiSpecial();
break;
case 2U:
c = m_getAsciiNumeric();
break;
case 1U:
c = m_getAsciiLetterLowerCase();
break;
case 0U:
c = m_getAsciiLetterUpperCase();
break;
default:
throw new InvalidOperationException();


result[i] = c;


FisherYatesShuffle(randomNumberGenerator, result);

return new string(result);


private static void SwapRandom<T>(ISecureRandom randomNumberGenerator, IList<T> list, uint indexLowerBound, uint indexUpperBound)
var randomIndex = randomNumberGenerator.Next(indexLowerBound, indexUpperBound);
var tempValue = list[(int)randomIndex];

list[(int)randomIndex] = list[(int)indexUpperBound];
list[(int)indexUpperBound] = tempValue;

private static void FisherYatesShuffle<T>(ISecureRandom randomNumberGenerator, IList<T> list)
var length = list.Count;
var offset = 0U;

while (offset < length)
SwapRandom(randomNumberGenerator, list, 0U, offset++);







share|improve this answer











$endgroup$








  • 1




    $begingroup$
    This can throw an IndexOutOfRange exception in the m_GetAsciiSpecial generator, right? If requested password length is greater than length of the passed in list of special characters. Shouldn't the generator use the length of the list, not the password length?
    $endgroup$
    – TomG
    4 hours ago






  • 1




    $begingroup$
    @TomG Good catch! That's a bug; fixed.
    $endgroup$
    – Kittoes0124
    4 hours ago











  • $begingroup$
    Is there really no predefined SecureRandom class in .NET? It sounds really strange that everyone would have to write this class by themselves.
    $endgroup$
    – Roland Illig
    1 hour ago










  • $begingroup$
    This code looks really bloated, compared to the original code. Generating a password using a given simple and fixed scheme should take no more than 30 lines of code.
    $endgroup$
    – Roland Illig
    1 hour ago










  • $begingroup$
    @RolandIllig Sadly, there really isn't anything in the standard lib; I'd have used it. One concedes that the code is bloated but figured that having parameters is more "maintainable" in the long run; for example, one might be dealing with many clients that all have different configurations (or, heaven forbid, a client that changes their requirements over time).
    $endgroup$
    – Kittoes0124
    59 mins ago














Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f224033%2fpassword-maker-in-c%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









4












$begingroup$

When calling your method like this:



 for (int i = 0; i < 10; i++)

Console.WriteLine(GenerateSimplePassword());



I get this result:



 88yuQg7H
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o


which is not so random.



The reason is the Random object being instantiated each time the method is called. Instead you must move the Random object outside the method as a static one time initialized field.




You could make the valid chars as a static field string:



const string chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int DigitStart = chars.IndexOf('0');
static int UpperStart = chars.IndexOf('A');



Instead of concatenate strings throughout the process it would be better to use:



char[] password = new char[length]



it will improve both performance and readability:



password[0] = chars[random.Next(0, DigitStart)];



All in all my suggestion would be something like:



public static class PasswordGenerator


static readonly Random random = new Random();
const string chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int DigitStart = chars.IndexOf('0');
static int UpperStart = chars.IndexOf('A');

public static string GenerateSimplePassword(int length = 8)

length = Math.Max(length, 8);

char[] password = new char[length];
password[0] = chars[random.Next(0, DigitStart)];
password[1] = chars[random.Next(DigitStart, UpperStart)];
password[2] = chars[random.Next(UpperStart, chars.Length)];

for (int i = 3; i < length; i++)

password[i] = chars[random.Next(0, chars.Length)];


return new string(password.OrderBy(_ => random.Next()).ToArray());







share|improve this answer











$endgroup$












  • $begingroup$
    Thanks for the answer. The string is shuffled with generatedCode.OrderBy(x => r.Next(), how the hacker could find out anything? But it gives a hint about bad code readability. I don't see how the simpler solution would assure that I get any of the 3 different char class. Does it?
    $endgroup$
    – Thomas Ayoub
    6 hours ago










  • $begingroup$
    @ThomasAyoub: Sorry, I missed the last line of code. But why do you want at least one of each?
    $endgroup$
    – Henrik Hansen
    6 hours ago










  • $begingroup$
    Which version of .NET you use? According to @Eric Lippert this problem with Random is resolved in later versions.
    $endgroup$
    – dfhwze
    5 hours ago










  • $begingroup$
    @dfhwze: I use 4.8. I've see he commented that somewhere, but can't remember where. Apparently it is not solved yet. Maybe in core? his blog: "Fortunately, I have just learned from an attentive reader that this problem has been fixed in some versions of the CLR; exactly which I am not sure..."?
    $endgroup$
    – Henrik Hansen
    5 hours ago











  • $begingroup$
    @Eric Lippert Eric's answer here talks about it: codereview.stackexchange.com/questions/223593/…
    $endgroup$
    – dfhwze
    5 hours ago















4












$begingroup$

When calling your method like this:



 for (int i = 0; i < 10; i++)

Console.WriteLine(GenerateSimplePassword());



I get this result:



 88yuQg7H
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o


which is not so random.



The reason is the Random object being instantiated each time the method is called. Instead you must move the Random object outside the method as a static one time initialized field.




You could make the valid chars as a static field string:



const string chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int DigitStart = chars.IndexOf('0');
static int UpperStart = chars.IndexOf('A');



Instead of concatenate strings throughout the process it would be better to use:



char[] password = new char[length]



it will improve both performance and readability:



password[0] = chars[random.Next(0, DigitStart)];



All in all my suggestion would be something like:



public static class PasswordGenerator


static readonly Random random = new Random();
const string chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int DigitStart = chars.IndexOf('0');
static int UpperStart = chars.IndexOf('A');

public static string GenerateSimplePassword(int length = 8)

length = Math.Max(length, 8);

char[] password = new char[length];
password[0] = chars[random.Next(0, DigitStart)];
password[1] = chars[random.Next(DigitStart, UpperStart)];
password[2] = chars[random.Next(UpperStart, chars.Length)];

for (int i = 3; i < length; i++)

password[i] = chars[random.Next(0, chars.Length)];


return new string(password.OrderBy(_ => random.Next()).ToArray());







share|improve this answer











$endgroup$












  • $begingroup$
    Thanks for the answer. The string is shuffled with generatedCode.OrderBy(x => r.Next(), how the hacker could find out anything? But it gives a hint about bad code readability. I don't see how the simpler solution would assure that I get any of the 3 different char class. Does it?
    $endgroup$
    – Thomas Ayoub
    6 hours ago










  • $begingroup$
    @ThomasAyoub: Sorry, I missed the last line of code. But why do you want at least one of each?
    $endgroup$
    – Henrik Hansen
    6 hours ago










  • $begingroup$
    Which version of .NET you use? According to @Eric Lippert this problem with Random is resolved in later versions.
    $endgroup$
    – dfhwze
    5 hours ago










  • $begingroup$
    @dfhwze: I use 4.8. I've see he commented that somewhere, but can't remember where. Apparently it is not solved yet. Maybe in core? his blog: "Fortunately, I have just learned from an attentive reader that this problem has been fixed in some versions of the CLR; exactly which I am not sure..."?
    $endgroup$
    – Henrik Hansen
    5 hours ago











  • $begingroup$
    @Eric Lippert Eric's answer here talks about it: codereview.stackexchange.com/questions/223593/…
    $endgroup$
    – dfhwze
    5 hours ago













4












4








4





$begingroup$

When calling your method like this:



 for (int i = 0; i < 10; i++)

Console.WriteLine(GenerateSimplePassword());



I get this result:



 88yuQg7H
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o


which is not so random.



The reason is the Random object being instantiated each time the method is called. Instead you must move the Random object outside the method as a static one time initialized field.




You could make the valid chars as a static field string:



const string chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int DigitStart = chars.IndexOf('0');
static int UpperStart = chars.IndexOf('A');



Instead of concatenate strings throughout the process it would be better to use:



char[] password = new char[length]



it will improve both performance and readability:



password[0] = chars[random.Next(0, DigitStart)];



All in all my suggestion would be something like:



public static class PasswordGenerator


static readonly Random random = new Random();
const string chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int DigitStart = chars.IndexOf('0');
static int UpperStart = chars.IndexOf('A');

public static string GenerateSimplePassword(int length = 8)

length = Math.Max(length, 8);

char[] password = new char[length];
password[0] = chars[random.Next(0, DigitStart)];
password[1] = chars[random.Next(DigitStart, UpperStart)];
password[2] = chars[random.Next(UpperStart, chars.Length)];

for (int i = 3; i < length; i++)

password[i] = chars[random.Next(0, chars.Length)];


return new string(password.OrderBy(_ => random.Next()).ToArray());







share|improve this answer











$endgroup$



When calling your method like this:



 for (int i = 0; i < 10; i++)

Console.WriteLine(GenerateSimplePassword());



I get this result:



 88yuQg7H
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o
u5UqU36o


which is not so random.



The reason is the Random object being instantiated each time the method is called. Instead you must move the Random object outside the method as a static one time initialized field.




You could make the valid chars as a static field string:



const string chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int DigitStart = chars.IndexOf('0');
static int UpperStart = chars.IndexOf('A');



Instead of concatenate strings throughout the process it would be better to use:



char[] password = new char[length]



it will improve both performance and readability:



password[0] = chars[random.Next(0, DigitStart)];



All in all my suggestion would be something like:



public static class PasswordGenerator


static readonly Random random = new Random();
const string chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static int DigitStart = chars.IndexOf('0');
static int UpperStart = chars.IndexOf('A');

public static string GenerateSimplePassword(int length = 8)

length = Math.Max(length, 8);

char[] password = new char[length];
password[0] = chars[random.Next(0, DigitStart)];
password[1] = chars[random.Next(DigitStart, UpperStart)];
password[2] = chars[random.Next(UpperStart, chars.Length)];

for (int i = 3; i < length; i++)

password[i] = chars[random.Next(0, chars.Length)];


return new string(password.OrderBy(_ => random.Next()).ToArray());








share|improve this answer














share|improve this answer



share|improve this answer








edited 4 hours ago









dfhwze

4,4601 gold badge7 silver badges34 bronze badges




4,4601 gold badge7 silver badges34 bronze badges










answered 6 hours ago









Henrik HansenHenrik Hansen

10.5k1 gold badge13 silver badges38 bronze badges




10.5k1 gold badge13 silver badges38 bronze badges











  • $begingroup$
    Thanks for the answer. The string is shuffled with generatedCode.OrderBy(x => r.Next(), how the hacker could find out anything? But it gives a hint about bad code readability. I don't see how the simpler solution would assure that I get any of the 3 different char class. Does it?
    $endgroup$
    – Thomas Ayoub
    6 hours ago










  • $begingroup$
    @ThomasAyoub: Sorry, I missed the last line of code. But why do you want at least one of each?
    $endgroup$
    – Henrik Hansen
    6 hours ago










  • $begingroup$
    Which version of .NET you use? According to @Eric Lippert this problem with Random is resolved in later versions.
    $endgroup$
    – dfhwze
    5 hours ago










  • $begingroup$
    @dfhwze: I use 4.8. I've see he commented that somewhere, but can't remember where. Apparently it is not solved yet. Maybe in core? his blog: "Fortunately, I have just learned from an attentive reader that this problem has been fixed in some versions of the CLR; exactly which I am not sure..."?
    $endgroup$
    – Henrik Hansen
    5 hours ago











  • $begingroup$
    @Eric Lippert Eric's answer here talks about it: codereview.stackexchange.com/questions/223593/…
    $endgroup$
    – dfhwze
    5 hours ago
















  • $begingroup$
    Thanks for the answer. The string is shuffled with generatedCode.OrderBy(x => r.Next(), how the hacker could find out anything? But it gives a hint about bad code readability. I don't see how the simpler solution would assure that I get any of the 3 different char class. Does it?
    $endgroup$
    – Thomas Ayoub
    6 hours ago










  • $begingroup$
    @ThomasAyoub: Sorry, I missed the last line of code. But why do you want at least one of each?
    $endgroup$
    – Henrik Hansen
    6 hours ago










  • $begingroup$
    Which version of .NET you use? According to @Eric Lippert this problem with Random is resolved in later versions.
    $endgroup$
    – dfhwze
    5 hours ago










  • $begingroup$
    @dfhwze: I use 4.8. I've see he commented that somewhere, but can't remember where. Apparently it is not solved yet. Maybe in core? his blog: "Fortunately, I have just learned from an attentive reader that this problem has been fixed in some versions of the CLR; exactly which I am not sure..."?
    $endgroup$
    – Henrik Hansen
    5 hours ago











  • $begingroup$
    @Eric Lippert Eric's answer here talks about it: codereview.stackexchange.com/questions/223593/…
    $endgroup$
    – dfhwze
    5 hours ago















$begingroup$
Thanks for the answer. The string is shuffled with generatedCode.OrderBy(x => r.Next(), how the hacker could find out anything? But it gives a hint about bad code readability. I don't see how the simpler solution would assure that I get any of the 3 different char class. Does it?
$endgroup$
– Thomas Ayoub
6 hours ago




$begingroup$
Thanks for the answer. The string is shuffled with generatedCode.OrderBy(x => r.Next(), how the hacker could find out anything? But it gives a hint about bad code readability. I don't see how the simpler solution would assure that I get any of the 3 different char class. Does it?
$endgroup$
– Thomas Ayoub
6 hours ago












$begingroup$
@ThomasAyoub: Sorry, I missed the last line of code. But why do you want at least one of each?
$endgroup$
– Henrik Hansen
6 hours ago




$begingroup$
@ThomasAyoub: Sorry, I missed the last line of code. But why do you want at least one of each?
$endgroup$
– Henrik Hansen
6 hours ago












$begingroup$
Which version of .NET you use? According to @Eric Lippert this problem with Random is resolved in later versions.
$endgroup$
– dfhwze
5 hours ago




$begingroup$
Which version of .NET you use? According to @Eric Lippert this problem with Random is resolved in later versions.
$endgroup$
– dfhwze
5 hours ago












$begingroup$
@dfhwze: I use 4.8. I've see he commented that somewhere, but can't remember where. Apparently it is not solved yet. Maybe in core? his blog: "Fortunately, I have just learned from an attentive reader that this problem has been fixed in some versions of the CLR; exactly which I am not sure..."?
$endgroup$
– Henrik Hansen
5 hours ago





$begingroup$
@dfhwze: I use 4.8. I've see he commented that somewhere, but can't remember where. Apparently it is not solved yet. Maybe in core? his blog: "Fortunately, I have just learned from an attentive reader that this problem has been fixed in some versions of the CLR; exactly which I am not sure..."?
$endgroup$
– Henrik Hansen
5 hours ago













$begingroup$
@Eric Lippert Eric's answer here talks about it: codereview.stackexchange.com/questions/223593/…
$endgroup$
– dfhwze
5 hours ago




$begingroup$
@Eric Lippert Eric's answer here talks about it: codereview.stackexchange.com/questions/223593/…
$endgroup$
– dfhwze
5 hours ago













3












$begingroup$

Fully Refactored Code: see this Gist



Example Usage:



static void Main(string[] args) 
var generator = new PasswordGenerator(new PasswordGeneratorOptions
MinimumNumberOfLowerCaseCharacters = 1,
MinimumNumberOfNumericCharacters = 1,
MinimumNumberOfUpperCaseCharacters = 1,
OutputLength = 8,
RandomNumberGenerator = new SecureRandom(),
SpecialCharacters = new char[0] ,
);

Console.WriteLine(generator.Next());



Review Comments:



First off, using Random as a source of entropy probably isn't the best idea since it is possible (however unlikely) that an attacker could gain the underlying seed; with it they would trivially be able to deduce all of the outputs used to generate our passwords. Let's implement a generator that uses a static instance of the RNGCryptoServiceProvider class instead:



// makes it easier to replace the implementation I demonstrate with something better
public interface ISecureRandom

uint Next(uint x, uint y);


// a possible implementation of a secure RNG, not exhaustively tested...
public sealed class SecureRandom : ISecureRandom

private static RandomNumberGenerator m_randomNumberGenerator = new RNGCryptoServiceProvider();

public SecureRandom(RandomNumberGenerator randomNumberGenerator)
if (null == randomNumberGenerator)
throw new ArgumentNullException(paramName: nameof(randomNumberGenerator));


m_randomNumberGenerator = randomNumberGenerator;

public SecureRandom() : this(new RNGCryptoServiceProvider())

public byte[] GetBytes(byte[] buffer)
m_randomNumberGenerator.GetBytes(buffer);

return buffer;

public byte[] GetBytes(int count) => GetBytes(new byte[count]);
public uint Next() => BitConverter.ToUInt32(GetBytes(sizeof(uint)), 0);
public uint Next(uint x, uint y)
if (x > y)
var z = x;

x = y;
y = z;


var range = (y - x);

if (range == 0)
return x;

else if (range == uint.MaxValue)
return Next();

else
return (Next(exclusiveHigh: range) + x);



private uint Next(uint exclusiveHigh)
var range = (uint.MaxValue - (((uint.MaxValue % exclusiveHigh) + 1) % exclusiveHigh));
var result = 0U;

do
result = Next();
while (result > range);

return (result % exclusiveHigh);




Regarding maintainability, we could start by creating a proper class to encapsulate all of our settings:



public sealed class PasswordGeneratorOptions

private static readonly char[] DefaultSpecialChars = new[] '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '=', '`', '~', '_', '+', ',', '.', ''', '"', ';', ':', '?', ';

public int MinimumNumberOfNumericCharacters get; set;
public int MinimumNumberOfLowerCaseCharacters get; set;
public int MinimumNumberOfSpecialCharacters get; set;
public int MinimumNumberOfUpperCaseCharacters get; set;
public int OutputLength get; set;
public ISecureRandom RandomNumberGenerator get; set;
public IReadOnlyList<char> SpecialCharacters get; set;

public PasswordGeneratorOptions()
MinimumNumberOfLowerCaseCharacters = 0;
MinimumNumberOfNumericCharacters = 0;
MinimumNumberOfSpecialCharacters = 0;
MinimumNumberOfUpperCaseCharacters = 0;
SpecialCharacters = DefaultSpecialChars;




Then we can declare a PasswordGenerator class that consumes our options:



public sealed class PasswordGenerator

private readonly PasswordGeneratorOptions m_options;

public PasswordGenerator(PasswordGeneratorOptions options)
if (options.OutputLength < (options.MinimumNumberOfLowerCaseCharacters + options.MinimumNumberOfNumericCharacters + options.MinimumNumberOfSpecialCharacters + options.MinimumNumberOfUpperCaseCharacters))
throw new ArgumentOutOfRangeException(message: "output length must be greater than or equal to the sum of all MinimumNumber* properties", actualValue: options.OutputLength, paramName: nameof(options.OutputLength));


m_options = options;




Now we can start thinking about implementation details, I personally like the idea of breaking things up into various categories so that we can provide more complex configuration options later. Let's try classifying your characters into lower-case, upper-case, number, and special. The ASCII encoding has largely taken care of all of this for us "for free" since three of these four classes have contiguous regions in the specification; we can write a simple generator for each like so:



var randomNumberGenerator = new SecureRandom();

Func<char> GetAsciiLetterLowerCase = () => ((char)randomNumberGenerator.Next(97, 123));
Func<char> GetAsciiLetterUpperCase = () => ((char)randomNumberGenerator.Next(65, 91));
Func<char> GetAsciiNumber = () => ((char)randomNumberGenerator.Next(48, 58));


Special characters aren't as easy so we'll resort to using a table of values:



var specialCharacters = new char[] '!', '@', '#', ;

Func<char> GetAsciiSpecial = () => specialCharacters[randomNumberGenerator.Next(0U, ((uint)specialCharacters.Count))];


The only remaining thing left to do is implement the generator function. This can be accomplished by more or less following the same strategy you already have in place. I chose to implement Fisher–Yates shuffle instead of using LINQ for the final randomization step:



public string Next() 
var index = 0;
var length = m_options.OutputLength;
var randomNumberGenerator = m_options.RandomNumberGenerator;
var result = new char[length];
var useSpecial = (0 < m_options.SpecialCharacters.Count);

for (var i = 0; (i < m_options.MinimumNumberOfLowerCaseCharacters); i++)
result[index++] = m_getAsciiLetterLowerCase();


for (var i = 0; (i < m_options.MinimumNumberOfNumericCharacters); i++)
result[index++] = m_getAsciiNumeric();


for (var i = 0; (i < m_options.MinimumNumberOfSpecialCharacters); i++)
result[index++] = m_getAsciiSpecial();


for (var i = 0; (i < m_options.MinimumNumberOfUpperCaseCharacters); i++)
result[index++] = m_getAsciiLetterUpperCase();


for (var i = index; (i < length); i++)
char c;

switch (randomNumberGenerator.Next(0U, (useSpecial ? 4U : 3U)))
case 3U:
c = m_getAsciiSpecial();
break;
case 2U:
c = m_getAsciiNumeric();
break;
case 1U:
c = m_getAsciiLetterLowerCase();
break;
case 0U:
c = m_getAsciiLetterUpperCase();
break;
default:
throw new InvalidOperationException();


result[i] = c;


FisherYatesShuffle(randomNumberGenerator, result);

return new string(result);


private static void SwapRandom<T>(ISecureRandom randomNumberGenerator, IList<T> list, uint indexLowerBound, uint indexUpperBound)
var randomIndex = randomNumberGenerator.Next(indexLowerBound, indexUpperBound);
var tempValue = list[(int)randomIndex];

list[(int)randomIndex] = list[(int)indexUpperBound];
list[(int)indexUpperBound] = tempValue;

private static void FisherYatesShuffle<T>(ISecureRandom randomNumberGenerator, IList<T> list)
var length = list.Count;
var offset = 0U;

while (offset < length)
SwapRandom(randomNumberGenerator, list, 0U, offset++);







share|improve this answer











$endgroup$








  • 1




    $begingroup$
    This can throw an IndexOutOfRange exception in the m_GetAsciiSpecial generator, right? If requested password length is greater than length of the passed in list of special characters. Shouldn't the generator use the length of the list, not the password length?
    $endgroup$
    – TomG
    4 hours ago






  • 1




    $begingroup$
    @TomG Good catch! That's a bug; fixed.
    $endgroup$
    – Kittoes0124
    4 hours ago











  • $begingroup$
    Is there really no predefined SecureRandom class in .NET? It sounds really strange that everyone would have to write this class by themselves.
    $endgroup$
    – Roland Illig
    1 hour ago










  • $begingroup$
    This code looks really bloated, compared to the original code. Generating a password using a given simple and fixed scheme should take no more than 30 lines of code.
    $endgroup$
    – Roland Illig
    1 hour ago










  • $begingroup$
    @RolandIllig Sadly, there really isn't anything in the standard lib; I'd have used it. One concedes that the code is bloated but figured that having parameters is more "maintainable" in the long run; for example, one might be dealing with many clients that all have different configurations (or, heaven forbid, a client that changes their requirements over time).
    $endgroup$
    – Kittoes0124
    59 mins ago
















3












$begingroup$

Fully Refactored Code: see this Gist



Example Usage:



static void Main(string[] args) 
var generator = new PasswordGenerator(new PasswordGeneratorOptions
MinimumNumberOfLowerCaseCharacters = 1,
MinimumNumberOfNumericCharacters = 1,
MinimumNumberOfUpperCaseCharacters = 1,
OutputLength = 8,
RandomNumberGenerator = new SecureRandom(),
SpecialCharacters = new char[0] ,
);

Console.WriteLine(generator.Next());



Review Comments:



First off, using Random as a source of entropy probably isn't the best idea since it is possible (however unlikely) that an attacker could gain the underlying seed; with it they would trivially be able to deduce all of the outputs used to generate our passwords. Let's implement a generator that uses a static instance of the RNGCryptoServiceProvider class instead:



// makes it easier to replace the implementation I demonstrate with something better
public interface ISecureRandom

uint Next(uint x, uint y);


// a possible implementation of a secure RNG, not exhaustively tested...
public sealed class SecureRandom : ISecureRandom

private static RandomNumberGenerator m_randomNumberGenerator = new RNGCryptoServiceProvider();

public SecureRandom(RandomNumberGenerator randomNumberGenerator)
if (null == randomNumberGenerator)
throw new ArgumentNullException(paramName: nameof(randomNumberGenerator));


m_randomNumberGenerator = randomNumberGenerator;

public SecureRandom() : this(new RNGCryptoServiceProvider())

public byte[] GetBytes(byte[] buffer)
m_randomNumberGenerator.GetBytes(buffer);

return buffer;

public byte[] GetBytes(int count) => GetBytes(new byte[count]);
public uint Next() => BitConverter.ToUInt32(GetBytes(sizeof(uint)), 0);
public uint Next(uint x, uint y)
if (x > y)
var z = x;

x = y;
y = z;


var range = (y - x);

if (range == 0)
return x;

else if (range == uint.MaxValue)
return Next();

else
return (Next(exclusiveHigh: range) + x);



private uint Next(uint exclusiveHigh)
var range = (uint.MaxValue - (((uint.MaxValue % exclusiveHigh) + 1) % exclusiveHigh));
var result = 0U;

do
result = Next();
while (result > range);

return (result % exclusiveHigh);




Regarding maintainability, we could start by creating a proper class to encapsulate all of our settings:



public sealed class PasswordGeneratorOptions

private static readonly char[] DefaultSpecialChars = new[] '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '=', '`', '~', '_', '+', ',', '.', ''', '"', ';', ':', '?', ';

public int MinimumNumberOfNumericCharacters get; set;
public int MinimumNumberOfLowerCaseCharacters get; set;
public int MinimumNumberOfSpecialCharacters get; set;
public int MinimumNumberOfUpperCaseCharacters get; set;
public int OutputLength get; set;
public ISecureRandom RandomNumberGenerator get; set;
public IReadOnlyList<char> SpecialCharacters get; set;

public PasswordGeneratorOptions()
MinimumNumberOfLowerCaseCharacters = 0;
MinimumNumberOfNumericCharacters = 0;
MinimumNumberOfSpecialCharacters = 0;
MinimumNumberOfUpperCaseCharacters = 0;
SpecialCharacters = DefaultSpecialChars;




Then we can declare a PasswordGenerator class that consumes our options:



public sealed class PasswordGenerator

private readonly PasswordGeneratorOptions m_options;

public PasswordGenerator(PasswordGeneratorOptions options)
if (options.OutputLength < (options.MinimumNumberOfLowerCaseCharacters + options.MinimumNumberOfNumericCharacters + options.MinimumNumberOfSpecialCharacters + options.MinimumNumberOfUpperCaseCharacters))
throw new ArgumentOutOfRangeException(message: "output length must be greater than or equal to the sum of all MinimumNumber* properties", actualValue: options.OutputLength, paramName: nameof(options.OutputLength));


m_options = options;




Now we can start thinking about implementation details, I personally like the idea of breaking things up into various categories so that we can provide more complex configuration options later. Let's try classifying your characters into lower-case, upper-case, number, and special. The ASCII encoding has largely taken care of all of this for us "for free" since three of these four classes have contiguous regions in the specification; we can write a simple generator for each like so:



var randomNumberGenerator = new SecureRandom();

Func<char> GetAsciiLetterLowerCase = () => ((char)randomNumberGenerator.Next(97, 123));
Func<char> GetAsciiLetterUpperCase = () => ((char)randomNumberGenerator.Next(65, 91));
Func<char> GetAsciiNumber = () => ((char)randomNumberGenerator.Next(48, 58));


Special characters aren't as easy so we'll resort to using a table of values:



var specialCharacters = new char[] '!', '@', '#', ;

Func<char> GetAsciiSpecial = () => specialCharacters[randomNumberGenerator.Next(0U, ((uint)specialCharacters.Count))];


The only remaining thing left to do is implement the generator function. This can be accomplished by more or less following the same strategy you already have in place. I chose to implement Fisher–Yates shuffle instead of using LINQ for the final randomization step:



public string Next() 
var index = 0;
var length = m_options.OutputLength;
var randomNumberGenerator = m_options.RandomNumberGenerator;
var result = new char[length];
var useSpecial = (0 < m_options.SpecialCharacters.Count);

for (var i = 0; (i < m_options.MinimumNumberOfLowerCaseCharacters); i++)
result[index++] = m_getAsciiLetterLowerCase();


for (var i = 0; (i < m_options.MinimumNumberOfNumericCharacters); i++)
result[index++] = m_getAsciiNumeric();


for (var i = 0; (i < m_options.MinimumNumberOfSpecialCharacters); i++)
result[index++] = m_getAsciiSpecial();


for (var i = 0; (i < m_options.MinimumNumberOfUpperCaseCharacters); i++)
result[index++] = m_getAsciiLetterUpperCase();


for (var i = index; (i < length); i++)
char c;

switch (randomNumberGenerator.Next(0U, (useSpecial ? 4U : 3U)))
case 3U:
c = m_getAsciiSpecial();
break;
case 2U:
c = m_getAsciiNumeric();
break;
case 1U:
c = m_getAsciiLetterLowerCase();
break;
case 0U:
c = m_getAsciiLetterUpperCase();
break;
default:
throw new InvalidOperationException();


result[i] = c;


FisherYatesShuffle(randomNumberGenerator, result);

return new string(result);


private static void SwapRandom<T>(ISecureRandom randomNumberGenerator, IList<T> list, uint indexLowerBound, uint indexUpperBound)
var randomIndex = randomNumberGenerator.Next(indexLowerBound, indexUpperBound);
var tempValue = list[(int)randomIndex];

list[(int)randomIndex] = list[(int)indexUpperBound];
list[(int)indexUpperBound] = tempValue;

private static void FisherYatesShuffle<T>(ISecureRandom randomNumberGenerator, IList<T> list)
var length = list.Count;
var offset = 0U;

while (offset < length)
SwapRandom(randomNumberGenerator, list, 0U, offset++);







share|improve this answer











$endgroup$








  • 1




    $begingroup$
    This can throw an IndexOutOfRange exception in the m_GetAsciiSpecial generator, right? If requested password length is greater than length of the passed in list of special characters. Shouldn't the generator use the length of the list, not the password length?
    $endgroup$
    – TomG
    4 hours ago






  • 1




    $begingroup$
    @TomG Good catch! That's a bug; fixed.
    $endgroup$
    – Kittoes0124
    4 hours ago











  • $begingroup$
    Is there really no predefined SecureRandom class in .NET? It sounds really strange that everyone would have to write this class by themselves.
    $endgroup$
    – Roland Illig
    1 hour ago










  • $begingroup$
    This code looks really bloated, compared to the original code. Generating a password using a given simple and fixed scheme should take no more than 30 lines of code.
    $endgroup$
    – Roland Illig
    1 hour ago










  • $begingroup$
    @RolandIllig Sadly, there really isn't anything in the standard lib; I'd have used it. One concedes that the code is bloated but figured that having parameters is more "maintainable" in the long run; for example, one might be dealing with many clients that all have different configurations (or, heaven forbid, a client that changes their requirements over time).
    $endgroup$
    – Kittoes0124
    59 mins ago














3












3








3





$begingroup$

Fully Refactored Code: see this Gist



Example Usage:



static void Main(string[] args) 
var generator = new PasswordGenerator(new PasswordGeneratorOptions
MinimumNumberOfLowerCaseCharacters = 1,
MinimumNumberOfNumericCharacters = 1,
MinimumNumberOfUpperCaseCharacters = 1,
OutputLength = 8,
RandomNumberGenerator = new SecureRandom(),
SpecialCharacters = new char[0] ,
);

Console.WriteLine(generator.Next());



Review Comments:



First off, using Random as a source of entropy probably isn't the best idea since it is possible (however unlikely) that an attacker could gain the underlying seed; with it they would trivially be able to deduce all of the outputs used to generate our passwords. Let's implement a generator that uses a static instance of the RNGCryptoServiceProvider class instead:



// makes it easier to replace the implementation I demonstrate with something better
public interface ISecureRandom

uint Next(uint x, uint y);


// a possible implementation of a secure RNG, not exhaustively tested...
public sealed class SecureRandom : ISecureRandom

private static RandomNumberGenerator m_randomNumberGenerator = new RNGCryptoServiceProvider();

public SecureRandom(RandomNumberGenerator randomNumberGenerator)
if (null == randomNumberGenerator)
throw new ArgumentNullException(paramName: nameof(randomNumberGenerator));


m_randomNumberGenerator = randomNumberGenerator;

public SecureRandom() : this(new RNGCryptoServiceProvider())

public byte[] GetBytes(byte[] buffer)
m_randomNumberGenerator.GetBytes(buffer);

return buffer;

public byte[] GetBytes(int count) => GetBytes(new byte[count]);
public uint Next() => BitConverter.ToUInt32(GetBytes(sizeof(uint)), 0);
public uint Next(uint x, uint y)
if (x > y)
var z = x;

x = y;
y = z;


var range = (y - x);

if (range == 0)
return x;

else if (range == uint.MaxValue)
return Next();

else
return (Next(exclusiveHigh: range) + x);



private uint Next(uint exclusiveHigh)
var range = (uint.MaxValue - (((uint.MaxValue % exclusiveHigh) + 1) % exclusiveHigh));
var result = 0U;

do
result = Next();
while (result > range);

return (result % exclusiveHigh);




Regarding maintainability, we could start by creating a proper class to encapsulate all of our settings:



public sealed class PasswordGeneratorOptions

private static readonly char[] DefaultSpecialChars = new[] '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '=', '`', '~', '_', '+', ',', '.', ''', '"', ';', ':', '?', ';

public int MinimumNumberOfNumericCharacters get; set;
public int MinimumNumberOfLowerCaseCharacters get; set;
public int MinimumNumberOfSpecialCharacters get; set;
public int MinimumNumberOfUpperCaseCharacters get; set;
public int OutputLength get; set;
public ISecureRandom RandomNumberGenerator get; set;
public IReadOnlyList<char> SpecialCharacters get; set;

public PasswordGeneratorOptions()
MinimumNumberOfLowerCaseCharacters = 0;
MinimumNumberOfNumericCharacters = 0;
MinimumNumberOfSpecialCharacters = 0;
MinimumNumberOfUpperCaseCharacters = 0;
SpecialCharacters = DefaultSpecialChars;




Then we can declare a PasswordGenerator class that consumes our options:



public sealed class PasswordGenerator

private readonly PasswordGeneratorOptions m_options;

public PasswordGenerator(PasswordGeneratorOptions options)
if (options.OutputLength < (options.MinimumNumberOfLowerCaseCharacters + options.MinimumNumberOfNumericCharacters + options.MinimumNumberOfSpecialCharacters + options.MinimumNumberOfUpperCaseCharacters))
throw new ArgumentOutOfRangeException(message: "output length must be greater than or equal to the sum of all MinimumNumber* properties", actualValue: options.OutputLength, paramName: nameof(options.OutputLength));


m_options = options;




Now we can start thinking about implementation details, I personally like the idea of breaking things up into various categories so that we can provide more complex configuration options later. Let's try classifying your characters into lower-case, upper-case, number, and special. The ASCII encoding has largely taken care of all of this for us "for free" since three of these four classes have contiguous regions in the specification; we can write a simple generator for each like so:



var randomNumberGenerator = new SecureRandom();

Func<char> GetAsciiLetterLowerCase = () => ((char)randomNumberGenerator.Next(97, 123));
Func<char> GetAsciiLetterUpperCase = () => ((char)randomNumberGenerator.Next(65, 91));
Func<char> GetAsciiNumber = () => ((char)randomNumberGenerator.Next(48, 58));


Special characters aren't as easy so we'll resort to using a table of values:



var specialCharacters = new char[] '!', '@', '#', ;

Func<char> GetAsciiSpecial = () => specialCharacters[randomNumberGenerator.Next(0U, ((uint)specialCharacters.Count))];


The only remaining thing left to do is implement the generator function. This can be accomplished by more or less following the same strategy you already have in place. I chose to implement Fisher–Yates shuffle instead of using LINQ for the final randomization step:



public string Next() 
var index = 0;
var length = m_options.OutputLength;
var randomNumberGenerator = m_options.RandomNumberGenerator;
var result = new char[length];
var useSpecial = (0 < m_options.SpecialCharacters.Count);

for (var i = 0; (i < m_options.MinimumNumberOfLowerCaseCharacters); i++)
result[index++] = m_getAsciiLetterLowerCase();


for (var i = 0; (i < m_options.MinimumNumberOfNumericCharacters); i++)
result[index++] = m_getAsciiNumeric();


for (var i = 0; (i < m_options.MinimumNumberOfSpecialCharacters); i++)
result[index++] = m_getAsciiSpecial();


for (var i = 0; (i < m_options.MinimumNumberOfUpperCaseCharacters); i++)
result[index++] = m_getAsciiLetterUpperCase();


for (var i = index; (i < length); i++)
char c;

switch (randomNumberGenerator.Next(0U, (useSpecial ? 4U : 3U)))
case 3U:
c = m_getAsciiSpecial();
break;
case 2U:
c = m_getAsciiNumeric();
break;
case 1U:
c = m_getAsciiLetterLowerCase();
break;
case 0U:
c = m_getAsciiLetterUpperCase();
break;
default:
throw new InvalidOperationException();


result[i] = c;


FisherYatesShuffle(randomNumberGenerator, result);

return new string(result);


private static void SwapRandom<T>(ISecureRandom randomNumberGenerator, IList<T> list, uint indexLowerBound, uint indexUpperBound)
var randomIndex = randomNumberGenerator.Next(indexLowerBound, indexUpperBound);
var tempValue = list[(int)randomIndex];

list[(int)randomIndex] = list[(int)indexUpperBound];
list[(int)indexUpperBound] = tempValue;

private static void FisherYatesShuffle<T>(ISecureRandom randomNumberGenerator, IList<T> list)
var length = list.Count;
var offset = 0U;

while (offset < length)
SwapRandom(randomNumberGenerator, list, 0U, offset++);







share|improve this answer











$endgroup$



Fully Refactored Code: see this Gist



Example Usage:



static void Main(string[] args) 
var generator = new PasswordGenerator(new PasswordGeneratorOptions
MinimumNumberOfLowerCaseCharacters = 1,
MinimumNumberOfNumericCharacters = 1,
MinimumNumberOfUpperCaseCharacters = 1,
OutputLength = 8,
RandomNumberGenerator = new SecureRandom(),
SpecialCharacters = new char[0] ,
);

Console.WriteLine(generator.Next());



Review Comments:



First off, using Random as a source of entropy probably isn't the best idea since it is possible (however unlikely) that an attacker could gain the underlying seed; with it they would trivially be able to deduce all of the outputs used to generate our passwords. Let's implement a generator that uses a static instance of the RNGCryptoServiceProvider class instead:



// makes it easier to replace the implementation I demonstrate with something better
public interface ISecureRandom

uint Next(uint x, uint y);


// a possible implementation of a secure RNG, not exhaustively tested...
public sealed class SecureRandom : ISecureRandom

private static RandomNumberGenerator m_randomNumberGenerator = new RNGCryptoServiceProvider();

public SecureRandom(RandomNumberGenerator randomNumberGenerator)
if (null == randomNumberGenerator)
throw new ArgumentNullException(paramName: nameof(randomNumberGenerator));


m_randomNumberGenerator = randomNumberGenerator;

public SecureRandom() : this(new RNGCryptoServiceProvider())

public byte[] GetBytes(byte[] buffer)
m_randomNumberGenerator.GetBytes(buffer);

return buffer;

public byte[] GetBytes(int count) => GetBytes(new byte[count]);
public uint Next() => BitConverter.ToUInt32(GetBytes(sizeof(uint)), 0);
public uint Next(uint x, uint y)
if (x > y)
var z = x;

x = y;
y = z;


var range = (y - x);

if (range == 0)
return x;

else if (range == uint.MaxValue)
return Next();

else
return (Next(exclusiveHigh: range) + x);



private uint Next(uint exclusiveHigh)
var range = (uint.MaxValue - (((uint.MaxValue % exclusiveHigh) + 1) % exclusiveHigh));
var result = 0U;

do
result = Next();
while (result > range);

return (result % exclusiveHigh);




Regarding maintainability, we could start by creating a proper class to encapsulate all of our settings:



public sealed class PasswordGeneratorOptions

private static readonly char[] DefaultSpecialChars = new[] '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '=', '`', '~', '_', '+', ',', '.', ''', '"', ';', ':', '?', ';

public int MinimumNumberOfNumericCharacters get; set;
public int MinimumNumberOfLowerCaseCharacters get; set;
public int MinimumNumberOfSpecialCharacters get; set;
public int MinimumNumberOfUpperCaseCharacters get; set;
public int OutputLength get; set;
public ISecureRandom RandomNumberGenerator get; set;
public IReadOnlyList<char> SpecialCharacters get; set;

public PasswordGeneratorOptions()
MinimumNumberOfLowerCaseCharacters = 0;
MinimumNumberOfNumericCharacters = 0;
MinimumNumberOfSpecialCharacters = 0;
MinimumNumberOfUpperCaseCharacters = 0;
SpecialCharacters = DefaultSpecialChars;




Then we can declare a PasswordGenerator class that consumes our options:



public sealed class PasswordGenerator

private readonly PasswordGeneratorOptions m_options;

public PasswordGenerator(PasswordGeneratorOptions options)
if (options.OutputLength < (options.MinimumNumberOfLowerCaseCharacters + options.MinimumNumberOfNumericCharacters + options.MinimumNumberOfSpecialCharacters + options.MinimumNumberOfUpperCaseCharacters))
throw new ArgumentOutOfRangeException(message: "output length must be greater than or equal to the sum of all MinimumNumber* properties", actualValue: options.OutputLength, paramName: nameof(options.OutputLength));


m_options = options;




Now we can start thinking about implementation details, I personally like the idea of breaking things up into various categories so that we can provide more complex configuration options later. Let's try classifying your characters into lower-case, upper-case, number, and special. The ASCII encoding has largely taken care of all of this for us "for free" since three of these four classes have contiguous regions in the specification; we can write a simple generator for each like so:



var randomNumberGenerator = new SecureRandom();

Func<char> GetAsciiLetterLowerCase = () => ((char)randomNumberGenerator.Next(97, 123));
Func<char> GetAsciiLetterUpperCase = () => ((char)randomNumberGenerator.Next(65, 91));
Func<char> GetAsciiNumber = () => ((char)randomNumberGenerator.Next(48, 58));


Special characters aren't as easy so we'll resort to using a table of values:



var specialCharacters = new char[] '!', '@', '#', ;

Func<char> GetAsciiSpecial = () => specialCharacters[randomNumberGenerator.Next(0U, ((uint)specialCharacters.Count))];


The only remaining thing left to do is implement the generator function. This can be accomplished by more or less following the same strategy you already have in place. I chose to implement Fisher–Yates shuffle instead of using LINQ for the final randomization step:



public string Next() 
var index = 0;
var length = m_options.OutputLength;
var randomNumberGenerator = m_options.RandomNumberGenerator;
var result = new char[length];
var useSpecial = (0 < m_options.SpecialCharacters.Count);

for (var i = 0; (i < m_options.MinimumNumberOfLowerCaseCharacters); i++)
result[index++] = m_getAsciiLetterLowerCase();


for (var i = 0; (i < m_options.MinimumNumberOfNumericCharacters); i++)
result[index++] = m_getAsciiNumeric();


for (var i = 0; (i < m_options.MinimumNumberOfSpecialCharacters); i++)
result[index++] = m_getAsciiSpecial();


for (var i = 0; (i < m_options.MinimumNumberOfUpperCaseCharacters); i++)
result[index++] = m_getAsciiLetterUpperCase();


for (var i = index; (i < length); i++)
char c;

switch (randomNumberGenerator.Next(0U, (useSpecial ? 4U : 3U)))
case 3U:
c = m_getAsciiSpecial();
break;
case 2U:
c = m_getAsciiNumeric();
break;
case 1U:
c = m_getAsciiLetterLowerCase();
break;
case 0U:
c = m_getAsciiLetterUpperCase();
break;
default:
throw new InvalidOperationException();


result[i] = c;


FisherYatesShuffle(randomNumberGenerator, result);

return new string(result);


private static void SwapRandom<T>(ISecureRandom randomNumberGenerator, IList<T> list, uint indexLowerBound, uint indexUpperBound)
var randomIndex = randomNumberGenerator.Next(indexLowerBound, indexUpperBound);
var tempValue = list[(int)randomIndex];

list[(int)randomIndex] = list[(int)indexUpperBound];
list[(int)indexUpperBound] = tempValue;

private static void FisherYatesShuffle<T>(ISecureRandom randomNumberGenerator, IList<T> list)
var length = list.Count;
var offset = 0U;

while (offset < length)
SwapRandom(randomNumberGenerator, list, 0U, offset++);








share|improve this answer














share|improve this answer



share|improve this answer








edited 47 mins ago

























answered 6 hours ago









Kittoes0124Kittoes0124

9862 gold badges7 silver badges20 bronze badges




9862 gold badges7 silver badges20 bronze badges







  • 1




    $begingroup$
    This can throw an IndexOutOfRange exception in the m_GetAsciiSpecial generator, right? If requested password length is greater than length of the passed in list of special characters. Shouldn't the generator use the length of the list, not the password length?
    $endgroup$
    – TomG
    4 hours ago






  • 1




    $begingroup$
    @TomG Good catch! That's a bug; fixed.
    $endgroup$
    – Kittoes0124
    4 hours ago











  • $begingroup$
    Is there really no predefined SecureRandom class in .NET? It sounds really strange that everyone would have to write this class by themselves.
    $endgroup$
    – Roland Illig
    1 hour ago










  • $begingroup$
    This code looks really bloated, compared to the original code. Generating a password using a given simple and fixed scheme should take no more than 30 lines of code.
    $endgroup$
    – Roland Illig
    1 hour ago










  • $begingroup$
    @RolandIllig Sadly, there really isn't anything in the standard lib; I'd have used it. One concedes that the code is bloated but figured that having parameters is more "maintainable" in the long run; for example, one might be dealing with many clients that all have different configurations (or, heaven forbid, a client that changes their requirements over time).
    $endgroup$
    – Kittoes0124
    59 mins ago













  • 1




    $begingroup$
    This can throw an IndexOutOfRange exception in the m_GetAsciiSpecial generator, right? If requested password length is greater than length of the passed in list of special characters. Shouldn't the generator use the length of the list, not the password length?
    $endgroup$
    – TomG
    4 hours ago






  • 1




    $begingroup$
    @TomG Good catch! That's a bug; fixed.
    $endgroup$
    – Kittoes0124
    4 hours ago











  • $begingroup$
    Is there really no predefined SecureRandom class in .NET? It sounds really strange that everyone would have to write this class by themselves.
    $endgroup$
    – Roland Illig
    1 hour ago










  • $begingroup$
    This code looks really bloated, compared to the original code. Generating a password using a given simple and fixed scheme should take no more than 30 lines of code.
    $endgroup$
    – Roland Illig
    1 hour ago










  • $begingroup$
    @RolandIllig Sadly, there really isn't anything in the standard lib; I'd have used it. One concedes that the code is bloated but figured that having parameters is more "maintainable" in the long run; for example, one might be dealing with many clients that all have different configurations (or, heaven forbid, a client that changes their requirements over time).
    $endgroup$
    – Kittoes0124
    59 mins ago








1




1




$begingroup$
This can throw an IndexOutOfRange exception in the m_GetAsciiSpecial generator, right? If requested password length is greater than length of the passed in list of special characters. Shouldn't the generator use the length of the list, not the password length?
$endgroup$
– TomG
4 hours ago




$begingroup$
This can throw an IndexOutOfRange exception in the m_GetAsciiSpecial generator, right? If requested password length is greater than length of the passed in list of special characters. Shouldn't the generator use the length of the list, not the password length?
$endgroup$
– TomG
4 hours ago




1




1




$begingroup$
@TomG Good catch! That's a bug; fixed.
$endgroup$
– Kittoes0124
4 hours ago





$begingroup$
@TomG Good catch! That's a bug; fixed.
$endgroup$
– Kittoes0124
4 hours ago













$begingroup$
Is there really no predefined SecureRandom class in .NET? It sounds really strange that everyone would have to write this class by themselves.
$endgroup$
– Roland Illig
1 hour ago




$begingroup$
Is there really no predefined SecureRandom class in .NET? It sounds really strange that everyone would have to write this class by themselves.
$endgroup$
– Roland Illig
1 hour ago












$begingroup$
This code looks really bloated, compared to the original code. Generating a password using a given simple and fixed scheme should take no more than 30 lines of code.
$endgroup$
– Roland Illig
1 hour ago




$begingroup$
This code looks really bloated, compared to the original code. Generating a password using a given simple and fixed scheme should take no more than 30 lines of code.
$endgroup$
– Roland Illig
1 hour ago












$begingroup$
@RolandIllig Sadly, there really isn't anything in the standard lib; I'd have used it. One concedes that the code is bloated but figured that having parameters is more "maintainable" in the long run; for example, one might be dealing with many clients that all have different configurations (or, heaven forbid, a client that changes their requirements over time).
$endgroup$
– Kittoes0124
59 mins ago





$begingroup$
@RolandIllig Sadly, there really isn't anything in the standard lib; I'd have used it. One concedes that the code is bloated but figured that having parameters is more "maintainable" in the long run; for example, one might be dealing with many clients that all have different configurations (or, heaven forbid, a client that changes their requirements over time).
$endgroup$
– Kittoes0124
59 mins ago


















draft saved

draft discarded
















































Thanks for contributing an answer to Code Review Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

Use MathJax to format equations. MathJax reference.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f224033%2fpassword-maker-in-c%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Canceling a color specificationRandomly assigning color to Graphics3D objects?Default color for Filling in Mathematica 9Coloring specific elements of sets with a prime modified order in an array plotHow to pick a color differing significantly from the colors already in a given color list?Detection of the text colorColor numbers based on their valueCan color schemes for use with ColorData include opacity specification?My dynamic color schemes

Invision Community Contents History See also References External links Navigation menuProprietaryinvisioncommunity.comIPS Community ForumsIPS Community Forumsthis blog entry"License Changes, IP.Board 3.4, and the Future""Interview -- Matt Mecham of Ibforums""CEO Invision Power Board, Matt Mecham Is a Liar, Thief!"IPB License Explanation 1.3, 1.3.1, 2.0, and 2.1ArchivedSecurity Fixes, Updates And Enhancements For IPB 1.3.1Archived"New Demo Accounts - Invision Power Services"the original"New Default Skin"the original"Invision Power Board 3.0.0 and Applications Released"the original"Archived copy"the original"Perpetual licenses being done away with""Release Notes - Invision Power Services""Introducing: IPS Community Suite 4!"Invision Community Release Notes

François Viète Contents Biography Work and thought Bibliography See also Notes Further reading External links Navigation menup. 21Google Bookspp. 75–77Google BooksDe thou (from University of Saint Andrews)ArchivedGoogle BooksGoogle BooksGoogle BooksGoogle booksGoogle Bookscc-parthenay.frL'histoire universelle (fr)Universal History (en)ArchivedAdsabs.harvard.eduPagesperso-orange.frArchive.orgChikara Sasaki. Descartes' mathematical thought p.259Google BooksGoogle BooksGoogle Bookspp. 152 and onwardGoogle BooksGoogle BooksScribd.comGoogle Books1257-7979Google BooksGoogle BooksGoogle BooksGoogle BooksGoogle BooksGoogle BooksGallica.bnf.frGoogle BooksGoogle Books"François Viète"Francois Viète: Father of Modern Algebraic NotationThe Lawyer and the GamblerAbout TarporleySite de Jean-Paul GuichardL'algèbre nouvelle"About the Harmonicon"cb120511976(data)1188044800000 0001 0913 5903n82164680ola2013766880073431702w6vt1sb70287374827140948071409480