com.zimbra.common.util.RandomPassword.java Source code

Java tutorial

Introduction

Here is the source code for com.zimbra.common.util.RandomPassword.java

Source

/*
 * ***** BEGIN LICENSE BLOCK *****
 * Zimbra Collaboration Suite Server
 * Copyright (C) 2005, 2006, 2007, 2009, 2010, 2013, 2014, 2015, 2016 Synacor, Inc.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software Foundation,
 * version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with this program.
 * If not, see <https://www.gnu.org/licenses/>.
 * ***** END LICENSE BLOCK *****
 */

package com.zimbra.common.util;

import java.security.SecureRandom;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

public class RandomPassword {

    /**
     * 64 entry alphabet gives a 6 bits of entropy per character in the
     * password.
     *
     * http://world.std.com/~reinhold/dicewarefaq.html#calculatingentropy
     *
     * If the passphrase is made out of M symbols, each chosen at
     * random from a universe of N possibilities, each equally likely,
     * the entropy is M*log2(N).
     *
     */
    public static final String ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.";

    /*
     * RandomPassword is also used to generate local part of email.
     * Postfix has a limitation that it does not allow two dots in local part.
     * In the installer often local part generated by RandomPassword is concatenated
     * with a prefix, e.g. "spam.", and if the generated string has a leading dot then
     * the resulting local part will have two dots.  To get around it, we use a
     * alphabet set that does not contain . when generating local part.
     */
    public static final String ALPHABET_NO_DOT = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";

    public static final String ALPHABET_ONLY_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private static final int DEFAULT_MIN_LENGTH = 24;

    private static final int DEFAULT_MAX_LENGTH = 32;

    /**
     * http://world.std.com/~reinhold/passgen.html
     *
     * When using an 8-bit value to select a character from an
     * alphabet of length k, there is a risk of bias if k does not
     * evenly divide 256. To eliminate this, candidate cipher output
     * bytes are discarded if they are greater than or equal to the
     * largest multiple of k less than 256.
     */
    private static int byteLimit(int alphabetLength) {
        if (alphabetLength > 256) {
            // ie, some of the alphabet will never show up!
            throw new IllegalStateException("alphabet length " + alphabetLength + " has risk of bias");
        }
        return 256 - (256 % alphabetLength);
    }

    /**
     * Generate a random password of random length.
     */

    private static String generate(int minLength, int maxLength, boolean localpart) {
        String alphabet = localpart ? ALPHABET_NO_DOT : ALPHABET;
        return generate(minLength, maxLength, alphabet);
    }

    public static String generate(int minLength, int maxLength, String alphabet) {
        SecureRandom random = new SecureRandom();

        // Calculate the desired length of the password
        int length;
        if (minLength > maxLength) {
            throw new IllegalArgumentException("minLength=" + minLength + " > maxLength=" + maxLength);
        } else if (minLength < maxLength) {
            length = minLength + random.nextInt(1 + maxLength - minLength);
        } else {
            length = maxLength;
        }

        int alphabetLength = alphabet.length();
        int limit = byteLimit(alphabetLength);

        StringBuffer password = new StringBuffer(length);
        byte[] randomByte = new byte[1];

        while (password.length() < length) {
            random.nextBytes(randomByte);
            int i = randomByte[0] + 128;
            if (i < limit) {
                password.append(alphabet.charAt(i % alphabetLength));
            }
        }

        return password.toString();
    }

    public static String generate() {
        return generate(DEFAULT_MIN_LENGTH, DEFAULT_MAX_LENGTH, false);
    }

    private static void usage() {
        System.out.println("");
        System.out.println("RandomPassword [-l] <minLength> <maxLength>");
        System.exit(1);
    }

    public static void main(String args[]) {
        CommandLineParser parser = new GnuParser();
        Options options = new Options();
        options.addOption("l", "localpart", false, "generated string does not contain dot(.)");

        CommandLine cl = null;
        boolean err = false;

        try {
            cl = parser.parse(options, args, true);
        } catch (ParseException pe) {
            System.err.println("error: " + pe.getMessage());
            err = true;
        }

        if (err || cl.hasOption('h')) {
            usage();
        }

        boolean localpart = false;
        int minLength = DEFAULT_MIN_LENGTH;
        int maxLength = DEFAULT_MAX_LENGTH;

        if (cl.hasOption('l'))
            localpart = true;

        args = cl.getArgs();

        if (args.length != 0) {
            if (args.length != 2) {
                usage();
            }
            try {
                minLength = Integer.valueOf(args[0]).intValue();
                maxLength = Integer.valueOf(args[1]).intValue();
            } catch (Exception e) {
                System.err.println(e);
                e.printStackTrace();
            }
        }

        System.out.println(generate(minLength, maxLength, localpart));
    }
}