Java 需要一个安全的密码生成器推荐

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4090021/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 11:30:50  来源:igfitidea点击:

Need a secure password generator recommendation

javapassword-generator

提问by Brian Matthews

Can anybody recommend a secure password generator available under a Apache or LGPL licence for Java?

谁能推荐一个在 Apache 或 LGPL 许可下可用的安全密码生成器?

采纳答案by 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.

我不会太担心生成令人难以置信的强一次性密码。使密码很长,如果您限制密码的有效时间,那么使用蛮力应该不会有问题。如果密码仅在 1 小时内有效,那么如果密码仍未使用,则不会有问题。在那个时间跨度内,不太可能有人使用蛮力破解它。

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 RandomStringUtilsand let the password be 10-15 characters of letters and numbers.

我会选择 Apache Commons RandomStringUtils并让密码为 10-15 个字母和数字字符。

...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...

...虽然这总是一个关于你想变得多么偏执的问题。此解决方案适用于常规 Web 应用程序,但对于银行来说还不够好......

回答by Matthew Vines

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.

它在 .net 中,但转换起来应该很简单。对于大多数人来说可能有点太多了,但这是我一直在我的应用程序中使用的实现方式。这是我前段时间找到的一个实现,并做了一些修改,我不记得原作者了,但我会快速搜索一下,看看我是否可以给予他适当的信任。

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.

我相信我是从http://www.obviex.com/Samples/Password.aspx 上发布的代码开始的 。虽然代码现在有一些更多的功能。

回答by Jherico

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

Password Safe是开源的(在艺术许可下)并且包括密码生成代码。

回答by Reese Moore

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

您可以使用 Random 和内置的 MessageDigest 实现轻松实现它。

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 ) ); 
    }
}

回答by Dave

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

这是一个使用 Commons 的例子。它创建一个长度在 8 到 20 个字符之间的字母数字密码。

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

UPDATERandomUtils.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.

UPDATERandomUtils.nextInt 返回一个介于 0(包含)和指定值(不包含)之间的数字,因此要获得包含 8 到 20 个字符的值,参数值应该是 13。我已经更正了上面的代码。

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

UPDATE正如下面的评论中所指出的,这可以在不使用 StringBuffer 的情况下编写。这是一个修改过的单行版本:

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

回答by eugene

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

对于那些感兴趣的人,这里是 Matthew 的代码,转换为 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));

}

}

回答by Ryan

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:

我添加了一个类似于 C#/Java 版本的 Golang 实现。它在 Apache 2.0 下可用。来源位于:

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

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