Rasgele bir alfa-sayısal dize nasıl oluşturulur?


1741

Sahte rasgele alfa-sayısal bir dize oluşturmak için basit bir Java algoritması arıyordum . Benim durumumda, bu, 500K+nesil boyunca benzersiz olması muhtemel benzersiz bir oturum / anahtar tanımlayıcı olarak kullanılacaktır (ihtiyaçlarım çok daha karmaşık bir şey gerektirmiyor).

İdeal olarak, benzersizlik ihtiyaçlarıma bağlı olarak bir uzunluk belirleyebilirdim. Örneğin, uzunluk 12'nin oluşturulmuş bir dizesi şuna benzeyebilir "AEYGF7K0DM1X".



58
Doğum günü paradoksu dikkate alınsa bile, 12 alfanümerik karakter kullanırsanız (toplam 62), paradoksa ulaşmak için 34 milyardan fazla dizeye ihtiyacınız olacaktır. Ve doğum günü paradoksu zaten bir çarpışmayı garanti etmiyor, sadece% 50'den fazla şans olduğunu söylüyor.
NullUserException

4
@NullUserException% 50 başarı şansı (deneme başına) çok yüksek: 10 denemede bile başarı oranı 0.999'dur. Bu ve 24 saatlik bir süre içinde A LOT'u deneyebileceğiniz gerçeği ile, en az birini tahmin edebileceğinizden emin olmak için 34 milyar dizeye ihtiyacınız yok. Bazı oturum belirteçlerinin gerçekten çok uzun olmasının nedeni budur.
Pijusn

16
Bu 3 tek satır kod sanırım çok yararlı ..Long.toHexString(Double.doubleToLongBits(Math.random())); UUID.randomUUID().toString(); RandomStringUtils.randomAlphanumeric(12);
Manindar

18
@Pijusn Bunun eski olduğunu biliyorum, ama ... doğum günü paradoksunda "% 50 şans" denemeye " DEĞİL ", (% 50), (bu durumda) 34 milyar ipin içinde msgstr "en az bir çift kopya". Deneme başına% 50 şansın olması için veri tabanınızdaki 1.6 sept illion - 1.6e21 - girişlerine ihtiyacınız olacaktır.
Kalay Sihirbazı

Yanıtlar:


1541

Algoritma

Rastgele bir dize oluşturmak için, kabul edilebilir simgeler kümesinden rastgele çizilen karakterleri, dize istenen uzunluğa ulaşıncaya kadar birleştirin.

uygulama

Rastgele tanımlayıcılar oluşturmak için oldukça basit ve çok esnek bir kod. Önemli uygulama notları için aşağıdaki bilgileri okuyun .

public class RandomString {

    /**
     * Generate a random string.
     */
    public String nextString() {
        for (int idx = 0; idx < buf.length; ++idx)
            buf[idx] = symbols[random.nextInt(symbols.length)];
        return new String(buf);
    }

    public static final String upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    public static final String lower = upper.toLowerCase(Locale.ROOT);

    public static final String digits = "0123456789";

    public static final String alphanum = upper + lower + digits;

    private final Random random;

    private final char[] symbols;

    private final char[] buf;

    public RandomString(int length, Random random, String symbols) {
        if (length < 1) throw new IllegalArgumentException();
        if (symbols.length() < 2) throw new IllegalArgumentException();
        this.random = Objects.requireNonNull(random);
        this.symbols = symbols.toCharArray();
        this.buf = new char[length];
    }

    /**
     * Create an alphanumeric string generator.
     */
    public RandomString(int length, Random random) {
        this(length, random, alphanum);
    }

    /**
     * Create an alphanumeric strings from a secure generator.
     */
    public RandomString(int length) {
        this(length, new SecureRandom());
    }

    /**
     * Create session identifiers.
     */
    public RandomString() {
        this(21);
    }

}

Kullanım örnekleri

8 karakterli tanımlayıcılar için güvenli olmayan bir jeneratör oluşturun:

RandomString gen = new RandomString(8, ThreadLocalRandom.current());

Oturum tanımlayıcıları için güvenli bir jeneratör oluşturun:

RandomString session = new RandomString();

Yazdırma için okunması kolay kodlara sahip bir jeneratör oluşturun. Daha az sembol kullanmayı telafi etmek için dizeler tam alfasayısal dizelerden daha uzundur:

String easy = RandomString.digits + "ACEFGHJKLMNPQRUVWXYabcdefhijkprstuvwx";
RandomString tickets = new RandomString(23, new SecureRandom(), easy);

Oturum tanımlayıcıları olarak kullan

Benzersiz olması muhtemel oturum tanımlayıcıları oluşturmak yeterince iyi değildir, ya da basit bir sayaç kullanabilirsiniz. Saldırganlar öngörülebilir tanımlayıcılar kullanıldığında oturumları ele geçirir.

Uzunluk ve güvenlik arasında gerginlik var. Daha kısa tanımlayıcıların tahmin edilmesi daha kolaydır, çünkü daha az olasılık vardır. Ancak daha uzun tanımlayıcılar daha fazla depolama alanı ve bant genişliği tüketir. Daha büyük bir simge seti yardımcı olur, ancak URL'lere tanımlayıcılar dahil edilirse veya elle yeniden girilirse kodlama sorunlarına neden olabilir.

Oturum tanımlayıcıları için temeldeki rasgelelik veya entropi kaynağı, kriptografi için tasarlanmış rastgele bir sayı üreticisinden gelmelidir. Bununla birlikte, bu jeneratörlerin başlatılması bazen hesaplama açısından pahalı veya yavaş olabilir, bu nedenle mümkünse bunları yeniden kullanmak için çaba gösterilmelidir.

Nesne tanımlayıcıları olarak kullan

Her uygulama güvenlik gerektirmez. Rastgele atama, birden çok kuruluş için herhangi bir koordinasyon veya bölümleme olmaksızın paylaşılan bir alanda tanımlayıcılar oluşturmak için etkili bir yol olabilir. Koordinasyon, özellikle kümelenmiş veya dağıtılmış bir ortamda yavaş olabilir ve bir alanın bölünmesi, kuruluşların çok küçük veya çok büyük paylaşımlarla sonuçlanması durumunda sorunlara neden olur.

Bir saldırgan, çoğu web uygulamasında olduğu gibi bunları görüntüleyip değiştirebiliyorsa, bunları öngörülemez hale getirmek için önlem almadan oluşturulan tanımlayıcılar başka yollarla korunmalıdır. Tanımlayıcısı bir saldırgan tarafından erişim izni olmadan tahmin edilebilecek nesneleri koruyan ayrı bir yetkilendirme sistemi olmalıdır.

Öngörülen toplam tanımlayıcı sayısı göz önüne alındığında, çarpışma olasılığını ortadan kaldıracak kadar uzun tanımlayıcıların kullanılmasına da dikkat edilmelidir. Buna "doğum günü paradoksu" denir. Bir çarpışma olasılığı, p , yaklaşık olarak n 2 / (2q x ) 'dir, burada n , gerçekte oluşturulan tanımlayıcıların sayısıdır, q , alfabedeki farklı sembollerin sayısıdır ve x , tanımlayıcıların uzunluğudur. Bu, 2 ‑50 veya daha az gibi çok küçük bir sayı olmalıdır .

Bunu çözmek, 500k 15 karakterlik tanımlayıcılar arasında çarpışma olasılığının yaklaşık 2 ‑52 olduğunu gösterir , bu muhtemelen kozmik ışınlardan vb. Tespit edilmeyen hatalardan daha az olasıdır.

UUID'lerle Karşılaştırma

Onların Patent açıklamasına göre, Uuıdlerin öngörülemeyen olacak şekilde tasarlanmıştır değildir ve olmamalıdır oturum tanımlayıcıları olarak kullanılabilir.

Standart formatlarındaki UUID'ler çok yer kaplar: Sadece 122 bit entropi için 36 karakter. ("Rastgele" bir UUID'nin tüm bitleri rastgele seçilmez.) Rastgele seçilen bir alfasayısal dize, yalnızca 21 karakterde daha fazla entropi oluşturur.

UUID'ler esnek değildir; standart bir yapıya ve düzene sahiptirler. Bu onların başlıca erdemleri ve ana zayıflıklarıdır. Bir dış tarafla işbirliği yaparken, UUID'ler tarafından sunulan standartlaştırma yardımcı olabilir. Tamamen dahili kullanım için verimsiz olabilirler.


6
Sizinkinde boşluklara ihtiyacınız varsa, normal rezerve takası yapmak .replaceAll("\\d", " ");için return new BigInteger(130, random).toString(32);hattın sonuna yapıştırabilirsiniz . Tüm basamakları boşluklarla değiştirir. Benim için harika çalışıyor: Bunu bir ön uç Lorem
Ipsum'un

4
@weisjohn Bu iyi bir fikir. İkinci yönteme benzer bir şey yapabilirsiniz, symbolsbunun yerine basamakları kaldırarak ve boşluk kullanarak; sembollerdeki boşluk sayısını değiştirerek ortalama "kelime" uzunluğunu kontrol edebilirsiniz (daha kısa kelimeler için daha fazla tekrar). En üst düzey sahte metin çözümü için bir Markov zinciri kullanabilirsiniz!
erickson

4
Bu tanımlayıcılar belirli bir boyuttaki boşluktan rastgele seçilir. 1 karakter uzunluğunda olabilirler. Sabit bir uzunluk istiyorsanız SecureRandom, randomdeğişkene atanmış bir örnekle ikinci çözümü kullanabilirsiniz .
erickson

15
Neden .toString (36) yerine .toString (32)?
ejain

17
@ejain çünkü 32 = 2 ^ 5; her karakter tam olarak 5 biti temsil eder ve 130 bit karakterlere eşit olarak bölünebilir.
erickson

817

Java bunu doğrudan yapmanın bir yolunu sunar. Çizgileri istemiyorsanız, çıkarılması kolaydır. Sadece kullanuuid.replace("-", "")

import java.util.UUID;

public class randomStringGenerator {
    public static void main(String[] args) {
        System.out.println(generateString());
    }

    public static String generateString() {
        String uuid = UUID.randomUUID().toString();
        return "uuid = " + uuid;
    }
}

Çıktı:

uuid = 2d7428a6-b58c-4008-8575-f05549f16316

33
Bu çözümün yalnızca onaltılık karakterler içeren rastgele bir dize oluşturduğuna dikkat edin. Hangi bazı durumlarda iyi olabilir.
Dave

5
UUID sınıfı kullanışlıdır. Ancak, cevaplarım tarafından üretilen tanımlayıcılar kadar kompakt değiller. Bu, örneğin URL'lerde bir sorun olabilir. İhtiyaçlarınıza bağlıdır.
erickson

6
@Ruggs - Hedef alfa-sayısal dizeler. Çıktıyı olası baytlara genişletmek buna nasıl uyuyor?
erickson

72
RFC4122'ye göre UUID'lerin jeton olarak kullanılması kötü bir fikirdir: UUID'lerin tahmin edilmesinin zor olduğunu varsaymayın; örneğin, güvenlik yetenekleri olarak kullanılmamalıdır (yalnızca mülkiyeti erişim izni veren tanımlayıcılar). Öngörülebilir rastgele bir sayı kaynağı durumu daha da kötüleştirecektir. ietf.org/rfc/rfc4122.txt
Somatik

34
UUID.randomUUID().toString().replaceAll("-", "");dizeyi istendiği gibi alfa-sayısal yapar.
Numid

546
static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static SecureRandom rnd = new SecureRandom();

String randomString( int len ){
   StringBuilder sb = new StringBuilder( len );
   for( int i = 0; i < len; i++ ) 
      sb.append( AB.charAt( rnd.nextInt(AB.length()) ) );
   return sb.toString();
}

61
+1, belirtilen uzunlukta rastgele bir dize oluşturmak için en basit çözüm (Commons Lang'dan RandomStringUtils kullanmak dışında).
Jonik

12
Sınıf SecureRandomyerine kullanmayı düşünün Random. Parolalar bir sunucuda oluşturulursa, zamanlama saldırılarına karşı savunmasız olabilir.
foens

8
Ben de küçük harf eklemek istiyorum: AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";ve diğer bazı izin verilen karakter.
ACV

1
Neden static Random rnd = new Random();yöntemin içine girmiyorsunuz?
Micro

4
@MicroR RandomHer yöntem çağrısında nesne oluşturmak için iyi bir neden var mı ? Ben öyle düşünmüyorum.
cassiomolin

484

Apache sınıflarını kullanmaktan memnunsanız org.apache.commons.text.RandomStringGenerator(commons-text) kullanabilirsiniz.

Misal:

RandomStringGenerator randomStringGenerator =
        new RandomStringGenerator.Builder()
                .withinRange('0', 'z')
                .filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS)
                .build();
randomStringGenerator.generate(12); // toUpperCase() if you want

Commons-lang 3.6 olduğundan RandomStringUtils, kullanımdan kaldırılmıştır.


22
Sadece baktın Has bahsedilen sınıfın içinde Apache Commons Lang 3.3.1kütüphaneye - ve sadece kullandığı java.util.Randombu üretiyor, böylece rasgele dizileri sağlamak üzere güvensiz dizileri .
Yuriy Nakonechnyy

16
RandomStringUtils kullanırken SecureRandom kullandığınızdan emin olun:public static java.lang.String random(int count, int start, int end, boolean letters, boolean numbers, @Nullable char[] chars, java.util.Random random)
Ruslans Uralovs

KULLANMAYIN. Bu güvensiz diziler yaratır !
Patrick Favre

110

Bunun için Apache kütüphanesini kullanabilirsiniz: RandomStringUtils

RandomStringUtils.randomAlphanumeric(20).toUpperCase();

18
@kamil, RandomStringUtils için kaynak koduna baktım ve argümanlar olmadan örneklenen bir java.util.Random örneği kullanıyor. Java.util.Random belgeleri hiçbir tohum sağlanmadığında geçerli sistem saatini kullandığını söylüyor. Bu, saldırganın oluşturulan oturum tanımlayıcılarının herhangi bir zamanda ne olduğunu kolayca tahmin edebilmesi nedeniyle oturum tanımlayıcıları / anahtarları için kullanılamayacağı anlamına gelir.
İnşallah

36
@Inshallah: Sistemi gereksiz yere aşırıya kaçıyorsunuz. Ben tohum olarak zaman kullandığını kabul ederken, saldırgan gerçekten istediğini elde etmek için aşağıdaki verilere erişim olması gerekir 1. Tam milisaniye, kod tohumlandı zaman 2. Şimdiye kadar gerçekleşen çağrı sayısı 3. Kendi çağrısı için atomisite (böylece şimdiye kadar yapılan çağrıların sayısı aynıdır) Eğer saldırgan bu üç şeyin hepsine sahipse, o zaman elinizde çok daha büyük bir sorun var ...
Ajeet Ganga

3
compile 'commons-lang:commons-lang:2.6'
dereceye

4
@Ajeet bu doğru değil. Rastgele sayı üretecinin durumunu çıktısından türetebilirsiniz. Saldırgan rasgele API belirteçleri oluşturmak için birkaç bin çağrı oluşturabilirse, saldırgan gelecekteki tüm API belirteçlerini tahmin edebilir.
Thomas Grainger

3
@AjeetGanga Aşırı mühendislikle ilgisi yok. Oturum kimlikleri oluşturmak istiyorsanız, bir şifreleme sözde rasgele oluşturucusuna ihtiyacınız vardır. Zamanı tohum olarak kullanan her prng öngörülebilir ve öngörülememesi gereken veriler için çok güvensizdir. Sadece kullan SecureRandomve iyisin.
Patrick Favre

105

Bir satırda:

Long.toHexString(Double.doubleToLongBits(Math.random()));

http://mynotes.wordpress.com/2009/07/23/java-generating-random-string/


9
Ama sadece 6 harf :(
Moshe Revah

2
Bana da yardımcı oldu ama sadece onaltılık basamaklar :(
noquery

@Zippoxer, birkaç kez
katılabilirsin

7
OP örneği, AEYGF7K0DM1Xonaltılı olmayan bir örnek olarak aşağıdaki Dizeyi gösterdi . İnsanların alfanümerik değerleri onaltılı olarak ne sıklıkta hatalandırdıkları beni endişelendiriyor. Aynı şey değiller.
hfontanez

6
Bu, 0 ile 1 arasında Math.random()bir ürettiği için dize uzunluğunun verilmesi gerektiğinden çok daha az rastgeledir double, bu nedenle üs parçası çoğunlukla kullanılmaz. Kullanım random.nextLongrastgele için longyerine bu çirkin kesmek.
maaartinus

80

Bu, herhangi bir harici kütüphane olmadan kolayca elde edilebilir.

1. Şifreleme Sözde Rastgele Veri Üretimi

İlk önce kriptografik bir PRNG'ye ihtiyacınız var. Java buna sahiptir SecureRandomve genellikle makinedeki en iyi entropi kaynağını kullanır (örn. /dev/random). Daha fazlasını buradan okuyun.

SecureRandom rnd = new SecureRandom();
byte[] token = new byte[byteLength];
rnd.nextBytes(token);

Not: SecureRandom Java'da rasgele bayt oluşturmanın en yavaş, ancak en güvenli yoludur. Ancak, saniyede milyonlarca jeton oluşturmanız gerekmediği sürece, genellikle uygulamanız üzerinde gerçek bir etkisi olmadığından performansı dikkate almamanızı öneririm.

2. Olası Değerlerin Gerekli Alanı

Sonra belirtecinizin "ne kadar benzersiz" olması gerektiğine karar vermelisiniz. Entropi düşünmenin bütün ve tek noktası, sistemin kaba kuvvet saldırılarına karşı koyabildiğinden emin olmaktır: olası değerlerin alanı, herhangi bir saldırganın gülünç olmayan zamanda 1 değerlerinin ihmal edilebilir bir kısmını deneyebileceği kadar büyük olmalıdır . Rastgele gibi benzersiz tanımlayıcıların UUID122bit entropisi vardır (yani 2 ^ 122 = 5.3x10 ^ 36) - çarpışma şansı, milyarlarca çoğaltma şansı olan bir tane olması için 103 *, ... trilyon versiyonudur. 4 UUID oluşturulmalıdır 2 ". Tam olarak 16 bayta sığdığı ve son derece yeterli görüldüğü için 128 bit seçeceğiztemel olarak her, ancak en uç nokta için benzersiz olduğu için, kullanım örnekleri ve kopyaları düşünmek zorunda değilsiniz. İşte doğum günü sorununun basit analizini içeren basit bir entropi karşılaştırma tablosu .

jeton boyutlarının karşılaştırılması

Basit gereksinimler için 8 veya 12 bayt uzunluk yeterli olabilir, ancak 16 bayt ile "güvenli tarafta "sınız.

Ve temelde bu. Son şey, yazdırılabilir bir metin olarak gösterilebilmesi için kodlamayı düşünmektir (okuma, a String).

3. İkili Metin Kodlama

Tipik kodlamalar şunları içerir:

  • Base64her karakter 6 bit kodlar ve% 33 ek yük oluşturur. Neyse ki Java 8+ ve Android'de standart uygulamalar var . Eski Java ile çok sayıda üçüncü taraf kütüphanesinden herhangi birini kullanabilirsiniz . Jetonlarınızın URL güvenli olmasını istiyorsanız , RFC4648'in URL güvenli sürümünü kullanın (genellikle çoğu uygulama tarafından desteklenir). Dolgu ile 16 bayt kodlama örneği:XfJhfv3C0P6ag7y9VQxSbw==

  • Base32her karakter 5 bit kodlar ve% 40 ek yük oluşturur. Bu, büyük / küçük harfe duyarlı olmayan alfasayısal olarak kullanılır A-Zve 2-7alandan tasarruf sağlar. JDK'da standart bir uygulama yoktur . Dolgu olmadan 16 bayt kodlama örneği:WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16(onaltılık) her karakter bayt başına 2 karakter gerektiren 4 bit kodlar (yani 16 bayt 32 uzunluklu bir dize oluşturur). Bu nedenle altıgen daha verimli daha az yer kaplıyor Base32ama sadece kullanımlar beri çoğu durumda (URL) kullanılmak üzere güvenlidir 0-9ve Ahiç F. Örnek 16 bayt kodlayan 4fa3dd0f57cb3bf331441ed285b27735. Burada hex'e dönüştürmeyle ilgili bir SO tartışmasına bakın.

Base85 ve egzotik Base122 gibi ek kodlamalar daha iyi / daha kötü alan verimliliği ile mevcuttur. Kendi kodlama oluşturabilirsiniz (temel olarak bu iş parçacığında en çok cevap yapar) ama çok spesifik gereksinimleriniz yoksa buna karşı tavsiye ederim. Bkz Wikipedia makalesinde daha kodlama şemaları.

4. Özet ve Örnek

  • kullanım SecureRandom
  • En az 16 bayt (2 ^ 128) olası değer kullanın
  • Gereksinimlerinize göre kodlayın (genellikle hexveya base32alfa-sayısal olması gerekiyorsa)

Yapma

  • ... ev demleme kodlamanızı kullanın: aynı anda karakter oluşturan döngüler için garip yerine hangi standart kodlamayı kullandığınızı görürlerse, diğerleri için daha iyi korunabilir ve okunabilir.
  • ... UUID kullanın: rasgelelik konusunda hiçbir garantisi yoktur; 6 bit entropi harcıyorsunuz ve ayrıntılı dize gösterimi var

Örnek: Hex Token Generator

public static String generateRandomHexToken(int byteLength) {
    SecureRandom secureRandom = new SecureRandom();
    byte[] token = new byte[byteLength];
    secureRandom.nextBytes(token);
    return new BigInteger(1, token).toString(16); //hex encoding
}

//generateRandomHexToken(16) -> 2189df7475e96aa3982dbeab266497cd

Örnek: Base64 Token Generator (Url Kasası)

public static String generateRandomBase64Token(int byteLength) {
    SecureRandom secureRandom = new SecureRandom();
    byte[] token = new byte[byteLength];
    secureRandom.nextBytes(token);
    return Base64.getUrlEncoder().withoutPadding().encodeToString(token); //base64 encoding
}

//generateRandomBase64Token(16) -> EEcCCAYuUcQk7IuzdaPzrg

Örnek: Java CLI Aracı

Kullanıma hazır bir kırpma aracı istiyorsanız zar kullanabilirsiniz: https://github.com/patrickfav/dice

Örnek: İlgili sorun - Mevcut Kimliklerinizi Koruyun

Kullanabileceğiniz bir kimliğiniz varsa (örneğin longkuruluşunuzda bir sentetik ), ancak dahili değeri yayınlamak istemiyorsanız , bu kitaplığı şifrelemek ve gizlemek için kullanabilirsiniz: https://github.com/patrickfav / id-maskesi

IdMask<Long> idMask = IdMasks.forLongIds(Config.builder(key).build());
String maskedId = idMask.mask(id);
//example: NPSBolhMyabUBdTyanrbqT8
long originalId = idMask.unmask(maskedId);

3
Bu cevap eksiksizdir ve herhangi bir bağımlılık eklemeden çalışır. Çıktıda olası eksi işaretlerinden kaçınmak istiyorsanız, negatif BigIntegerbir yapıcı parametresi: BigInteger(1, token)yerine BigInteger(token).
francoisr

Tanklar @francoisr ipucu için, kod örneğini düzenledim
Patrick Favre

import java.security.SecureRandom;ve import java.math.BigInteger;örneğin çalışması için gerekli, ama harika çalışıyor!
anothermh

İyi cevap ama / dev / random, entropinin çok düşük olması durumunda bloke etme noktasına kadar yavaş olmasının nedeni olan bir engelleme yöntemidir. Daha iyi ve engellemeyen yöntem / dev / urandom'dur. Bu <jre> /lib/security/java.security aracılığıyla yapılandırılabilir ve securerandom.source = file: file: / dev /./ urandom
Muzammil

@Muzammil Bkz. Tersesystems.com/blog/2015/12/17/… (ayrıca cevaba da bağlıdır) - new SecureRandom()kullanımlar/dev/urandom
Patrick Favre

42

kullanarak Doları basit gibi olmalıdır:

// "0123456789" + "ABCDE...Z"
String validCharacters = $('0', '9').join() + $('A', 'Z').join();

String randomString(int length) {
    return $(validCharacters).shuffle().slice(length).toString();
}

@Test
public void buildFiveRandomStrings() {
    for (int i : $(5)) {
        System.out.println(randomString(12));
    }
}

şöyle bir çıktı verir:

DKL1SBH9UJWC
JH7P0IT21EA5
5DTI72EO6SFU
HQUMJTEBNF7Y
1HCR6SKYWGT7

SecureRandom'u karışık olarak kullanmak mümkün mü?
iwein

34

İşte Java'da:

import static java.lang.Math.round;
import static java.lang.Math.random;
import static java.lang.Math.pow;
import static java.lang.Math.abs;
import static java.lang.Math.min;
import static org.apache.commons.lang.StringUtils.leftPad

public class RandomAlphaNum {
  public static String gen(int length) {
    StringBuffer sb = new StringBuffer();
    for (int i = length; i > 0; i -= 12) {
      int n = min(12, abs(i));
      sb.append(leftPad(Long.toString(round(random() * pow(36, n)), 36), n, '0'));
    }
    return sb.toString();
  }
}

İşte örnek bir çalışma:

scala> RandomAlphaNum.gen(42)
res3: java.lang.String = uja6snx21bswf9t89s00bxssu8g6qlu16ffzqaxxoy

4
Bu, güvensiz sekanslar, yani kolayca tahmin edilebilen sekanslar üretecektir .
Yuriy Nakonechnyy

8
Tüm bu çift istilalı rasgele int nesli, yavaş ve okunamayan tasarımla kırıldı. Random#nextIntVeya tuşunu kullanın nextLong. SecureRandomGerekirse geçin .
maaartinus

31

Şaşırtıcı kimse burada önermedi ama:

import java.util.UUID

UUID.randomUUID().toString();

Kolay.

Bunun yararı, UUID'lerin hoş ve uzun olması ve çarpışmanın neredeyse imkansız olduğu garantisidir.

Vikipedi'nin iyi bir açıklaması var:

“... önümüzdeki 100 yıl boyunca her saniyede 1 milyar UUID ürettikten sonra, sadece bir kopya oluşturma olasılığı yaklaşık% 50 olacaktır.”

http://en.wikipedia.org/wiki/Universally_unique_identifier#Random_UUID_probability_of_duplicates

İlk 4 bit sürüm tipidir ve varyant için 2'dir, böylece 122 bit rastgele elde edersiniz. Eğer Yani eğer istediğiniz UUID boyutunu azaltmak için ucundan kesecek sana. Tavsiye edilmez, ancak 500k kayıtlarınız için yeterince rasgele yükünüz var.


39
Birisi bunu önermiş, yaklaşık bir yıl önce.
erickson

31

Kısa ve kolay bir çözüm, ancak sadece küçük harf ve sayısal kullanır:

Random r = new java.util.Random ();
String s = Long.toString (r.nextLong () & Long.MAX_VALUE, 36);

Boyut 36 tabanına yaklaşık 12 basamaklıdır ve bu şekilde daha fazla geliştirilemez. Tabii ki birden fazla örnek ekleyebilirsiniz.


11
Unutmayın, sonucun önünde eksi işaretinin% 50 şansı vardır! Eksi işaretini istemiyorsanız r.nextLong () öğesini bir Math.abs () öğesine kaydırmak kullanılabilir: Long.toString(Math.abs(r.nextLong()), 36);
Ray Hulha

5
@RayHulha: Eksi işaretini istemiyorsanız, kesmelisiniz, çünkü şaşırtıcı bir şekilde Math.abs, Long.MIN_VALUE için negatif bir değer döndürür.
kullanıcı bilinmiyor

Negatif dönen Math.abs ilginç. Daha fazla burada: bmaurer.blogspot.co.nz/2006/10/…
Phil

1
Sorun abs, en önemli biti temizlemek için bir bitsel operatör kullanılarak çözülmüştür. Bu, tüm değerler için çalışacaktır.
Radiodef

1
@Radiodef Temelde @userunkown bunu söyledi. Sanırım sen de yapabilirsin << 1 >>> 1.
shmosel

15

Java 8'de bir alternatif:

static final Random random = new Random(); // Or SecureRandom
static final int startChar = (int) '!';
static final int endChar = (int) '~';

static String randomString(final int maxLength) {
  final int length = random.nextInt(maxLength + 1);
  return random.ints(length, startChar, endChar + 1)
        .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
        .toString();
}

3
Harika - ama kesinlikle alfasayısal olarak tutmak istiyorsanız (0-9, az, AZ) buraya bakın rationaljava.com/2015/06/…
Dan

12

UUID'lerin kullanımı güvenli değildir, çünkü UUID'nin bazı bölümleri rastgele değildir. @Erickson prosedürü çok temizdir, ancak aynı uzunlukta dizeler oluşturmaz. Aşağıdaki snippet yeterli olmalıdır:

/*
 * The random generator used by this class to create random keys.
 * In a holder class to defer initialization until needed.
 */
private static class RandomHolder {
    static final Random random = new SecureRandom();
    public static String randomKey(int length) {
        return String.format("%"+length+"s", new BigInteger(length*5/*base 32,2^5*/, random)
            .toString(32)).replace('\u0020', '0');
    }
}

Neden seçiyorsun length*5. Rastgele uzunluk 1'in basit bir dizesini, yani bir rastgele karakteri ele alalım. 0-9 arasındaki tüm rakamları ve az karakterlerini içeren rastgele bir karakter elde etmek için, her karakterden birini almak için 0 ile 35 arasında rastgele bir sayıya ihtiyacımız olacaktır. BigIntegeraralık içinde eşit olarak dağıtılmış rastgele bir sayı üretmek için bir yapıcı sağlar 0 to (2^numBits - 1). Ne yazık ki 35, 2 ^ numBits - 1 ile alınabilecek bir sayı değildir. Bu nedenle iki seçeneğimiz var: Ya 2^5-1=31ya ya ile gidin 2^6-1=63. Eğer 2^6seçersek çok fazla "gereksiz" / "daha uzun" sayı alırdık. Bu nedenle 2^54 karakter (wz) kaybetsek bile daha iyi bir seçenektir. Şimdi belirli bir uzunlukta bir dize oluşturmak için,2^(length*numBits)-1numara. Son sorun, belirli bir uzunluğa sahip bir dize istiyorsak, rastgele küçük bir sayı üretebilir, bu nedenle uzunluk karşılanmaz, bu nedenle dizeyi sıfırdan önce gereken uzunluğa doldurmamız gerekir.


5'i daha iyi açıklayabilir misiniz?
Julian Suarez

11
public static String generateSessionKey(int length){
String alphabet = 
        new String("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); //9
int n = alphabet.length(); //10

String result = new String(); 
Random r = new Random(); //11

for (int i=0; i<length; i++) //12
    result = result + alphabet.charAt(r.nextInt(n)); //13

return result;
}

10
import java.util.Random;

public class passGen{
    //Verison 1.0
    private static final String dCase = "abcdefghijklmnopqrstuvwxyz";
    private static final String uCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String sChar = "!@#$%^&*";
    private static final String intChar = "0123456789";
    private static Random r = new Random();
    private static String pass = "";

    public static void main (String[] args) {
        System.out.println ("Generating pass...");
        while (pass.length () != 16){
            int rPick = r.nextInt(4);
            if (rPick == 0){
                int spot = r.nextInt(25);
                pass += dCase.charAt(spot);
            } else if (rPick == 1) {
                int spot = r.nextInt (25);
                pass += uCase.charAt(spot);
            } else if (rPick == 2) {
                int spot = r.nextInt (7);
                pass += sChar.charAt(spot);
            } else if (rPick == 3){
                int spot = r.nextInt (9);
                pass += intChar.charAt (spot);
            }
        }
        System.out.println ("Generated Pass: " + pass);
    }
}

Yani bu sadece dize içine şifre eklemek ve ... evet iyi çalışır kontrol edin ... çok basit. Yazdım


Bazı küçük değişiklikler yapmama izin verdim. Neden + 0sık sık ekliyorsun ? Neden spot beyanını ve başlatmayı bölüyorsunuz? 0,1,2,3 yerine 1,2,3,4 endekslerinin avantajı nedir? En önemlisi: Rastgele bir değer aldınız ve eğer daha fazla rastgelelik kazanmadan her zaman uyuşmayan yeni bir değerin 4 katı ise. Ancak geri dönmekten çekinmeyin.
kullanıcı bilinmiyor

8

Ben rastgele bir hex kodlanmış dize üreten bu çözümü buldum. Sağlanan birim testi birincil kullanım durumuma dayanıyor gibi görünüyor. Bununla birlikte, sağlanan diğer cevaplardan biraz daha karmaşıktır.

/**
 * Generate a random hex encoded string token of the specified length
 *  
 * @param length
 * @return random hex string
 */
public static synchronized String generateUniqueToken(Integer length){ 
    byte random[] = new byte[length];
    Random randomGenerator = new Random();
    StringBuffer buffer = new StringBuffer();

    randomGenerator.nextBytes(random);

    for (int j = 0; j < random.length; j++) {
        byte b1 = (byte) ((random[j] & 0xf0) >> 4);
        byte b2 = (byte) (random[j] & 0x0f);
        if (b1 < 10)
            buffer.append((char) ('0' + b1));
        else
            buffer.append((char) ('A' + (b1 - 10)));
        if (b2 < 10)
            buffer.append((char) ('0' + b2));
        else
            buffer.append((char) ('A' + (b2 - 10)));
    }
    return (buffer.toString());
}

@Test
public void testGenerateUniqueToken(){
    Set set = new HashSet();
    String token = null;
    int size = 16;

    /* Seems like we should be able to generate 500K tokens 
     * without a duplicate 
     */
    for (int i=0; i<500000; i++){
        token = Utility.generateUniqueToken(size);

        if (token.length() != size * 2){
            fail("Incorrect length");
        } else if (set.contains(token)) {
            fail("Duplicate token generated");
        } else{
            set.add(token);
        }
    }
}

Tamamen olasılığa dayalı yinelenen jetonlar için başarısız olmanın adil olduğunu düşünmüyorum.
Thom Wiggers

8
  1. Dize karakterlerini gereksinimlerinize göre değiştirin.

  2. Dize değişmez. Burada StringBuilder.appenddize birleştirmesinden daha etkilidir.


public static String getRandomString(int length) {
       final String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_+";
       StringBuilder result = new StringBuilder();
       while(length > 0) {
           Random rand = new Random();
           result.append(characters.charAt(rand.nextInt(characters.length())));
           length--;
       }
       return result.toString();
    }

3
Bu, daha önce verilen düzinelerce cevabı hiçbir şey katmaz. Ve Randomdöngünün her yinelemesinde yeni bir örnek oluşturmak verimsizdir.
erickson

7
import java.util.Date;
import java.util.Random;

public class RandomGenerator {

  private static Random random = new Random((new Date()).getTime());

    public static String generateRandomString(int length) {
      char[] values = {'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','0','1','2','3',
               '4','5','6','7','8','9'};

      String out = "";

      for (int i=0;i<length;i++) {
          int idx=random.nextInt(values.length);
          out += values[idx];
      }
      return out;
    }
}

7
import java.util.*;
import javax.swing.*;
public class alphanumeric{
    public static void main(String args[]){
        String nval,lenval;
        int n,len;

        nval=JOptionPane.showInputDialog("Enter number of codes you require : ");
        n=Integer.parseInt(nval);

        lenval=JOptionPane.showInputDialog("Enter code length you require : ");
        len=Integer.parseInt(lenval);

        find(n,len);

    }
    public static void find(int n,int length) {
        String str1="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        StringBuilder sb=new StringBuilder(length);
        Random r = new Random();

        System.out.println("\n\t Unique codes are \n\n");
        for(int i=0;i<n;i++){
            for(int j=0;j<length;j++){
                sb.append(str1.charAt(r.nextInt(str1.length())));
            }
            System.out.println("  "+sb.toString());
            sb.delete(0,length);
        }
    }
}

7

"Basit" çözümle ilgili şu cevaplardan hiçbirini beğenme: S

Basit;), saf java, bir astar (entropi rastgele dize uzunluğu ve verilen karakter kümesine dayanır) için gider:

public String randomString(int length, String characterSet) {
    return IntStream.range(0, length).map(i -> new SecureRandom().nextInt(characterSet.length())).mapToObj(randomInt -> characterSet.substring(randomInt, randomInt + 1)).collect(Collectors.joining());
}

@Test
public void buildFiveRandomStrings() {
    for (int q = 0; q < 5; q++) {
        System.out.println(randomString(10, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"));//charachterSet can basically be anything
    }
}

veya (biraz daha okunabilir eski yol)

public String randomString(int length, String characterSet) {
    StringBuilder sb = new StringBuilder(); //consider using StringBuffer if needed
    for (int i = 0; i < length; i++) {
        int randomInt = new SecureRandom().nextInt(characterSet.length());
        sb.append(characterSet.substring(randomInt, randomInt + 1));
    }
    return sb.toString();
}

@Test
public void buildFiveRandomStrings() {
    for (int q = 0; q < 5; q++) {
        System.out.println(randomString(10, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")); //charachterSet can basically be anything
    }
}

Ancak öte yandan oldukça iyi bir entropisi olan UUID ile de gidebilirsiniz ( https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions ):

UUID.randomUUID().toString().replace("-", "")

Umarım yardımcı olur.


6

"Basit" den bahsediyorsunuz, ancak başkalarının daha katı güvenlik gereksinimlerini karşılayan bir şey araması durumunda jpwgen'e bakmak isteyebilirsiniz . jpwgen, Unix'te pwgen'den sonra modellenmiştir ve çok yapılandırılabilir.


Teşekkürler, düzelttim. Yani en azından kaynak var ve bağlantı geçerlidir. Olumsuz, bir süredir güncellenmiş gibi görünmüyor, ancak pwgen'in son zamanlarda güncellendiğini görüyorum.
michaelok

4

UUID sınıfını, 64 bit Rastgele veri almak için getLeastSignificantBits () mesajıyla birlikte kullanabilir ve ardından bir sayı 36 numarasına (yani 0-9, AZ'den oluşan bir dize) dönüştürebilirsiniz:

Long.toString(Math.abs( UUID.randomUUID().getLeastSignificantBits(), 36));

Bu, 13 karakter uzunluğunda bir Dize verir. Bir eksi işaretinin gizlice girmediğinden emin olmak için Math.abs () kullanıyoruz.


2
Neden dünyada rastgele bitler almak için UUID kullanıyorsunuz? Neden sadece kullanmıyorsunuz random.nextLong()? Hatta Double.doubleToLongBits(Math.random())?
erickson

4

Şifreniz zorunlu alfabetik özel karakterler içeriyorsa, aşağıdaki kodu kullanabilirsiniz:

private static final String NUMBERS = "0123456789";
private static final String UPPER_ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String LOWER_ALPHABETS = "abcdefghijklmnopqrstuvwxyz";
private static final String SPECIALCHARACTERS = "@#$%&*";
private static final int MINLENGTHOFPASSWORD = 8;

public static String getRandomPassword() {
    StringBuilder password = new StringBuilder();
    int j = 0;
    for (int i = 0; i < MINLENGTHOFPASSWORD; i++) {
        password.append(getRandomPasswordCharacters(j));
        j++;
        if (j == 3) {
            j = 0;
        }
    }
    return password.toString();
}

private static String getRandomPasswordCharacters(int pos) {
    Random randomNum = new Random();
    StringBuilder randomChar = new StringBuilder();
    switch (pos) {
        case 0:
            randomChar.append(NUMBERS.charAt(randomNum.nextInt(NUMBERS.length() - 1)));
            break;
        case 1:
            randomChar.append(UPPER_ALPHABETS.charAt(randomNum.nextInt(UPPER_ALPHABETS.length() - 1)));
            break;
        case 2:
            randomChar.append(SPECIALCHARACTERS.charAt(randomNum.nextInt(SPECIALCHARACTERS.length() - 1)));
            break;
        case 3:
            randomChar.append(LOWER_ALPHABETS.charAt(randomNum.nextInt(LOWER_ALPHABETS.length() - 1)));
            break;
    }
    return randomChar.toString();

}

4

İşte AbacusUtil'in bir satır kodu

String.valueOf(CharStream.random('0', 'z').filter(c -> N.isLetterOrDigit(c)).limit(12).toArray())

Rastgele, benzersiz olması gerektiği anlamına gelmez. kullanarak benzersiz dizeler elde etmek için:

N.uuid() // e.g.: "e812e749-cf4c-4959-8ee1-57829a69a80f". length is 36.
N.guid() // e.g.: "0678ce04e18945559ba82ddeccaabfcd". length is 32 without '-'

3

İşte bir Scala çözümü:

(for (i <- 0 until rnd.nextInt(64)) yield { 
  ('0' + rnd.nextInt(64)).asInstanceOf[Char] 
}) mkString("")


3
public static String randomSeriesForThreeCharacter() {
    Random r = new Random();
    String value="";
    char random_Char ;
    for(int i=0; i<10;i++)
    { 
        random_Char = (char) (48 + r.nextInt(74));
        value=value+random_char;
    }
    return value;
}

2
Bu dize birleştirmesi gereksiz yere verimsizdir. Ve çılgın girinti kodunuzu neredeyse okunamaz hale getiriyor. Bu Jamie'nin fikriyle aynı , ancak kötü bir şekilde yürütüldü.
erickson

3

Buradaki en küçük çözüm veya neredeyse en küçük çözümlerden biri olduğunu düşünüyorum:

 public String generateRandomString(int length) {
    String randomString = "";

    final char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890".toCharArray();
    final SecureRandom random = new SecureRandom();
    for (int i = 0; i < length; i++) {
        randomString = randomString + chars[random.nextInt(chars.length)];
    }

    return randomString;
}

Kod gayet iyi çalışıyor. Bu yöntemi kullanıyorsanız, 10'dan fazla karakter kullanmanızı öneririz. Çarpışma 5 karakter / 30362 yinelemede gerçekleşir. Bu 9 saniye sürdü.


3
public static String getRandomString(int length) {
        char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRST".toCharArray();

        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            char c = chars[random.nextInt(chars.length)];
            sb.append(c);
        }
        String randomStr = sb.toString();

        return randomStr;
    }

1
Gerçekten hoş! Ancak bunun lengthyerine chars.lengthfor döngüsü içinde olması gerekir : for (int i = 0; i < length; i++)
Yakma

2
public static String getRandomString(int length) 
{
   String randomStr = UUID.randomUUID().toString();
   while(randomStr.length() < length) {
       randomStr += UUID.randomUUID().toString();
   }
   return randomStr.substring(0, length);
}

3
Bu, Steve McLeod'un iki yıl önce verdiği cevapla hemen hemen aynı .
erickson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.