用 Java 生成一个安全的随机密码,具有最低的特殊字符要求
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31260512/
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
Generate a Secure Random Password in Java with Minimum Special Character Requirements
提问by summer
How do I create a random password that meets the system's length and character set requirements in Java?
如何在Java中创建满足系统长度和字符集要求的随机密码?
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 toospecial and cannot be used, so I cannot use just printed ASCII.
我必须创建一个长度为 10-14 个字符且至少有一个大写字母、一个小写字母和一个特殊字符的随机密码。不幸的是,一些特殊字符太特殊,无法使用,所以我不能只使用打印的 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
~`!@#$%^&*()-_=+[{]}\|;:'",<.>/?
采纳答案by summer
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。它提供了它的PasswordGenerator 类中所需的功能。它使用 CharacterRules 而不是 PasswordCharacterSets 随机生成满足以下要求的密码,就像我在下面所做的那样。它不是为随机字符插入保存未使用的索引列表,而是在插入符合要求的字符后简单地对字符缓冲区进行洗牌。
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
以下是之前遗留的内容,如果您的许可允许,我建议使用 Passay,否则此代码应该可以工作,并提供有关为什么生成的密码密码强度高的详细信息
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.
我最终写了两次这段代码。曾经得到一个随机字符结果,但结果字符的分布取决于字符集的大小(哎呀!)。我重写了它,现在您应该复制/粘贴代码并将 Main.java 更改为您想要的字符集。尽管可以采用不同的方式,但我认为这是获得正确结果的相对简单的方法,我鼓励重用、评论、批评和深思熟虑的编辑。
The controls of the PasswordGenerator code is as follows:
PasswordGenerator 代码的控制如下:
- 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.
- 最小/最大长度:使用随机数设置
- PasswordCharacterSet:假设所有传入 PasswordGenerator 的 PasswordCharacterSets 都由唯一的字符集组成,否则随机字符将向重复项倾斜。
- PasswordCharacterSet Min Characters:用于此字符集的最小字符。
The main bits for the actual password generation:
实际密码生成的主要位:
- 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.
- Random 的随机性:我们使用的是 SecureRandom,它由加密强的 PRNG 支持,而不是 Random 类,它不是。
- 密码的随机字符顺序:将 pw 字符数组的所有索引添加到剩余索引数组中。当我们调用 addRandomCharacters 时,它会随机删除一个索引,然后我们使用删除的索引来填充数组。
- 随机字符:在 addRandomCharacters 中,从我们使用的字符索引中选择一个随机索引并将其添加到 pw 数组中。
- 保证设置每种类型的最小字符数:我们简单地先雕刻出最小字符数。我们从每个字符集中选择最少量的随机值,然后继续。
- 剩余字符的随机分布:设置最小值后,我们希望使其余字符在所有字符集中随机分布。所有字符都添加到一个数组中。剩余的插槽使用与先前字符集相同的策略填充。
Description of password complexity: Password complexity is usually talked about in bits of entropy. Here are the number of possibilities for your keyspace:
密码复杂度的描述:密码复杂度通常以位熵来讨论。以下是您的密钥空间的多种可能性:
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:
至少有一个大写字母字符(共 26 个)、一个小写字母字符(共 26 个)、一个数字(共 10 个)和一个特殊字符(共 32 个),您计算可能性的方式是每个字符的可能性数乘以字符数,因为它们是随机放置在字符串中的。所以我们知道四个字符的可能性是:
Required Characters = 26*26*10*32=216,320
All remaining characters have 94 (26+26+10+32) possibilities each
所有剩余的字符每个都有 94 (26+26+10+32) 种可能性
Our calculation is:
我们的计算是:
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
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.
记住这一点,如果您想要最安全的密码,您应该始终选择尽可能多的字符,在这种情况下为 14。
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
密码生成器.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;
}
}
}
回答by Robert Wadowski
I suggest using apache commons RandomStringUtils. Use something what is already done.
我建议使用 apache commons RandomStringUtils。使用已经完成的东西。
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^&*()-_=+[{]}\|;:\'\",<.>/?";
String pwd = RandomStringUtils.random( 15, characters );
System.out.println( pwd );
If you are using maven
如果您使用的是 Maven
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
otherwise download jar
否则下载jar
UPDATEVersion with secure random. So matter of required characters left and can be solved as in comment, generate required parts separately and normal ones. Then join them randomly.
带有安全随机的更新版本。所以剩下的所需字符问题可以在注释中解决,分别生成所需部分和正常部分。然后随机加入他们。
char[] possibleCharacters = (new String("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^&*()-_=+[{]}\|;:\'\",<.>/?")).toCharArray();
String randomStr = RandomStringUtils.random( randomStrLength, 0, possibleCharacters.length-1, false, false, possibleCharacters, new SecureRandom() );
System.out.println( randomStr );
回答by Jose Martinez
Here is a utility that uses just vanilla Java and implements the requirements. It basically gets one of each of the required character sets. Then populates the rest with random chars from the whole set. Then shuffles it all up.
这是一个仅使用普通 Java 并实现要求的实用程序。它基本上获得了每个必需的字符集之一。然后用整个集合中的随机字符填充其余部分。然后将其全部洗牌。
public class PasswordUtils {
static char[] SYMBOLS = (new String("^$*.[]{}()?-\"!@#%&/\,><':;|_~`")).toCharArray();
static char[] LOWERCASE = (new String("abcdefghijklmnopqrstuvwxyz")).toCharArray();
static char[] UPPERCASE = (new String("ABCDEFGHIJKLMNOPQRSTUVWXYZ")).toCharArray();
static char[] NUMBERS = (new String("0123456789")).toCharArray();
static char[] ALL_CHARS = (new String("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789^$*.[]{}()?-\"!@#%&/\,><':;|_~`")).toCharArray();
static Random rand = new SecureRandom();
public static String getPassword(int length) {
assert length >= 4;
char[] password = new char[length];
//get the requirements out of the way
password[0] = LOWERCASE[rand.nextInt(LOWERCASE.length)];
password[1] = UPPERCASE[rand.nextInt(UPPERCASE.length)];
password[2] = NUMBERS[rand.nextInt(NUMBERS.length)];
password[3] = SYMBOLS[rand.nextInt(SYMBOLS.length)];
//populate rest of the password with random chars
for (int i = 4; i < length; i++) {
password[i] = ALL_CHARS[rand.nextInt(ALL_CHARS.length)];
}
//shuffle it up
for (int i = 0; i < password.length; i++) {
int randomPosition = rand.nextInt(password.length);
char temp = password[i];
password[i] = password[randomPosition];
password[randomPosition] = temp;
}
return new String(password);
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println(getPassword(8));
}
}
}
回答by Manjunatha B
Using the random functionality of java.util package of rt.jar, we can create a random password of any length. below is the snippet for the same.
使用 rt.jar 的 java.util 包的随机功能,我们可以创建任意长度的随机密码。下面是相同的片段。
public class GeneratePassword {
public static void main(String[] args)
{
int length = 10;
String symbol = "-/.^&*_!@%=+>)";
String cap_letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String small_letter = "abcdefghijklmnopqrstuvwxyz";
String numbers = "0123456789";
String finalString = cap_letter + small_letter +
numbers + symbol;
Random random = new Random();
char[] password = new char[length];
for (int i = 0; i < length; i++)
{
password[i] =
finalString.charAt(random.nextInt(finalString.length()));
}
System.out.println(password);
}
}
}