Je cherchais un algorithme Java simple pour générer une chaîne alphanumérique pseudo-aléatoire. Dans ma situation, il serait utilisé comme un identifiant de session / clé unique qui serait "probablement" unique au fil de la 500K+
génération (mes besoins n'exigent pas vraiment quelque chose de beaucoup plus sophistiqué).
Idéalement, je serais en mesure de spécifier une longueur en fonction de mes besoins d'unicité. Par exemple, une chaîne générée de longueur 12 pourrait ressembler à quelque chose "AEYGF7K0DM1X"
.
Long.toHexString(Double.doubleToLongBits(Math.random()));
UUID.randomUUID().toString();
RandomStringUtils.randomAlphanumeric(12);
Réponses:
Algorithme
Pour générer une chaîne aléatoire, concaténez des caractères tirés au hasard à partir de l'ensemble de symboles acceptables jusqu'à ce que la chaîne atteigne la longueur souhaitée.
la mise en oeuvre
Voici un code assez simple et très flexible pour générer des identificateurs aléatoires. Lisez les informations qui suivent pour les notes d'application importantes.
Exemples d'utilisation
Créez un générateur non sécurisé pour les identifiants à 8 caractères:
Créez un générateur sécurisé pour les identifiants de session:
Créez un générateur avec des codes faciles à lire pour l'impression. Les chaînes sont plus longues que les chaînes alphanumériques complètes pour compenser l'utilisation de moins de symboles:
Utiliser comme identificateurs de session
Générer des identifiants de session susceptibles d'être uniques n'est pas suffisant, ou vous pouvez simplement utiliser un simple compteur. Les attaquants détournent des sessions lorsque des identifiants prévisibles sont utilisés.
Il existe une tension entre la longueur et la sécurité. Les identifiants plus courts sont plus faciles à deviner, car il y a moins de possibilités. Mais les identifiants plus longs consomment plus de stockage et de bande passante. Un plus grand ensemble de symboles est utile, mais peut entraîner des problèmes de codage si les identifiants sont inclus dans les URL ou saisis à nouveau manuellement.
La source sous-jacente d'aléatoire, ou d'entropie, pour les identificateurs de session devrait provenir d'un générateur de nombres aléatoires conçu pour la cryptographie. Cependant, l'initialisation de ces générateurs peut parfois être coûteuse ou lente en termes de calcul, il convient donc de s'efforcer de les réutiliser lorsque cela est possible.
Utiliser comme identificateurs d'objet
Toutes les applications ne nécessitent pas de sécurité. L'affectation aléatoire peut être un moyen efficace pour plusieurs entités de générer des identifiants dans un espace partagé sans aucune coordination ni partitionnement. La coordination peut être lente, en particulier dans un environnement en cluster ou distribué, et la division d'un espace provoque des problèmes lorsque les entités se retrouvent avec des partages trop petits ou trop grands.
Les identifiants générés sans prendre de mesures pour les rendre imprévisibles doivent être protégés par d'autres moyens si un attaquant peut les voir et les manipuler, comme cela se produit dans la plupart des applications Web. Il devrait y avoir un système d'autorisation distinct qui protège les objets dont l'identifiant peut être deviné par un attaquant sans autorisation d'accès.
Il faut également veiller à utiliser des identifiants suffisamment longs pour rendre les collisions improbables compte tenu du nombre total prévu d'identifiants. C'est ce qu'on appelle «le paradoxe de l'anniversaire». La probabilité d'une collision, p , est d'environ n 2 / (2q x ), où n est le nombre d'identificateurs réellement générés, q est le nombre de symboles distincts dans l'alphabet et x est la longueur des identificateurs. Ce devrait être un très petit nombre, comme 2 à 50 ou moins.
Ce travail montre que le risque de collision entre 500k identificateurs à 15 caractères est d'environ 2 à 52 , ce qui est probablement moins probable que les erreurs non détectées des rayons cosmiques, etc.
Comparaison avec les UUID
Selon leur spécification, les UUID ne sont pas conçus pour être imprévisibles et ne doivent pas être utilisés comme identificateurs de session.
Les UUID dans leur format standard prennent beaucoup de place: 36 caractères pour seulement 122 bits d'entropie. (Tous les bits d'un UUID "aléatoire" ne sont pas sélectionnés au hasard.) Une chaîne alphanumérique choisie au hasard contient plus d'entropie en seulement 21 caractères.
Les UUID ne sont pas flexibles; ils ont une structure et une disposition normalisées. C'est leur principale vertu ainsi que leur principale faiblesse. Lorsque vous collaborez avec un tiers, la standardisation offerte par les UUID peut être utile. Pour un usage purement interne, ils peuvent être inefficaces.
la source
.replaceAll("\\d", " ");
au bout de lareturn new BigInteger(130, random).toString(32);
ligne pour effectuer un échange d'expression régulière. Il remplace tous les chiffres par des espaces. Fonctionne très bien pour moi: je l'utilise comme substitut d'un Lorem Ipsumsymbols
et en utilisant un espace à la place; vous pouvez contrôler la longueur moyenne des "mots" en modifiant le nombre d'espaces dans les symboles (plus d'occurrences pour les mots plus courts). Pour une solution de faux texte vraiment exagérée, vous pouvez utiliser une chaîne Markov!SecureRandom
instance affectée à larandom
variable.Java fournit un moyen de le faire directement. Si vous ne voulez pas les tirets, ils sont faciles à retirer. Utilisez simplement
uuid.replace("-", "")
Production:
la source
UUID.randomUUID().toString().replaceAll("-", "");
rend la chaîne alphanumérique, comme demandé.la source
SecureRandom
place de laRandom
classe. Si des mots de passe sont générés sur un serveur, il peut être vulnérable aux attaques de synchronisation.AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
et quelques autres caractères autorisés.static Random rnd = new Random();
à l'intérieur de la méthode?Random
objet dans chaque appel de méthode? Je ne pense pas.Si vous êtes heureux d'utiliser des classes Apache, vous pouvez utiliser
org.apache.commons.text.RandomStringGenerator
(commons-text).Exemple:
Depuis commons-lang 3.6,
RandomStringUtils
est obsolète.la source
Apache Commons Lang 3.3.1
bibliothèque mentionnée - et il n'utilise quejava.util.Random
pour fournir des séquences aléatoires, il produit donc des séquences non sécurisées .public static java.lang.String random(int count, int start, int end, boolean letters, boolean numbers, @Nullable char[] chars, java.util.Random random)
Vous pouvez utiliser la bibliothèque Apache pour cela: RandomStringUtils
la source
compile 'commons-lang:commons-lang:2.6'
SecureRandom
et vous êtes bon.En une seule ligne:
la source
AEYGF7K0DM1X
qui n'est pas hexadécimal. Je m'inquiète de la fréquence à laquelle les gens confondent alphanumérique et hexadécimal. Ce n'est pas la même chose.Math.random()
produit undouble
entre 0 et 1, donc la partie exposant est principalement inutilisée. Utilisezrandom.nextLong
pour un hasardlong
au lieu de ce vilain hack.Ceci est facilement réalisable sans aucune bibliothèque externe.
1. Génération cryptographique de données pseudo aléatoires
Vous avez d'abord besoin d'un PRNG cryptographique. Java a
SecureRandom
pour cela et utilise généralement la meilleure source d'entropie sur la machine (par exemple/dev/random
). En savoir plus ici.Remarque:
SecureRandom
est le moyen le plus lent, mais le plus sûr en Java de générer des octets aléatoires. Je recommande cependant de NE PAS considérer les performances ici car elles n'ont généralement pas d'impact réel sur votre application, sauf si vous devez générer des millions de jetons par seconde.2. Espace requis des valeurs possibles
Ensuite, vous devez décider «à quel point» votre jeton doit être unique. Le seul et unique point de considérer l'entropie est de s'assurer que le système peut résister aux attaques par force brute: l'espace des valeurs possibles doit être si grand que tout attaquant ne peut essayer qu'une proportion négligeable des valeurs en un temps non ridicule 1 . Les identifiants uniques tels que random
UUID
ont 122 bits d'entropie (c.-à-d. 2 ^ 122 = 5,3x10 ^ 36) - le risque de collision est "* (...) pour qu'il y ait une chance sur un milliard de duplication, version 103 trillions 4 UUID doivent être générés 2 ". Nous choisirons 128 bits car il tient exactement dans 16 octets et est considéré comme hautement suffisantpour être unique pour pratiquement tous les cas d'utilisation, mais les plus extrêmes, et vous n'avez pas à penser aux doublons. Voici un tableau de comparaison simple d'entropie comprenant une analyse simple du problème d'anniversaire .Pour des exigences simples, une longueur de 8 ou 12 octets peut suffire, mais avec 16 octets, vous êtes du "côté sûr".
Et c'est essentiellement ça. La dernière chose est de penser à l'encodage afin qu'il puisse être représenté comme un texte imprimable (lire, a
String
).3. Codage binaire en texte
Les encodages typiques incluent:
Base64
chaque personnage code 6 bits, créant une surcharge de 33%. Heureusement, il existe des implémentations standard dans Java 8+ et Android . Avec Java plus ancien, vous pouvez utiliser l'une des nombreuses bibliothèques tierces . Si vous voulez que vos jetons à url utilisation en toute sécurité l' URL de sécurité version de RFC4648 (qui est généralement pris en charge par la plupart des implémentations). Exemple d'encodage de 16 octets avec remplissage:XfJhfv3C0P6ag7y9VQxSbw==
Base32
chaque personnage code 5 bits, créant une surcharge de 40%. Cela utiliseraA-Z
et rendra l'2-7
espace raisonnablement efficace tout en étant insensible à la casse alphanumérique. Il n'y a pas d' implémentation standard dans le JDK . Exemple d'encodage de 16 octets sans remplissage:WUPIL5DQTZGMF4D3NX5L7LNFOY
Base16
(hex) chaque caractère code 4 bits nécessitant 2 caractères par octet (c'est-à-dire que 16 octets créent une chaîne de longueur 32). Par conséquent, hex est moins efficace en espace queBase32
mais est sûr à utiliser dans la plupart des cas (url) car il n'utilise que0-9
etA
pourF
. Exemple 16 octets codant pour :4fa3dd0f57cb3bf331441ed285b27735
. Voir une discussion SO sur la conversion en hexadécimal ici.Des encodages supplémentaires comme Base85 et exotique Base122 existent avec une efficacité d'espace meilleure / pire. Vous pouvez créer votre propre encodage (ce que font essentiellement la plupart des réponses de ce fil), mais je le déconseille si vous n'avez pas d'exigences très spécifiques. Voir plus de schémas d'encodage dans l'article Wikipedia.
4. Résumé et exemple
SecureRandom
hex
oubase32
si vous avez besoin qu'il soit alphanumérique)Ne le fais pas
Exemple: Générateur de jetons hexadécimal
Exemple: Générateur de jetons Base64 (sans danger pour les URL)
Exemple: outil CLI Java
Si vous voulez un outil cli prêt à l'emploi, vous pouvez utiliser des dés: https://github.com/patrickfav/dice
Exemple: problème connexe - Protégez vos identifiants actuels
Si vous avez déjà un identifiant que vous pouvez utiliser (par exemple un synthétique
long
dans votre entité), mais que vous ne souhaitez pas publier la valeur interne , vous pouvez utiliser cette bibliothèque pour la crypter et la masquer: https://github.com/patrickfav / id-maskla source
BigInteger
s négatifs en utilisant un paramètre constructeur:BigInteger(1, token)
au lieu deBigInteger(token)
.import java.security.SecureRandom;
etimport java.math.BigInteger;
sont nécessaires pour faire fonctionner l'exemple, mais cela fonctionne très bien!new SecureRandom()
utilisations/dev/urandom
utiliser Dollar doit être simple:
il produit quelque chose comme ça:
la source
Le voici en Java:
Voici un exemple d'exécution:
la source
Random#nextInt
ounextLong
. Passez àSecureRandom
si nécessaire.Surprenant, personne ici ne l'a suggéré, mais:
Facile.
L'avantage est que les UUID sont agréables et longs et garantis presque impossibles à entrer en collision.
Wikipédia en a une bonne explication:
http://en.wikipedia.org/wiki/Universally_unique_identifier#Random_UUID_probability_of_duplicates
Les 4 premiers bits sont le type de version et 2 pour la variante, vous obtenez donc 122 bits de hasard. Donc, si vous le souhaitez, vous pouvez tronquer depuis la fin pour réduire la taille de l'UUID. Ce n'est pas recommandé, mais vous avez toujours beaucoup d'aléatoire, assez pour vos enregistrements de 500k faciles.
la source
Une solution courte et facile, mais utilise uniquement des minuscules et des chiffres:
La taille est d'environ 12 chiffres à la base 36 et ne peut pas être améliorée de cette façon. Bien sûr, vous pouvez ajouter plusieurs instances.
la source
Long.toString(Math.abs(r.nextLong()), 36);
abs
est résolu en utilisant un opérateur au niveau du bit pour effacer le bit le plus significatif. Cela fonctionnera pour toutes les valeurs.<< 1 >>> 1
.Une alternative à Java 8 est:
la source
L'utilisation des UUID n'est pas sécurisée, car certaines parties de l'UUID ne sont pas aléatoires du tout. La procédure de @erickson est très soignée, mais ne crée pas de chaînes de même longueur. L'extrait suivant devrait être suffisant:
Pourquoi choisir
length*5
. Supposons le cas simple d'une chaîne aléatoire de longueur 1, donc un caractère aléatoire. Pour obtenir un caractère aléatoire contenant tous les chiffres 0-9 et les caractères az, nous aurions besoin d'un nombre aléatoire compris entre 0 et 35 pour obtenir un de chaque caractère.BigInteger
fournit un constructeur pour générer un nombre aléatoire, uniformément distribué sur la plage0 to (2^numBits - 1)
. Malheureusement, 35 n'est pas un numéro qui peut être reçu par 2 ^ numBits - 1. Nous avons donc deux options: soit aller avec2^5-1=31
ou2^6-1=63
. Si nous choisissions,2^6
nous obtiendrions un grand nombre de numéros «sans césarienne» / «plus longs». Par conséquent, le nombre. Le dernier problème, si nous voulons une chaîne avec une certaine longueur, random pourrait générer un petit nombre, donc la longueur n'est pas respectée, nous devons donc remplir la chaîne à sa longueur requise avant les zéros.2^5
la meilleure option, même si nous perdons 4 caractères (wz). Pour générer maintenant une chaîne d'une certaine longueur, nous pouvons simplement utiliser un2^(length*numBits)-1
la source
la source
Donc, cela ne fait qu'ajouter le mot de passe dans la chaîne et ... ouais fonctionne bien, vérifiez-le ... très simple. je l'ai écrit
la source
+ 0
cela souvent? Pourquoi divisez-vous la déclaration de tache et l'initialisation? Quel est l'avantage des indices 1,2,3,4 au lieu de 0,1,2,3? Plus important encore: vous avez pris une valeur aléatoire et comparé à if-else 4 fois une nouvelle valeur, qui pourrait toujours ne pas correspondre, sans gagner plus de caractère aléatoire. Mais n'hésitez pas à revenir en arrière.J'ai trouvé cette solution qui génère une chaîne codée hexadécimale aléatoire. Le test unitaire fourni semble résister à mon cas d'utilisation principal. Cependant, il est légèrement plus complexe que certaines des autres réponses fournies.
la source
Modifiez les caractères de chaîne selon vos besoins.
La chaîne est immuable. Voici
StringBuilder.append
plus efficace que la concaténation de chaînes.la source
Random
instance à chaque itération de la boucle est inefficace.la source
la source
Je n'aime vraiment aucune de ces réponses concernant la solution "simple": S
J'irais pour un simple;), java pur, un liner (l'entropie est basée sur une longueur de chaîne aléatoire et le jeu de caractères donné):
ou (un peu plus lisible à l'ancienne)
Mais d'un autre côté, vous pouvez également utiliser UUID qui a une assez bonne entropie ( https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions ):
J'espère que cela pourra aider.
la source
Vous mentionnez "simple", mais juste au cas où quelqu'un d'autre chercherait quelque chose qui réponde à des exigences de sécurité plus strictes, vous voudrez peut-être jeter un œil à jpwgen . jpwgen est calqué sur pwgen sous Unix et est très configurable.
la source
Vous pouvez utiliser la classe UUID avec son message getLeastSignificantBits () pour obtenir 64 bits de données aléatoires, puis les convertir en un numéro radix 36 (c'est-à-dire une chaîne composée de 0-9, AZ):
Cela donne une chaîne de 13 caractères maximum. Nous utilisons Math.abs () pour nous assurer qu'il n'y a pas de signe moins se faufilant.
la source
random.nextLong()
? Ou mêmeDouble.doubleToLongBits(Math.random())
?Vous pouvez utiliser le code suivant, si votre mot de passe obligatoire contient des chiffres alphabétiques spéciaux:
la source
Voici le code d'une ligne par AbacusUtil
Aléatoire ne signifie pas qu'il doit être unique. pour obtenir des chaînes uniques, en utilisant:
la source
Voici une solution Scala:
la source
en utilisant la bibliothèque apache, cela peut être fait en une seule ligne
voici le doc http://commons.apache.org/lang/api-2.3/org/apache/commons/lang/RandomStringUtils.html
la source
la source
Je pense que c'est la plus petite solution ici, ou presque l'une des plus petites:
Le code fonctionne très bien. Si vous utilisez cette méthode, je vous recommande d'utiliser plus de 10 caractères. La collision se produit à 5 caractères / 30362 itérations. Cela a pris 9 secondes.
la source
la source
length
au lieu dechars.length
dans la boucle for:for (int i = 0; i < length; i++)
la source