使用最低特殊字符要求在Java中生成安全随机密码 [英] Generate a Secure Random Password in Java with Minimum Special Character Requirements

查看:111
本文介绍了使用最低特殊字符要求在Java中生成安全随机密码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在Java中创建满足系统长度和字符集要求的随机密码?

How do I create a random password that meets the system's length and character set requirements in Java?

我必须创建一个10-14个字符长的随机密码,并且至少有一个大写,一个小写和一个特殊字符。不幸的是,一些特殊字符特殊且不能使用,所以我不能只使用打印的ASCII。

I have to create a random password that is 10-14 characters long and has at least one uppercase, one lowercase, and one special character. Unfortunately, some special characters are too special and cannot be used, so I cannot use just printed ASCII.

本网站上的许多例子生成随机密码或会话密钥,但字符中没有足够的熵,或者在上面给出的业务设置中没有实际要求,所以我要求更尖锐的问题来获得更好的答案。

Many of the examples on this site generate a random password or session key without enough entropy in the characters or without realistic requirements in a business setting like the ones given above, so I'm asking more pointed question to get a better answer.

我的角色集,标准美国键盘上的每个特殊字符除了空格:

My character set, every special character on a standard US keyboard except for a space:

A-Z
a-z
0-9
~`!@#$%^&*()-_=+[{]}\|;:'",<.>/?


推荐答案

我最近了解到 Passay 。它提供了 PasswordGenerator类。它随机生成密码符合下面的要求,使用CharacterRules而不是PasswordCharacterSets,如下所示。它不是保留随机字符插入的未使用索引列表,而是在插入符合要求的字符后简单地对字符缓冲区进行洗牌。

I recently learned about Passay. It provides the required functionality needed in its PasswordGenerator class. It randomly generates passwords meeting the requirements similar to what is written below using CharacterRules rather than PasswordCharacterSets as I have done below. Instead of holding a list of unused indexes for random character insertion, it simply shuffles the character buffer after inserting characters that meet the requirements.

下面是从以前遗留下来,如果您的许可允许,我建议使用Passay,此代码应该以其他方式工作,并提供生成密码为何具有密码效果的详细信息

Below is left over from before, I recommend using Passay if your licensing allows it, this code should work otherwise and provides details of why the generated passwords are crytographically strong

我最后两次编写这段代码。一旦获得随机字符结果,但事实证明字符的分布取决于字符集的大小(哎呀!)。我重写了它,现在你应该复制/粘贴代码并将Main.java更改为你想要的字符集。虽然可以采用不同的方式,但我认为这是一种相对简单的方法来获得正确的结果,我鼓励重用,评论,批评和经过深思熟虑的编辑。

I ended up writing this code twice. Once to get a random character result, but it turned out the distribution of characters depended on the size of the character set(whoops!). I rewrote it and now you should just copy/paste the code and change the Main.java to the character sets you want. Although it could have been done differently, I think this is a relatively straightforward approach to get the correct result and I encourage reuse, comments, criticisms, and well-thought edits.

PasswordGenerator代码的控件如下:

The controls of the PasswordGenerator code is as follows:


  • 最小/最大长度:使用随机数设置

  • PasswordCharacterSet :假设传入PasswordGenerator的所有PasswordCharacterSets都包含唯一的字符集,否则,随机字符会对重复字符产生偏差。

  • PasswordCharacterSet最小字符:用于此字符集的最小字符。

  • Min/Max Length: Set using a random number
  • PasswordCharacterSet: It is assumed that all PasswordCharacterSets passed into PasswordGenerator consist of unique character sets, if not, the random characters will have a skew towards the duplicates.
  • PasswordCharacterSet Min Characters: The min characters to use for this character set.

实际密码生成的主要部分:

The main bits for the actual password generation:


  • 随机随机性:我们正在使用SecureRandom由加密强PRNG支持,而不是Random类,而不是。

  • 密码的随机字符顺序 :pw char数组的所有索引都添加到remainingIndexes数组中。当我们调用addRandomCharacters时,它会随机删除一个索引,我们使用删除的索引来填充数组。

  • 随机字符:在addRandomCharacters中,一个随机索引来自选择我们正在使用的字符索引并将其添加到pw数组中。

  • 设置保证每种类型的最小字符:我们首先简单地划出最小字符数量。我们从每个字符集中选择最小量的随机值,然后继续。

  • 剩余字符的随机分布:设置最小值后,我们想让所有字符集中的其余字符随机。所有字符都添加到单个数组中。其余的插槽使用与先前字符集相同的策略填充。

  • Randomness of Random: We're using SecureRandom which is backed by a cryptographically strong PRNG, rather than the Random class which is not.
  • Random character order for the password: All the indexes of the pw char array are added to the remainingIndexes array. As we call addRandomCharacters, it removes an index randomly and we use the removed index to populate the array.
  • Random characters: In addRandomCharacters, a random index from the character index we're using is chosen and added to the pw array.
  • Guaranteeing minimum characters of each type are set: We simply carve out the minimum character amount first. We choose the minimum amount of random values from each character set and then move on.
  • Random distribution for the remaining characters: After the minimum values have been set, we want to make the rest of the characters random across all character sets. All the characters are added to a single array. The remaining slots are filled using the same strategy for the previous character sets.

密码复杂性说明:通常会谈到密码复杂性比特的熵。以下是密钥空间的可能性数量:

Description of password complexity: Password complexity is usually talked about in bits of entropy. Here are the number of possibilities for your keyspace:

至少有一个大写字母字符(26个中),一个小写字母字符(26个中),一个数字(满分10分)和一个特殊字符(满分32分),计算可能性数量的方式是每个字符的可能性数乘以字符数,因为它们随机放在字符串中。所以我们知道四个角色的可能性是:

There is at least one uppercase alpha character (out of 26), one lowercase alpha character(out of 26), one digit (out of 10), and one special character (out of 32), the way you calculate the number of possibilities is the number of possibilities for each character multiplied by the number of characters since they are randomly placed in the string. So we know the possibilities for four of the characters are:

Required Characters = 26*26*10*32=216,320

所有剩余字符各有94(26 + 26 + 10 + 32)种可能性

All remaining characters have 94 (26+26+10+32) possibilities each

我们的计算是:

Characters  Possibilities                                       Bits of Entropy
10 chars    216,320*94^6  = 149,232,631,038,033,920             ~2^57
11 chars    216,320*94^7  = 14,027,867,317,575,188,480          ~2^63
12 chars    216,320*94^8  = 1,318,619,527,852,067,717,120       ~2^70
13 chars    216,320*94^9  = 123,950,235,618,094,365,409,280     ~2^76
14 chars    216,320*94^10 = 11,651,322,148,100,870,348,472,320  ~2^83

考虑到这一点,如果你想要最安全的密码,你应该总是选择最多可能的字符数,在这种情况下为14。

With this is mind, if you want the most secure passwords, you should always choose the highest amount of characters possible which is 14 in this case.

Main.java

Main.java

package org.redtown.pw;

import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;

import org.redtown.pw.PasswordGenerator.PasswordCharacterSet;

public class Main {

    public static void main(String[] args) {
        Set<PasswordCharacterSet> values = new HashSet<PasswordCharacterSet>(EnumSet.allOf(SummerCharacterSets.class));
        PasswordGenerator pwGenerator = new PasswordGenerator(values, 10, 14);
        for(int i=0; i < 10; ++i) {
            System.out.println(pwGenerator.generatePassword());
        }
    }

    private static final char[] ALPHA_UPPER_CHARACTERS = { 'A', 'B', 'C', 'D',
            'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
            'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
    private static final char[] ALPHA_LOWER_CHARACTERS = { 'a', 'b', 'c', 'd',
            'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
            'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
    private static final char[] NUMERIC_CHARACTERS = { '0', '1', '2', '3', '4',
            '5', '6', '7', '8', '9' };
    private static final char[] SPECIAL_CHARACTERS = { '~', '`', '!', '@', '#',
            '$', '%', '^', '&', '*', '(', ')', '-', '_', '=', '+', '[', '{',
            ']', '}', '\\', '|', ';', ':', '\'', '"', ',', '<', '.', '>', '/',
            '?' };

    private enum SummerCharacterSets implements PasswordCharacterSet {
        ALPHA_UPPER(ALPHA_UPPER_CHARACTERS, 1),
        ALPHA_LOWER(ALPHA_LOWER_CHARACTERS, 1),
        NUMERIC(NUMERIC_CHARACTERS, 1),
        SPECIAL(SPECIAL_CHARACTERS, 1);

        private final char[] chars;
        private final int minUsage;

        private SummerCharacterSets(char[] chars, int minUsage) {
            this.chars = chars;
            this.minUsage = minUsage;
        }

        @Override
        public char[] getCharacters() {
            return chars;
        }

        @Override
        public int getMinCharacters() {
            return minUsage;
        }
    }
}

PasswordGenerator.java

PasswordGenerator.java

package org.redtown.pw;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Random;

public class PasswordGenerator {

    private final List<PasswordCharacterSet> pwSets;
    private final char[] allCharacters;
    private final int minLength;
    private final int maxLength;
    private final int presetCharacterCount;

    public PasswordGenerator(Collection<PasswordCharacterSet> origPwSets, int minLength, int maxLength) {
        this.minLength = minLength;
        this.maxLength = maxLength;

        // Make a copy of the character arrays and min-values so they cannot be changed after initialization
        int pwCharacters = 0;
        int preallocatedCharacters = 0;
        List<PasswordCharacterSet> pwSets = new ArrayList<PasswordCharacterSet>(origPwSets.size());
        for(PasswordCharacterSet origpwSet : origPwSets) {
            PasswordCharacterSet newPwSet = new PwSet(origpwSet);
            pwSets.add(newPwSet);
            pwCharacters += newPwSet.getCharacters().length;
            preallocatedCharacters += newPwSet.getMinCharacters();
        }
        this.presetCharacterCount = preallocatedCharacters;
        this.pwSets = Collections.unmodifiableList(pwSets);

        if (minLength < presetCharacterCount) {
            throw new IllegalArgumentException("Combined minimum lengths "
                    + presetCharacterCount
                    + " are greater than the minLength of " + minLength);
        }

        // Copy all characters into single array so we can evenly access all members when accessing this array
        char[] allChars = new char[pwCharacters];
        int currentIndex = 0;
        for(PasswordCharacterSet pwSet : pwSets) {
            char[] chars = pwSet.getCharacters();
            System.arraycopy(chars, 0, allChars, currentIndex, chars.length);
            currentIndex += chars.length;
        }
        this.allCharacters = allChars;
    }


    public char[] generatePassword() {
        SecureRandom rand = new SecureRandom();

        // Set pw length to minLength <= pwLength <= maxLength
        int pwLength = minLength + rand.nextInt(maxLength - minLength + 1);
        int randomCharacterCount = pwLength - presetCharacterCount;


        // Place each index in an array then remove them randomly to assign positions in the pw array
        List<Integer> remainingIndexes = new ArrayList<Integer>(pwLength);
        for(int i=0; i < pwLength; ++i) {
            remainingIndexes.add(i);
        }

        // Fill pw array
        char[] pw = new char[pwLength];
        for(PasswordCharacterSet pwSet : pwSets) {
            addRandomCharacters(pw, pwSet.getCharacters(), pwSet.getMinCharacters(), remainingIndexes, rand);
        }
        addRandomCharacters(pw, allCharacters, randomCharacterCount, remainingIndexes, rand);
        return pw;
    }

    private static void addRandomCharacters(char[] pw, char[] characterSet,
            int numCharacters, List<Integer> remainingIndexes, Random rand) {
        for(int i=0; i < numCharacters; ++i) {
            // Get and remove random index from the remaining indexes
            int pwIndex = remainingIndexes.remove(rand.nextInt(remainingIndexes.size()));

            // Set random character from character index to pwIndex
            int randCharIndex = rand.nextInt(characterSet.length);
            pw[pwIndex] = characterSet[randCharIndex];
        }
    }

    public static interface PasswordCharacterSet {
        char[] getCharacters();
        int getMinCharacters();
    }

    /**
     * Defensive copy of a passed-in PasswordCharacterSet
     */
    private static final class PwSet implements PasswordCharacterSet {
        private final char[] chars;
        private final int minChars;

        public PwSet(PasswordCharacterSet pwSet) {
            this.minChars = pwSet.getMinCharacters();
            char[] pwSetChars = pwSet.getCharacters();
            // Defensive copy
            this.chars = Arrays.copyOf(pwSetChars, pwSetChars.length);
        }

        @Override
        public char[] getCharacters() {
            return chars;
        }

        @Override
        public int getMinCharacters() {
            return minChars;
        }
    }
}

这篇关于使用最低特殊字符要求在Java中生成安全随机密码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆