/* * 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 net.formio.security; import java.math.BigInteger; import java.security.SecureRandom; /** * Generator of random passwords/strings * from potentially provided list of allowed characters. * * @author Radek Beran */ public final class PasswordGenerator { public static final int DEFAULT_PWD_LENGTH = 8; public static final String DEFAULT_PWD_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; public static String generatePassword() { return generatePassword(DEFAULT_PWD_LENGTH, DEFAULT_PWD_CHARS); } public static String generatePassword(int length) { return generatePassword(length, DEFAULT_PWD_CHARS); } public static String generatePassword(int length, String digits) { return generatePassword(length, digits, 0, ""); } /** * Creates secure password by generating random sequence of characters. Which characters will appear in * password is controlled by method parameter. * <p> * Note <b>never</b> use this method to generate more that one password in sequence. Sequential * initialization of random generator dos not guarantee enough "randomness". Use {@link #generatePassword(int, String, int, String)} * method instead. * * @param length length of password to be generated * @param digits alphabet of letters allows within password * @param grpSize if password character groups should be delimited, size of character group. Zero otherwise * @param grpSep password character separator * @return random sequence of character from provided alphabet with given lenght and * optionaly separated by provided separator at every <code>grpSize</code> position * @throws IllegalArgumentException if any character of separator is contained in alphabet */ public static String generatePassword(int length, String digits, int grpSize, String grpSep) { String groupSeparator = grpSep; groupSeparator = testSeparator(digits, groupSeparator); SecureRandom rnd = new SecureRandom(); return generateSinglePswd(length, digits, grpSize, groupSeparator, rnd); } protected static String generateSinglePswd(int length, String digits, int grpSize, String grpSep, SecureRandom rnd) { int l = digits.length(); int bits = (int) Math.ceil(Math.log(Math.pow(l,length))/Math.log(2)); StringBuffer buf = new StringBuffer(); BigInteger bl = BigInteger.valueOf(l); BigInteger bi = new BigInteger(bits,rnd); for (int j=0;j<length;j++) { if (j!=0 && grpSize>0 && j%grpSize==0) buf.append(grpSep); int d = bi.mod(bl).intValue(); bi = bi.divide(bl); buf.append(digits.charAt(d)); } return buf.toString(); } private static String testSeparator(String digits, String grpSep) { String groupSeparator = grpSep; if (groupSeparator==null) { groupSeparator = ""; } for (int i=0;i<groupSeparator.length();i++) { if (digits.indexOf(groupSeparator.charAt(i))>=0) { throw new IllegalArgumentException("Separator contains digits"); } } return groupSeparator; } private PasswordGenerator() { } }