web-dev-qa-db-fra.com

Générer un mot de passe aléatoire sécurisé en Java avec des exigences minimales relatives aux caractères spéciaux

Comment créer un mot de passe aléatoire répondant aux exigences de longueur et de jeu de caractères du système en Java? 

Je dois créer un mot de passe aléatoire composé de 10 à 14 caractères et comportant au moins une majuscule, une minuscule et un caractère spécial. Malheureusement, certains caractères spéciaux sont aussi spécial et ne peut pas être utilisé, je ne peux donc pas utiliser uniquement du texte ASCII imprimé.

La plupart des exemples sur ce site génèrent un mot de passe aléatoire ou une clé de session sans suffisamment d'entropie dans les caractères ni d'exigences réalistes dans un contexte professionnel tel que ceux indiqués ci-dessus. Je pose donc une question plus précise pour obtenir une meilleure réponse.

Mon jeu de caractères, tous les caractères spéciaux d'un clavier américain standard, à l'exception d'un espace:

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

J'ai récemment entendu parler de Passay . Il fournit les fonctionnalités requises dans la classe PasswordGenerator . Il génère de manière aléatoire des mots de passe répondant aux exigences similaires à celles décrites ci-dessous, en utilisant CharacterRules plutôt que PasswordCharacterSets, comme je l’ai fait ci-dessous. Au lieu de conserver une liste d'index inutilisés pour l'insertion aléatoire de caractères, il mélange simplement le tampon de caractères après avoir inséré les caractères répondant aux critères.

Ci-dessous, je vous recommande d'utiliser Passay si votre licence le permet, ce code devrait fonctionner autrement et explique en détail pourquoi les mots de passe générés sont hautement cryptographiques}

J'ai fini par écrire ce code deux fois. Une fois pour obtenir un résultat de caractère aléatoire, mais il s'est avéré que la distribution des caractères dépend de la taille du jeu de caractères (oups!). Je l'ai réécrit et maintenant vous devriez simplement copier/coller le code et changer le fichier Main.Java aux jeux de caractères souhaités. Bien que cela aurait pu être fait différemment, je pense que cette approche est relativement simple pour obtenir le résultat correct et j'encourage la réutilisation, les commentaires, les critiques et les modifications bien pensées.

Les contrôles du code PasswordGenerator sont les suivants:

  • Longueur Min/Max: Définir en utilisant un nombre aléatoire
  • PasswordCharacterSetRemarque: Il est supposé que tous les PasswordCharacterSets transmis à PasswordGenerator sont constitués de jeux de caractères uniques. Dans le cas contraire, les caractères aléatoires auront un biais vers les doublons.
  • PasswordCharacterSet Min Caractères: Les caractères min à utiliser pour ce jeu de caractères.

Les bits principaux pour la génération du mot de passe actuel:

  • Aléatoire de Aléatoire: Nous utilisons SecureRandom, qui s'appuie sur un PRNG extrêmement cryptographique, plutôt que sur la classe Random, qui ne l’est pas.
  • Ordre aléatoire des caractères pour le mot de passe: Tous les index du tableau pw char sont ajoutés au tableau resteIndexes. Lorsque nous appelons addRandomCharacters, il supprime un index de manière aléatoire et nous utilisons l'index supprimé pour renseigner le tableau.
  • Caractères aléatoires: Dans addRandomCharacters, un index aléatoire de l'index de caractère que nous utilisons est choisi et ajouté au tableau pw. 
  • Garantir le minimum de caractères de chaque type sont définis: Nous découpons simplement le montant minimum de caractères en premier. Nous choisissons la quantité minimale de valeurs aléatoires de chaque jeu de caractères, puis passons à autre chose.
  • Distribution aléatoire pour les caractères restants: Une fois les valeurs minimales définies, nous souhaitons rendre le reste des caractères aléatoire dans tous les jeux de caractères. Tous les caractères sont ajoutés à un seul tableau. Les emplacements restants sont remplis en utilisant la même stratégie pour les jeux de caractères précédents.

Description de la complexité du mot de passe: La complexité du mot de passe est généralement décrite en bits d'entropie. Voici le nombre de possibilités pour votre espace de clé:

Il existe au moins un caractère alpha majuscule (sur 26), un caractère alpha minuscule (sur 26), un chiffre (sur 10) et un caractère spécial (sur 32), la façon dont vous calculez le nombre de possibilités. est le nombre de possibilités pour chaque caractère multiplié par le nombre de caractères car ils sont placés aléatoirement dans la chaîne. Nous savons donc que les possibilités pour quatre des personnages sont:

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

Tous les personnages restants ont chacun 94 possibilités (26 + 26 + 10 + 32)

Notre calcul est:

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

Dans cet esprit, si vous voulez les mots de passe les plus sécurisés, vous devez toujours choisir le plus grand nombre de caractères possible, soit 14 dans ce cas.

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

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;
        }
    }
}
9
summer

Je suggère d'utiliser RandomStringUtils commons Apache. Utilisez quelque chose qui est déjà fait.

String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^&*()-_=+[{]}\\|;:\'\",<.>/?";
String pwd = RandomStringUtils.random( 15, characters );
System.out.println( pwd );

Si vous utilisez maven 

<dependency>
    <groupId>org.Apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.4</version>
</dependency>

sinon téléchargez le bocal

UPDATE Version avec random random sécurisée. Donc, peu importe le nombre de caractères requis qui peuvent être résolus comme dans un commentaire, générez les parties requises séparément et les normales. Puis rejoignez-les au hasard.

char[] possibleCharacters = (new String("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^&*()-_=+[{]}\\|;:\'\",<.>/?")).toCharArray();
String randomStr = RandomStringUtils.random( randomStrLength, 0, possibleCharacters.length-1, false, false, possibleCharacters, new SecureRandom() );
System.out.println( randomStr );
29
Robert Wadowski

En utilisant la fonctionnalité aléatoire du package Java.util de rt.jar, nous pouvons créer un mot de passe aléatoire de n'importe quelle longueur. ci-dessous est l'extrait pour la même chose.

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);
}

}

0
Manjunatha B

Voici un utilitaire qui utilise uniquement Java Java et implémente les exigences. Fondamentalement, il obtient l'un des jeux de caractères requis. Remplit ensuite le reste avec des caractères aléatoires de l'ensemble. Puis mélange le tout.

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));
        }
    }
}
0
Jose Martinez