blob: 5849bdd7706de4814948c40cac9c9d26a1dcdc0b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ambari.server.security;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import com.google.inject.Singleton;
@Singleton
public class SecurePasswordHelper {
/**
* The default number of characters to generate for a secure password
*/
public final static int DEFAULT_SECURE_PASSWORD_LENGTH = 18;
/**
* The default minimum number of lowercase letters to include when generating a secure password
*/
public final static int DEFAULT_SECURE_PASSWORD_MIN_LOWERCASE_LETTERS = 1;
/**
* The default minimum number of uppercase letters to include when generating a secure password
*/
public final static int DEFAULT_SECURE_PASSWORD_MIN_UPPERCASE_LETTERS = 1;
/**
* The default minimum number of digits to include when generating a secure password
*/
public final static int DEFAULT_SECURE_PASSWORD_MIN_DIGITS = 1;
/**
* The default minimum number of punctuation characters to include when generating a secure password
*/
public final static int DEFAULT_SECURE_PASSWORD_MIN_PUNCTUATION = 1;
/**
* The default minimum number of whitespace characters to include when generating a secure password
*/
public final static int DEFAULT_SECURE_PASSWORD_MIN_WHITESPACE = 1;
/**
* The set of available lowercase letters to use when generating a secure password
*/
protected final static char[] SECURE_PASSWORD_CHARACTER_CLASS_LOWERCASE_LETTERS = "abcdefghijklmnopqrstuvwxyz".toCharArray();
/**
* The set of available uppercase letters to use when generating a secure password
*/
protected final static char[] SECURE_PASSWORD_CHARACTER_CLASS_UPPERCASE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
/**
* The set of available digits to use when generating a secure password
*/
protected final static char[] SECURE_PASSWORD_CHARACTER_CLASS_DIGITS = "0123456789".toCharArray();
/**
* The set of available punctuation characters to use when generating a secure password
*/
protected final static char[] SECURE_PASSWORD_CHARACTER_CLASS_PUNCTUATION = "?.!$%^*()-_+=~".toCharArray();
/**
* The set of available whitespace characters to use when generating a secure password
*/
protected final static char[] SECURE_PASSWORD_CHARACTER_CLASS_WHITESPACE = " ".toCharArray();
/**
* The collection of available character classes
*/
private final static char[][] SECURE_PASSWORD_CHARACTER_CLASSES = {
SECURE_PASSWORD_CHARACTER_CLASS_LOWERCASE_LETTERS,
SECURE_PASSWORD_CHARACTER_CLASS_UPPERCASE_LETTERS,
SECURE_PASSWORD_CHARACTER_CLASS_DIGITS,
SECURE_PASSWORD_CHARACTER_CLASS_PUNCTUATION,
SECURE_PASSWORD_CHARACTER_CLASS_WHITESPACE
};
private final SecureRandom secureRandom = new SecureRandom();
/**
* Create a secure (random) password using a secure random number generator and a set of (reasonable)
* characters.
* <p/>
* The default rules are used to generate the password. See {@link #createSecurePassword(Integer, Integer, Integer, Integer, Integer, Integer)}
*
* @return a String containing the new password
* @see #createSecurePassword(Integer, Integer, Integer, Integer, Integer, Integer)
*/
public String createSecurePassword() {
return createSecurePassword(null, null, null, null, null, null);
}
/**
* Create a secure (random) password using a secure random number generator, a set of (reasonable)
* characters, and meeting the specified rules.
* <p/>
* If any rule is <code>null</code>, it's default value will be used:
* <ul>
* <li>length: 18</li>
* <li>minimum lowercase letters (a-z): 1</li>
* <li>minimum uppercase letters (A-Z): 1</li>
* <li>minimum digits (0-9): 1</li>
* <li>minimum punctuation (?.!$%^*()-_+=~): 1</li>
* <li>minimum whitespace ( ): 0</li>
* </ul>
*
* @param length the required length of the generated password
* @param minLowercaseLetters the required minimum number of lowercase letters
* @param minUppercaseLetters the required minimum number of uppercase letters
* @param minDigits the required minimum number of digits
* @param minPunctuation the required minimum number of punctuation characters
* @param minWhitespace the required minimum number of space characters
* @return a String containing the new password
*/
public String createSecurePassword(Integer length, Integer minLowercaseLetters, Integer minUppercaseLetters, Integer minDigits, Integer minPunctuation, Integer minWhitespace) {
if ((length == null) || (length < 1)) {
length = SecurePasswordHelper.DEFAULT_SECURE_PASSWORD_LENGTH;
}
if (minLowercaseLetters == null) {
minLowercaseLetters = SecurePasswordHelper.DEFAULT_SECURE_PASSWORD_MIN_LOWERCASE_LETTERS;
}
if (minUppercaseLetters == null) {
minUppercaseLetters = SecurePasswordHelper.DEFAULT_SECURE_PASSWORD_MIN_UPPERCASE_LETTERS;
}
if (minDigits == null) {
minDigits = SecurePasswordHelper.DEFAULT_SECURE_PASSWORD_MIN_DIGITS;
}
if (minPunctuation == null) {
minPunctuation = SecurePasswordHelper.DEFAULT_SECURE_PASSWORD_MIN_PUNCTUATION;
}
if (minWhitespace == null) {
minWhitespace = SecurePasswordHelper.DEFAULT_SECURE_PASSWORD_MIN_WHITESPACE;
}
// Gather the set of characters that meet the specified requirements
List<Character> characters = new ArrayList<>(length);
for (int i = 0; i < minLowercaseLetters; i++) {
characters.add(SecurePasswordHelper.SECURE_PASSWORD_CHARACTER_CLASS_LOWERCASE_LETTERS[secureRandom.nextInt(SecurePasswordHelper.SECURE_PASSWORD_CHARACTER_CLASS_LOWERCASE_LETTERS.length)]);
}
for (int i = 0; i < minUppercaseLetters; i++) {
characters.add(SecurePasswordHelper.SECURE_PASSWORD_CHARACTER_CLASS_UPPERCASE_LETTERS[secureRandom.nextInt(SecurePasswordHelper.SECURE_PASSWORD_CHARACTER_CLASS_UPPERCASE_LETTERS.length)]);
}
for (int i = 0; i < minDigits; i++) {
characters.add(SecurePasswordHelper.SECURE_PASSWORD_CHARACTER_CLASS_DIGITS[secureRandom.nextInt(SecurePasswordHelper.SECURE_PASSWORD_CHARACTER_CLASS_DIGITS.length)]);
}
for (int i = 0; i < minPunctuation; i++) {
characters.add(SecurePasswordHelper.SECURE_PASSWORD_CHARACTER_CLASS_PUNCTUATION[secureRandom.nextInt(SecurePasswordHelper.SECURE_PASSWORD_CHARACTER_CLASS_PUNCTUATION.length)]);
}
for (int i = 0; i < minWhitespace; i++) {
characters.add(SecurePasswordHelper.SECURE_PASSWORD_CHARACTER_CLASS_WHITESPACE[secureRandom.nextInt(SecurePasswordHelper.SECURE_PASSWORD_CHARACTER_CLASS_WHITESPACE.length)]);
}
// If we need to gather more characters, select randomly from the set of character classes
if (characters.size() < length) {
int difference = length - characters.size();
for (int i = 0; i < difference; i++) {
char[] characterClass = SecurePasswordHelper.SECURE_PASSWORD_CHARACTER_CLASSES[secureRandom.nextInt(SecurePasswordHelper.SECURE_PASSWORD_CHARACTER_CLASSES.length - 1)];
characters.add(characterClass[secureRandom.nextInt(characterClass.length)]);
}
}
// Generate the password string by randomly selecting from the list of available characters
StringBuilder passwordBuilder = new StringBuilder(characters.size());
while (!characters.isEmpty()) {
passwordBuilder.append(characters.remove(secureRandom.nextInt(characters.size())));
}
return passwordBuilder.toString();
}
}