Created
December 30, 2022 16:07
-
-
Save vadviktor/e59137cbd64dec2933b3af4407a35395 to your computer and use it in GitHub Desktop.
Generate random passwords in C#, taken from .Net Framework 4.8.2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://github.com/microsoft/referencesource/blob/master/System.Web/CrossSiteScriptingValidation.cs | |
private static readonly char[] StartingChars = new char[] { '<', '&' }; | |
private static bool IsAtoZ(char c) | |
{ | |
return c is >= 'a' and <= 'z' or >= 'A' and <= 'Z'; | |
} | |
private static bool IsDangerousString(string s) | |
{ | |
for (var i = 0;;) | |
{ | |
// Look for the start of one of our patterns | |
var n = s.IndexOfAny(StartingChars, i); | |
// If not found, the string is safe | |
if (n < 0) return false; | |
// If it's the last char, it's safe | |
if (n == s.Length - 1) return false; | |
switch (s[n]) | |
{ | |
case '<': | |
// If the < is followed by a letter or '!', it's unsafe (looks like a tag or HTML comment) | |
if (IsAtoZ(s[n + 1]) || s[n + 1] == '!' || s[n + 1] == '/' || s[n + 1] == '?') return true; | |
break; | |
case '&': | |
// If the & is followed by a #, it's unsafe (e.g. S) | |
if (s[n + 1] == '#') return true; | |
break; | |
} | |
// Continue searching | |
i = n + 1; | |
} | |
} | |
// https://github.com/microsoft/referencesource/blob/master/System.Web/Security/Membership.cs | |
private static readonly char[] Punctuations = "!@#$%^&*()_-+=[{]};:>|./?".ToCharArray(); | |
public static string GeneratePassword(int length, int numberOfNonAlphanumericCharacters) | |
{ | |
if (length is < 1 or > 128) | |
{ | |
throw new ArgumentException("Length is incorrect"); | |
} | |
if (numberOfNonAlphanumericCharacters > length || numberOfNonAlphanumericCharacters < 0) | |
{ | |
throw new ArgumentException("Not enough non-alphanumeric characters"); | |
} | |
string password; | |
do | |
{ | |
var buf = new byte[length]; | |
var cBuf = new char[length]; | |
var count = 0; | |
RandomNumberGenerator.Create().GetBytes(buf); | |
for (int iter = 0; iter < length; iter++) | |
{ | |
var i = buf[iter] % 87; | |
if (i < 10) | |
cBuf[iter] = (char)('0' + i); | |
else if (i < 36) | |
cBuf[iter] = (char)('A' + i - 10); | |
else if (i < 62) | |
cBuf[iter] = (char)('a' + i - 36); | |
else | |
{ | |
cBuf[iter] = Punctuations[i - 62]; | |
count++; | |
} | |
} | |
if (count < numberOfNonAlphanumericCharacters) | |
{ | |
int j; | |
Random rand = new Random(); | |
for (j = 0; j < numberOfNonAlphanumericCharacters - count; j++) | |
{ | |
int k; | |
do | |
{ | |
k = rand.Next(0, length); | |
} while (!Char.IsLetterOrDigit(cBuf[k])); | |
cBuf[k] = Punctuations[rand.Next(0, Punctuations.Length)]; | |
} | |
} | |
password = new string(cBuf); | |
} while (IsDangerousString(password)); | |
return password; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment