Need a secure password generator recommendation [closed]

佐手、 提交于 2019-11-28 21:25:53
Knubo

I would not worry that much about generating incredible strong one time passwords. Make the password long and it should not be a problem with brute force granted you limit how long the password is valid. If the password is only valid for say 1 hour then it will not be a problem if the password remains unused. And in that time span it is not likely that someone will get to crack it using brute force.

It is also important that you only let the one time password work just one time. This way, if the password is intercepted the user will notice when the one time password has expired and can take appropriate actions.

I'd go for Apache Commons RandomStringUtils and let the password be 10-15 characters of letters and numbers.

...though it always is a question of how paranoid you want to be. This solution would be fine for a regular web application, but not good enough for a bank...

It's in .net but should be trivial to convert. Maybe a little too much for most, but this is the go to implementation that I always use in my apps. It is an implementation that I found some time ago, and did some modifications to, I can't recall the original author, but I will do a quick search to see if I can give him appropriate credit.

public static string GenerateRandomString(int minLength, int maxLength, int minLCaseCount, int minUCaseCount, int minNumCount, int minSpecialCount)
        {
            char[] randomString;

            const string LCaseChars = "abcdefgijkmnopqrstwxyz";
            const string UCaseChars = "ABCDEFGHJKLMNPQRSTWXYZ";
            const string NumericChars = "23456789";
            const string SpecialChars = "*$-+?_&=!%{}/";

            Hashtable charGroupsUsed = new Hashtable();
            charGroupsUsed.Add("lcase", minLCaseCount);
            charGroupsUsed.Add("ucase", minUCaseCount);
            charGroupsUsed.Add("num", minNumCount);
            charGroupsUsed.Add("special", minSpecialCount);

            // 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];

            // Create a randomizer from the seed.
            Random random = new Random(seed);

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

            int requiredCharactersLeft = minLCaseCount + minUCaseCount + minNumCount + minSpecialCount;

            // Build the password.
            for (int i = 0; i < randomString.Length; i++)
            {
                string selectableChars = "";

                // if we still have plenty of characters left to acheive our minimum requirements.
                if (requiredCharactersLeft < randomString.Length - i)
                {
                    // choose from any group at random
                    selectableChars = LCaseChars + UCaseChars + NumericChars + SpecialChars;
                }
                else // we are out of wiggle room, choose from a random group that still needs to have a minimum required.
                {
                    // choose only from a group that we need to satisfy a minimum for.
                    foreach (DictionaryEntry charGroup in charGroupsUsed)
                    {
                        if ((int)charGroup.Value > 0)
                        {
                            switch (charGroup.Key.ToString())
                            {
                                case "lcase":
                                    selectableChars += LCaseChars;
                                    break;
                                case "ucase":
                                    selectableChars += UCaseChars;
                                    break;
                                case "num":
                                    selectableChars += NumericChars;
                                    break;
                                case "special":
                                    selectableChars += SpecialChars;
                                    break;
                            }
                        }
                    }
                }

                // Now that the string is built, get the next random character.
                char nextChar = selectableChars[random.Next(0, selectableChars.Length - 1)];

                // Tac it onto our password.
                randomString[i] = nextChar;

                // Now figure out where it came from, and decrement the appropriate minimum value.
                if (LCaseChars.Contains(nextChar))
                {
                    charGroupsUsed["lcase"] = (int)charGroupsUsed["lcase"] - 1;
                    if ((int)charGroupsUsed["lcase"] >= 0)
                    {
                        requiredCharactersLeft--;
                    }
                }
                else if (UCaseChars.Contains(nextChar))
                {
                    charGroupsUsed["ucase"] = (int)charGroupsUsed["ucase"] - 1;
                    if ((int)charGroupsUsed["ucase"] >= 0)
                    {
                        requiredCharactersLeft--;
                    }
                }
                else if (NumericChars.Contains(nextChar))
                {
                    charGroupsUsed["num"] = (int)charGroupsUsed["num"] - 1;
                    if ((int)charGroupsUsed["num"] >= 0)
                    {
                        requiredCharactersLeft--;
                    }
                }
                else if (SpecialChars.Contains(nextChar))
                {
                    charGroupsUsed["special"] = (int)charGroupsUsed["special"] - 1;
                    if ((int)charGroupsUsed["special"] >= 0)
                    {
                        requiredCharactersLeft--;
                    }
                }
            }
            return new string(randomString);
        }

Edit

I believe I started with the code posted at http://www.obviex.com/Samples/Password.aspx. Though the code now has a few more features.

Here is an example using Commons. It creates an Alphanumeric password between 8 and 20 characters long.

public String getRandomPassword() {
    StringBuffer password = new StringBuffer(20);
    int next = RandomUtils.nextInt(13) + 8;
    password.append(RandomStringUtils.randomAlphanumeric(next));
    return password.toString();
}

UPDATE RandomUtils.nextInt returns a number between 0 (inclusive) and the specified value (exclusive) so to get a value between 8 and 20 characters inclusive, the argument value should be 13. I've corrected the code above.

UPDATE As noted in a comment below, this could be written without using StringBuffer. Here is a modified one line version:

return RandomStringUtils.randomAlphanumeric(RandomUtils.nextInt(13) + 8);

For those interested, here's Matthew's code, converted to Java

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class PasswordGenerator {

public static String GenerateRandomString(int minLength, int maxLength, int minLCaseCount,     int minUCaseCount, int minNumCount, int minSpecialCount)
{
    char[] randomString;

    String LCaseChars = "abcdefgijkmnopqrstwxyz";
    String UCaseChars = "ABCDEFGHJKLMNPQRSTWXYZ";
    String NumericChars = "23456789";
    String SpecialChars = "*$-+?_&=!%{}/";

    Map<String,Integer> charGroupsUsed = new HashMap<String,Integer>();
    charGroupsUsed.put("lcase", minLCaseCount);
    charGroupsUsed.put("ucase", minUCaseCount);
    charGroupsUsed.put("num", minNumCount);
    charGroupsUsed.put("special", minSpecialCount);

    // 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.
    new Random().nextBytes(randomBytes);

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

    // Create a randomizer from the seed.
    Random random = new Random(seed);

    // Allocate appropriate memory for the password.
    int randomIndex = -1;
    if (minLength < maxLength)
    {
        randomIndex = random.nextInt((maxLength-minLength)+1)+minLength;
        randomString = new char[randomIndex];
    }
    else
    {
        randomString = new char[minLength];
    }

    int requiredCharactersLeft = minLCaseCount + minUCaseCount + minNumCount + minSpecialCount;

    // Build the password.
    for (int i = 0; i < randomString.length; i++)
    {
        String selectableChars = "";

        // if we still have plenty of characters left to acheive our minimum requirements.
        if (requiredCharactersLeft < randomString.length - i)
        {
            // choose from any group at random
            selectableChars = LCaseChars + UCaseChars + NumericChars + SpecialChars;
        }
        else // we are out of wiggle room, choose from a random group that still needs to have a minimum required.
        {
            // choose only from a group that we need to satisfy a minimum for.
            for(Map.Entry<String, Integer> charGroup : charGroupsUsed.entrySet())
            {
                if ((int)charGroup.getValue() > 0)
                {
                    if("lcase".equals(charGroup.getKey()) ){
                        selectableChars += LCaseChars;
                    }
                    else if("ucase".equals(charGroup.getKey())){
                        selectableChars += UCaseChars;
                    }
                    else if("num".equals(charGroup.getKey())){
                        selectableChars += NumericChars;
                    }
                    else if("special".equals(charGroup.getKey())){
                        selectableChars += SpecialChars;
                    }
                }
            }
        }

        // Now that the string is built, get the next random character.
        randomIndex = random.nextInt((selectableChars.length())-1);
        char nextChar = selectableChars.charAt(randomIndex);

        // Tac it onto our password.
        randomString[i] = nextChar;

        // Now figure out where it came from, and decrement the appropriate minimum value.
        if (LCaseChars.indexOf(nextChar) > -1)
        {
            charGroupsUsed.put("lcase",charGroupsUsed.get("lcase") - 1);
            if (charGroupsUsed.get("lcase") >= 0)
            {
                requiredCharactersLeft--;
            }
        }
        else if (UCaseChars.indexOf(nextChar) > -1)
        {
            charGroupsUsed.put("ucase",charGroupsUsed.get("ucase") - 1);
            if (charGroupsUsed.get("ucase") >= 0)
            {
                requiredCharactersLeft--;
            }
        }
        else if (NumericChars.indexOf(nextChar) > -1)
        {
            charGroupsUsed.put("num", charGroupsUsed.get("num") - 1);
            if (charGroupsUsed.get("num") >= 0)
            {
                requiredCharactersLeft--;
            }
        }
        else if (SpecialChars.indexOf(nextChar) > -1)
        {
            charGroupsUsed.put("special",charGroupsUsed.get("special") - 1);
            if (charGroupsUsed.get("special") >= 0)
            {
                requiredCharactersLeft--;
            }
        }
    }
    return new String(randomString);
}

}

And a unit test

import org.junit.Test;

public class PasswordGeneratorTest {

@Test
public void testPasswordCreation(){

    System.out.println(PasswordGenerator.GenerateRandomString(8,25,3,1,1,1));

}

}

Password Safe is open source (under the Artistic License) and includes password generation code.

You can easily implement it using Random and the built in MessageDigest implementations.

import java.util.Random;
import java.security.*;
import java.math.*;

public class RandPassGen {
    public static String genPass( int chars ) {
        Random r = new Random();
        MessageDigest md = null;

        try {
            md = MessageDigest.getInstance("MD5");
        } catch ( NoSuchAlgorithmException e ) {
            System.out.println( "Unsupported Algorithm!" );
            return null;
        }

        byte[] entropy = new byte[1024];
        r.nextBytes(entropy);
        md.update( entropy , 0, 1024 );

        return new BigInteger(1, md.digest()).toString(16).substring(0, chars);
    }

    public static void main( String[] av ) {
        Integer chars = Integer.valueOf(av[0]);
        if ((chars < 0) || (chars > 32)) {
            System.out.println( "Generate between 0 and 32 characters." );
            return;
        }

        System.out.println( genPass( chars ) ); 
    }
}

I added a Golang implementation that is similar to the C#/Java versions. It is available under Apache 2.0. The source is located here:

https://github.com/deftlabs/dlshared/blob/master/password_utils.go

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!