开发者

Create a 12 Character Secret

开发者 https://www.devze.com 2023-02-06 08:01 出处:网络
I need to create开发者_如何学运维 some secret 12 character string in C#. It can only have regular latin characters plus digits.

I need to create开发者_如何学运维 some secret 12 character string in C#. It can only have regular latin characters plus digits.

Any ideas of best practices?

Thanks


Here's a good article you could use. It's based on the RNGCryptoServiceProvider which is what needs to be used for ensuring real randomness. If you don't want special characters just remove the PASSWORD_CHARS_SPECIAL array used in the code. Example:

using System;
using System.Security.Cryptography;

public class RandomPassword
{
    // Define default min and max password lengths.
    private static int DEFAULT_MIN_PASSWORD_LENGTH = 8;
    private static int DEFAULT_MAX_PASSWORD_LENGTH = 10;

    // Define supported password characters divided into groups.
    // You can add (or remove) characters to (from) these groups.
    private static string PASSWORD_CHARS_LCASE = "abcdefgijkmnopqrstwxyz";
    private static string PASSWORD_CHARS_UCASE = "ABCDEFGHJKLMNPQRSTWXYZ";
    private static string PASSWORD_CHARS_NUMERIC = "23456789";

    /// <summary>
    /// Generates a random password.
    /// </summary>
    /// <returns>
    /// Randomly generated password.
    /// </returns>
    /// <remarks>
    /// The length of the generated password will be determined at
    /// random. It will be no shorter than the minimum default and
    /// no longer than maximum default.
    /// </remarks>
    public static string Generate()
    {
        return Generate(DEFAULT_MIN_PASSWORD_LENGTH,
                        DEFAULT_MAX_PASSWORD_LENGTH);
    }

    /// <summary>
    /// Generates a random password of the exact length.
    /// </summary>
    /// <param name="length">
    /// Exact password length.
    /// </param>
    /// <returns>
    /// Randomly generated password.
    /// </returns>
    public static string Generate(int length)
    {
        return Generate(length, length);
    }

    /// <summary>
    /// Generates a random password.
    /// </summary>
    /// <param name="minLength">
    /// Minimum password length.
    /// </param>
    /// <param name="maxLength">
    /// Maximum password length.
    /// </param>
    /// <returns>
    /// Randomly generated password.
    /// </returns>
    /// <remarks>
    /// The length of the generated password will be determined at
    /// random and it will fall with the range determined by the
    /// function parameters.
    /// </remarks>
    public static string Generate(int minLength,
                                  int maxLength)
    {
        // Make sure that input parameters are valid.
        if (minLength <= 0 || maxLength <= 0 || minLength > maxLength)
            return null;

        // Create a local array containing supported password characters
        // grouped by types. You can remove character groups from this
        // array, but doing so will weaken the password strength.
        char[][] charGroups = new char[][] 
        {
            PASSWORD_CHARS_LCASE.ToCharArray(),
            PASSWORD_CHARS_UCASE.ToCharArray(),
            PASSWORD_CHARS_NUMERIC.ToCharArray(),
        };

        // Use this array to track the number of unused characters in each
        // character group.
        int[] charsLeftInGroup = new int[charGroups.Length];

        // Initially, all characters in each group are not used.
        for (int i = 0; i < charsLeftInGroup.Length; i++)
            charsLeftInGroup[i] = charGroups[i].Length;

        // Use this array to track (iterate through) unused character groups.
        int[] leftGroupsOrder = new int[charGroups.Length];

        // Initially, all character groups are not used.
        for (int i = 0; i < leftGroupsOrder.Length; i++)
            leftGroupsOrder[i] = i;

        // Because we cannot use the default randomizer, which is based on the
        // current time (it will produce the same "random" number within a
        // second), we will use a random number generator to seed the
        // randomizer.

        // Use a 4-byte array to fill it with random bytes and convert it then
        // to an integer value.
        byte[] randomBytes = new byte[4];

        // Generate 4 random bytes.
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
        rng.GetBytes(randomBytes);

        // Convert 4 bytes into a 32-bit integer value.
        int seed = (randomBytes[0] & 0x7f) << 24 |
                    randomBytes[1] << 16 |
                    randomBytes[2] << 8 |
                    randomBytes[3];

        // Now, this is real randomization.
        Random random = new Random(seed);

        // This array will hold password characters.
        char[] password = null;

        // Allocate appropriate memory for the password.
        if (minLength < maxLength)
            password = new char[random.Next(minLength, maxLength + 1)];
        else
            password = new char[minLength];

        // Index of the next character to be added to password.
        int nextCharIdx;

        // Index of the next character group to be processed.
        int nextGroupIdx;

        // Index which will be used to track not processed character groups.
        int nextLeftGroupsOrderIdx;

        // Index of the last non-processed character in a group.
        int lastCharIdx;

        // Index of the last non-processed group.
        int lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;

        // Generate password characters one at a time.
        for (int i = 0; i < password.Length; i++)
        {
            // If only one character group remained unprocessed, process it;
            // otherwise, pick a random character group from the unprocessed
            // group list. To allow a special character to appear in the
            // first position, increment the second parameter of the Next
            // function call by one, i.e. lastLeftGroupsOrderIdx + 1.
            if (lastLeftGroupsOrderIdx == 0)
                nextLeftGroupsOrderIdx = 0;
            else
                nextLeftGroupsOrderIdx = random.Next(0,
                                                     lastLeftGroupsOrderIdx);

            // Get the actual index of the character group, from which we will
            // pick the next character.
            nextGroupIdx = leftGroupsOrder[nextLeftGroupsOrderIdx];

            // Get the index of the last unprocessed characters in this group.
            lastCharIdx = charsLeftInGroup[nextGroupIdx] - 1;

            // If only one unprocessed character is left, pick it; otherwise,
            // get a random character from the unused character list.
            if (lastCharIdx == 0)
                nextCharIdx = 0;
            else
                nextCharIdx = random.Next(0, lastCharIdx + 1);

            // Add this character to the password.
            password[i] = charGroups[nextGroupIdx][nextCharIdx];

            // If we processed the last character in this group, start over.
            if (lastCharIdx == 0)
                charsLeftInGroup[nextGroupIdx] =
                                          charGroups[nextGroupIdx].Length;
            // There are more unprocessed characters left.
            else
            {
                // Swap processed character with the last unprocessed character
                // so that we don't pick it until we process all characters in
                // this group.
                if (lastCharIdx != nextCharIdx)
                {
                    char temp = charGroups[nextGroupIdx][lastCharIdx];
                    charGroups[nextGroupIdx][lastCharIdx] =
                                charGroups[nextGroupIdx][nextCharIdx];
                    charGroups[nextGroupIdx][nextCharIdx] = temp;
                }
                // Decrement the number of unprocessed characters in
                // this group.
                charsLeftInGroup[nextGroupIdx]--;
            }

            // If we processed the last group, start all over.
            if (lastLeftGroupsOrderIdx == 0)
                lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;
            // There are more unprocessed groups left.
            else
            {
                // Swap processed group with the last unprocessed group
                // so that we don't pick it until we process all groups.
                if (lastLeftGroupsOrderIdx != nextLeftGroupsOrderIdx)
                {
                    int temp = leftGroupsOrder[lastLeftGroupsOrderIdx];
                    leftGroupsOrder[lastLeftGroupsOrderIdx] =
                                leftGroupsOrder[nextLeftGroupsOrderIdx];
                    leftGroupsOrder[nextLeftGroupsOrderIdx] = temp;
                }
                // Decrement the number of unprocessed groups.
                lastLeftGroupsOrderIdx--;
            }
        }
        return new string(password);
    }
}

class Program
{
    public static void Main()
    {
        Console.WriteLine(RandomPassword.Generate(12));
    }
}


For this task I'm using mainly the code described here.

public class PasswordGenerator
{
    private const string AlphaUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private const string AlphaLower = "abcdefghijklmnopqrstuvwxyz";
    private const string Numeric = "1234567890";
    private const string Special = "@#$?!";
    private const string AllChars = AlphaUpper + AlphaLower + Numeric + Special;

    private readonly MersenneTwister _random = new MersenneTwister();

    /// <summary>
    /// Generates a strong password out of upper case, lower case, numeric and special characters.
    /// </summary>
    /// <param name="length">The length of the password to generate. Must be at least 4.</param>
    /// <returns>A random strong password.</returns>
    public string Generate(int length)
    {
        string password = string.Empty;

        // generate four repeating random numbers for lower, upper, numeric and special characters
        // by filling these positions with corresponding characters, we can ensure the password has
        // at least one character of each type
        string posArray = "0123456789";

        if (length < posArray.Length)
            posArray = posArray.Substring(0, length);

        int pLower = GetRandomPosition(ref posArray);
        int pUpper = GetRandomPosition(ref posArray);
        int pNumber = GetRandomPosition(ref posArray);
        int pSpecial = GetRandomPosition(ref posArray);

        for (int i = 0; i < length; i++)
        {
            if (i == pLower)
            {
                password += GetRandomChar(AlphaUpper);
            }
            else if (i == pUpper)
            {
                password += GetRandomChar(AlphaLower);
            }
            else if (i == pNumber)
            {
                password += GetRandomChar(Numeric);
            }
            else if (i == pSpecial)
            {
                password += GetRandomChar(Special);
            }
            else
            {
                password += GetRandomChar(AllChars);
            }
        }

        return password;
    }

    private string GetRandomChar(string fullString)
    {
        return fullString.ToCharArray()[(int)Math.Floor(_random.NextDouble() * fullString.Length)].ToString();
    }

    private int GetRandomPosition(ref string posArray)
    {
        string randomChar = posArray.ToCharArray()[(int)Math.Floor(_random.NextDouble() * posArray.Length)].ToString();
        int pos = int.Parse(randomChar);
        posArray = posArray.Replace(randomChar, string.Empty);

        return pos;
    }
}

You just want't to get rid of the special character set.

Instead of using the .NET Random class described in the article, I'm using the Mersenne Twister algorithm to generate pseudorandom numbers.


How about the simple solution of constructing a string of the characters you want then calling the random number generator 12 times. Use each random number to index the string and get the character you want, something like (from memory):

String s = "abcdefghijklmnopqrstuvwxyz1234567890";
StringBuilder sekrit = new StringBuilder();
Random r = new Random();
for (int i = 0; i < 12; i++)
    sekrit = sekrit + s[r.Next(s.Length)];
return sekrit.ToString();
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号