ランダムな英数字の文字列を生成する方法は?


1741

私は、疑似ランダムな英数字の文字列を生成する単純な Javaアルゴリズムを探していました。私の状況では、これは500K+世代ごとに「可能性が高い」一意のセッション/キー識別子として使用されます(私のニーズは、実際にはもっと高度なものを必要としません)。

理想的には、独自性のニーズに応じて長さを指定できます。たとえば、生成された長さが12の文字列は、のようになり"AEYGF7K0DM1X"ます。


150
誕生日のパラドックスに注意してください。
pablosaraiva

58
誕生日のパラドックスを考慮していても、12文字の英数字(合計62文字)を使用する場合、パラドックスに到達するには340億を超える文字列が必要です。そして、誕生日のパラドックスは、とにかく衝突を保証するものではなく、それは50%以上の確率だと言っているだけです。
NullUserException 2012年

4
@NullUserException 50%の成功率(1回の試行あたり)は非常に高く、10回の試行でも成功率は0.999です。それと、24時間以内にA LOTを試すことができるという事実を踏まえると、少なくとも1つを推測するために340億の文字列は必要ありません。これが、一部のセッショントークンを本当に長くする必要がある理由です。
Pijusn 2015年

16
これらの3つのラインコードが..非常に多くの有用な私の推測であるLong.toHexString(Double.doubleToLongBits(Math.random())); UUID.randomUUID().toString(); RandomStringUtils.randomAlphanumeric(12);
Manindar

18
私はこれが誕生日のパラドックスでは「50%の確率で、」ある...古いですが、知っている@Pijusn ないで「試しごと」、それは340億個の文字列(この場合)のうち、時が存在する、という「50%のチャンスです少なくとも1組の重複」。あなたは1.6必要があると思います9月の試みあたり50%の確率があるようにするために、データベース内のエントリ- 1.6e21 - illionを。
Tin Wizard

回答:


1541

アルゴリズム

ランダムな文字列を生成するには、文字列が目的の長さに達するまで、受け入れ可能な記号のセットからランダムに描かれた文字を連結します。

実装

ランダムな識別子を生成するためのかなり単純で非常に柔軟なコードを次に示します。重要なアプリケーションノートについては、以下の情報をお読みください

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

}

使用例

8文字の識別子用の安全でないジェネレータを作成します。

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

セッション識別子の安全なジェネレータを作成します。

RandomString session = new RandomString();

印刷用の読みやすいコードでジェネレータを作成します。文字列は、使用する記号を少なくするために、完全な英数字の文字列よりも長くなっています。

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

セッション識別子として使用

一意である可能性が高いセッション識別子を生成することは十分ではなく、単純なカウンターを使用することもできます。予測可能な識別子が使用されている場合、攻撃者はセッションを乗っ取ります。

長さと安全性の間には緊張があります。識別子が短いほど、可能性が少なくなるため、推測が容易になります。ただし、識別子が長いほど、より多くのストレージと帯域幅を消費します。より大きな記号のセットは役立ちますが、識別子がURLに含まれているか、手動で再入力された場合、エンコードの問題が発生する可能性があります。

セッション識別子のランダム性、つまりエントロピーの根本的な原因は、暗号化用に設計された乱数ジェネレータからのものでなければなりません。ただし、これらのジェネレーターの初期化は、計算コストがかかる場合や遅い場合があるため、可能な場合は再利用するように努力する必要があります。

オブジェクト識別子として使用

すべてのアプリケーションにセキュリティが必要なわけではありません。ランダム割り当ては、複数のエンティティが調整やパーティション化を行わずに共有スペースで識別子を生成する効率的な方法です。特にクラスター化または分散環境では、調整が遅くなる可能性があり、エンティティーが小さすぎるまたは大きすぎる共有で終了する場合、スペースを分割すると問題が発生します。

ほとんどのWebアプリケーションで発生するように、攻撃者が識別子を表示および操作できる可能性がある場合、それらを予測不能にする対策を講じずに生成された識別子を他の手段で保護する必要があります。アクセス許可なしに攻撃者が識別子を推測できるオブジェクトを保護する別の認証システムが必要です。

予想される識別子の総数を考えれば、衝突を起こしそうにないほど長い識別子を使用するように注意する必要もあります。これは「誕生日のパラドックス」と呼ばれています。衝突の確率 pは約n 2 /(2q x)です。nは実際に生成された識別子の数、qはアルファベット内の個別の記号の数、xは識別子の長さです。これは、2 ‑50以下など、非常に小さい数である必要があります。

これを解決すると、500kの15文字の識別子が衝突する可能性は約2 ‑52であり、宇宙線などから検出されないエラーよりもおそらく少ない可能性があります。

UUIDとの比較

それらの仕様によれば、UUIDは予測不能になるように設計されておらず、セッション識別子として使用されるべきではありません

標準形式のUUIDは多くのスペースを必要とします。つまり、122ビットのエントロピーに対して36文字です。(「ランダム」UU​​IDのすべてのビットがランダムに選択されるわけではありません。)ランダムに選択された英数字の文字列は、21文字でより多くのエントロピーをパックします。

UUIDは柔軟ではありません。それらは標準化された構造とレイアウトを持っています。これが主な長所であり、主な弱点でもあります。外部のパーティとコラボレーションする場合、UUIDによって提供される標準化が役立つ場合があります。純粋に内部で使用する場合、非効率になる可能性があります。


6
スペースが必要な場合.replaceAll("\\d", " ");は、return new BigInteger(130, random).toString(32);行の最後に 追加して正規表現スワップを実行できます。すべての数字をスペースに置き換えます。私にとっては
うまくいき

4
@weisjohnそれは良い考えです。2番目の方法でも同様のことができますsymbols。代わりに数字を削除し、代わりにスペースを使用します。記号内のスペースの数を変更することで、平均的な「単語」の長さを制御できます(短い単語の場合、出現回数を増やす)。本当にオーバーザトップの偽のテキストソリューションの場合は、マルコフチェーンを使用できます。
エリクソン、2011年

4
これらの識別子は、特定のサイズのスペースからランダムに選択されます。長さは1文字です。固定長が必要な場合はSecureRandomrandom変数にインスタンスを割り当てて、2番目のソリューションを使用できます。
エリクソン2011

15
なぜ.toString(36)ではなく.toString(32)なのですか?
ejain

17
@ejain(32 = 2 ^ 5であるため)各文字は正確に5ビットを表し、130ビットは文字に均等に分割できます。
エリクソン

817

Javaはこれを直接行う方法を提供します。ダッシュが不要な場合は、簡単に取り除くことができます。使うだけuuid.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;
    }
}

出力:

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

33
このソリューションでは、16進文字を含むランダムな文字列のみが生成されることに注意してください。場合によってはこれで問題ありません。
デイブ

5
UUIDクラスは便利です。ただし、それらは私の回答で生成された識別子ほどコンパクトではありません。これは、たとえばURLで問題になる可能性があります。あなたのニーズに依存します。
エリクソン2011

6
@Ruggs-目標は英数字の文字列です。出力を任意の可能なバイトに拡張することはどのようにそれに適合しますか?
エリクソン、2011年

72
RFC4122によると、トークンとしてUUIDを使用することは悪い考えです。UUIDが推測されにくいと想定しないでください。たとえば、セキュリティ機能(単なる所有物がアクセスを許可する識別子)として使用しないでください。予測可能な乱数ソースは状況を悪化させます。ietf.org/rfc/rfc4122.txt
Somatik

34
UUID.randomUUID().toString().replaceAll("-", "");要求に応じて文字列を英数字にします。
Numid 2014年

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、ここでは指定された長さのランダム文字列を生成するための最も簡単なソリューション(Commons LangのRandomStringUtilsの使用を除く)。
Jonik

12
クラスのSecureRandom代わりに使用することを検討してくださいRandom。サーバー上でパスワードが生成されると、タイミング攻撃に対して脆弱になる可能性があります。
foens 2014年

8
私は小文字も追加します: AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";その他の許可された文字。
ACV

1
static Random rnd = new Random();メソッドの中に入れてみませんか?
マイクロ

4
@MicroR Random各メソッド呼び出しでオブジェクトを作成する十分な理由はありますか?私はそうは思いません。
カシオモリン2016

484

Apacheクラスを使用してよければ、org.apache.commons.text.RandomStringGenerator(commons-text)を使用できます。

例:

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以降、RandomStringUtils非推奨です。


22
上記Apache Commons Lang 3.3.1ライブラリのクラスを調べたjava.util.Randomところ、ランダムシーケンスを提供するためだけに使用されているため、安全でないシーケンスが生成されています
Yuriy Nakonechnyy 2014

16
確かRandomStringUtilsを使用しているとき、あなたがたSecureRandomを使用してください: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

使ってはいけません。これは安全でないシーケンスを作成します
Patrick Favre

110

これにはApacheライブラリを使用できます:RandomStringUtils

RandomStringUtils.randomAlphanumeric(20).toUpperCase();

18
@kamil、私はRandomStringUtilsのソースコードを見て、引数なしでインスタンス化されたjava.util.Randomのインスタンスを使用しています。java.util.Randomのドキュメントでは、シードが提供されていない場合、現在のシステム時刻が使用されると記載されています。これは、攻撃者が生成されたセッション識別子がいつであるかを簡単に予測できるため、セッション識別子/キーには使用できないことを意味します。
Inshallah 2012

36
@Inshallah:あなたは(不必要に)システムをオーバーエンジニアリングしています。時間をシードとして使用することに同意しますが、攻撃者は次のデータにアクセスして実際に必要なものを取得する必要があります1.コードがシードされた正確なミリ秒までの時間2.これまでに発生した呼び出しの数3.自分の通話の原子性(つまり、これまでの通話数と同じ数)攻撃者がこれら3つすべてを持っている場合、非常に大きな問題が発生しています...
Ajeet Ganga 2013

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

4
@Ajeetこれは真実ではありません。出力から乱数ジェネレータの状態を導出できます。攻撃者がランダムなAPIトークンを生成するために数千の呼び出しを生成できる場合、攻撃者は将来のすべてのAPIトークンを予測することができます。
トーマスグレインジャー、

3
@AjeetGangaオーバーエンジニアリングとは関係ありません。セッションIDを作成する場合は、暗号化疑似ランダムジェネレーターが必要です。時間をシードとして使用するすべてのprngは予測可能であり、予測不可能であるはずのデータに対して非常に安全ではありません。使うSecureRandomだけでいいです。
Patrick Favre 2017

105

1行で:

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

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


9
ただし、6文字のみ:(
Moshe Revah

2
それも私を助けましたが、16進数のみ:(
noquery

@Zippoxer、それを数回連結することができます=)
daniel.bavrin

7
OPの例では、AEYGF7K0DM1X16進数ではない例として次の文字列を示しました。人々が英数字を16進数と間違える頻度が心配です。彼らは同じものではありません。
hfontanez 2014年

6
これは、0と1の間をMath.random()生成するため、文字列長を指定する必要があるよりもランダム性がはるかに低いdoubleため、指数部はほとんど使用されません。この醜いハックの代わりにrandom.nextLongランダムlongに使用してください。
maaartinus 2015

80

これは、外部ライブラリがなくても簡単に実現できます。

1.暗号化擬似ランダムデータ生成

まず、暗号PRNGが必要です。JavaにSecureRandomはそれがあり、通常はマシン上で最高のエントロピーソースを使用します(たとえば/dev/random)。詳細はこちら。

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

注: SecureRandomはランダムなバイトを生成するJavaで最も低速ですが最も安全な方法です。ただし、ここではパフォーマンスを考慮しないことをお勧めします。毎秒数百万のトークンを生成する必要がない限り、通常、アプリケーションに実際の影響はありません。

2.可能な値の必要なスペース

次に、トークンを「どれだけユニーク」にする必要があるかを決定する必要があります。エントロピーを検討する全体的かつ唯一のポイントは、システムがブルートフォース攻撃に耐えることを確認することです。可能な値のスペースは非常に大きくなければならず、攻撃者は非ばかげた時間1で無視できる割合の値しか試行できません。ランダムなどの一意の識別子は、UUID122ビットのエントロピー(つまり、2 ^ 122 = 5.3x10 ^ 36)を持っています-衝突の可能性は、「*(...)」です。 4 UUIDを生成する必要があります2 "。128バイトを選択します。これは、16バイトに正確に適合、十分と見なされているためです。基本的にすべてで一意ですが、最も極端なユースケースであり、重複について考える必要はありません。これは誕生日問題の簡単な分析を含むエントロピーの簡単な比較表です。

トークンサイズの比較

単純な要件では、8バイトまたは12バイトの長さで十分な場合がありますが、16バイトの場合は「安全な側面」にあります。

そして、それは基本的にそれです。最後に、エンコーディングを検討して、印刷可能なテキスト(read、a String)として表現できるようにします。

3.バイナリからテキストへのエンコーディング

典型的なエンコーディングは次のとおりです。

  • Base64すべての文字が6ビットをエンコードし、33%のオーバーヘッドを作成します。幸い、Java 8以降Androidには標準の実装があります。古いJavaでは、多数のサードパーティライブラリを使用できます。トークンをURLセーフにする場合は、RFC4648のURLセーフバージョン(通常、ほとんどの実装でサポートされています)を使用します。パディングで16バイトをエンコードする例:XfJhfv3C0P6ag7y9VQxSbw==

  • Base32すべての文字が5ビットをエンコードして、40%のオーバーヘッドを作成します。これはA-Z2-7大文字と小文字を区別しない英数字を使用しながら、合理的にスペース効率を高めます。JDKには標準の実装はありません。パディングなしで16バイトをエンコードする例:WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16(16進数)すべての文字は4ビットをエンコードし、1バイトあたり2文字を必要とします(つまり、16バイトで長さ32の文字列が作成されます)。したがって、hexはスペース効率が劣りBase32ますが0-9、とAto のみを使用するため、ほとんどの場合(url)で安全に使用できますF。16バイトのエンコードの例:4fa3dd0f57cb3bf331441ed285b2773516進数への変換に関するSOの議論をここで参照してください。

以下のような追加のエンコーディングBase85とエキゾチックなBase122は、より良い/より悪いスペース効率に存在します。独自のエンコーディングを作成することもできます(これは基本的にこのスレッドのほとんどの回答で行われます)が、非常に具体的な要件がない場合は、お勧めしません。ウィキペディアの記事で、その他のエンコード方式を参照してください

4.まとめと例

  • 使用する SecureRandom
  • 少なくとも16バイト(2 ^ 128)の可能な値を使用してください
  • 要件に従ってエンコードします(通常、hexまたはbase32英数字にする必要がある場合)

しない

  • ...自作のエンコーディングを使用します。他のユーザーが一度に文字を作成する奇妙なループの代わりに使用する標準のエンコーディングを見つけた場合は、他の人がより保守しやすく、読みやすくなります。
  • ... UUIDを使用:ランダム性は保証されません。あなたは6ビットのエントロピーを無駄にしており、冗長な文字列表現を持っています

例:16進数トークンジェネレーター

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

例:Base64トークンジェネレーター(URLセーフ)

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

例:Java CLIツール

すぐに使用できるCLIツールが必要な場合は、ダイスを使用できます:https : //github.com/patrickfav/dice

例:関連する問題-現在のIDを保護する

使用できるID(longエンティティの合成など)を既に持っているが、内部値を公開したくない場合は、このライブラリを使用して暗号化し、難読化できます:https : //github.com/patrickfav / id-mask

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

3
この回答は完全であり、依存関係を追加することなく機能します。出力でマイナス記号が発生しないようにする場合は、の代わりにBigIntegerコンストラクタパラメータを使用して、負のを防ぐことができます。BigInteger(1, token)BigInteger(token)
francoisr 2017

ヒント用のTanks @francoisr、私はコード例を編集しました
Patrick Favre

import java.security.SecureRandom;およびimport java.math.BigInteger;サンプルを機能させるために必要ですが、うまく機能します!
anothermh

良い答えですが、/ dev / randomはブロッキング方法です。そのため、エントロピーが低すぎると、ブロッキングの段階まで遅くなります。より優れた非ブロッキング方式は/ dev / urandomです。これは<jre> /lib/security/java.securityを介して設定でき、securerandom.source = file:/ dev /./ urandomを設定できます
Muzammil

@Muzammil tersesystems.com/blog/2015/12/17/を参照してください(これも回答にリンクされています)- new SecureRandom()使用/dev/urandom
Patrick Favre

42

ドルの使用は次のように簡単です:

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

それはそのようなものを出力します:

DKL1SBH9UJWC
JH7P0IT21EA5
5DTI72EO6SFU
HQUMJTEBNF7Y
1HCR6SKYWGT7

SecureRandomをシャッフルで使用できますか?
iwein 2016年

34

ここではJavaです:

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

実行例は次のとおりです。

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

4
これは、安全でないシーケンス、つまり簡単に推測できるシーケンスを生成します。
Yuriy Nakonechnyy 2014

8
この二重に蔓延したランダムなintの生成はすべて、設計上問題があり、遅く、読み取り不可能です。Random#nextIntまたはを使用しnextLongます。SecureRandom必要に応じて切り替えます。
maaartinus 2015

31

ここで誰もそれを示唆していないが、

import java.util.UUID

UUID.randomUUID().toString();

簡単です。

これの利点は、UUIDが素晴らしく長く、衝突することがほぼ不可能であることが保証されていることです。

ウィキペディアにはそれについての良い説明があります:

「...次の100年間、毎秒10億個のUUIDを生成した後にのみ、複製が1つだけ作成される確率は約50%になります。」

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

最初の4ビットはバージョンタイプで、バリアントは2ビットなので、122ビットのランダムになります。したがって、必要に応じて、最後から切り捨ててUUIDのサイズを小さくすることができます。これは推奨されませんが、ランダム性の負荷がまだ十分にあり、500kレコードを簡単に処理できます。


39
誰かがあなたの約1年前にそれを提案しました
エリクソン2013

31

短くて簡単な解決策ですが、小文字と数値のみを使用します:

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

サイズは36を基数とする約12桁で、これ以上の改善はできません。もちろん、複数のインスタンスを追加できます。


11
結果の前にマイナス記号が表示される可能性が50%あることに注意してください。Math.absでラッピングr.nextLongは()(SO)は、マイナス記号を使用しない場合は、使用することができます: Long.toString(Math.abs(r.nextLong()), 36);
レイHulha

5
@RayHulha:驚くべきことに、Math.absはLong.MIN_VALUEに負の値を返すため、マイナス記号が不要な場合は、それを切り捨てる必要があります。
不明なユーザー

Math.absが負の値を返すのは興味深い。もっとここに:bmaurer.blogspot.co.nz/2006/10/...
フィル・

1
の問題absは、ビットごとの演算子を使用して最上位ビットをクリアすることで解決されます。これはすべての値で機能します。
Radiodef

1
@Radiodefそれは本質的に@userunkownが言ったことです。あなたもできると思います<< 1 >>> 1
shmosel

15

Java 8の代替は次のとおりです。

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
それは素晴らしいことだ-しかし、あなたは厳密に英数字にそれを維持したい場合は(0-9、AZ、AZ)は、ここを参照してくださいrationaljava.com/2015/06/...
ダン・

12

UUIDの一部はまったくランダムではないため、UUIDの使用は安全ではありません。@ericksonの手順は非常に簡潔ですが、同じ長さの文字列は作成されません。次のスニペットで十分です。

/*
 * 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');
    }
}

なぜ選びますlength*5。長さ1のランダム文字列、つまり1つのランダム文字の単純なケースを想定してみましょう。すべての数字0〜9と文字a〜zを含むランダムな文字を取得するには、各文字の1つを取得するために0〜35の乱数が必要です。BigInteger範囲に均一に分散された乱数を生成するコンストラクタを提供します0 to (2^numBits - 1)。残念ながら、35は2 ^ numBits-1で受信できる数ではありません。したがって、2つのオプションがあります。2^5-1=31またはのいずれかを使用します2^6-1=63。もし私たちが選ぶなら、私たちは2^6多くの「不要な」/「より長い」数を得るでしょう。したがって2^5、4文字(wz)を失うとしても、より良いオプションです。特定の長さの文字列を生成するには、単に2^(length*numBits)-1数。最後の問題、特定の長さの文字列が必要な場合、randomは小さな数を生成する可能性があるため、長さが満たされず、文字列を必要な長さの前にゼロを追加する必要があります。


5をもっとよく説明できますか?
ジュリアンスアレス

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

つまり、これは文字列にパスワードを追加するだけです。そうです、うまくいきます。非常に簡単です。私はそれを書きました


私は自分にいくつかのマイナーな変更を許可しました。なぜそれを+ 0頻繁に追加するのですか?スポット宣言と初期化を分割するのはなぜですか?0、1、2、3ではなく、インデックス1、2、3、4の利点は何ですか?最も重要なこと:ランダムな値を取り、if-elseと比較して新しい値の4倍を比較しました。ただし、ロールバックしてかまいません。
ユーザー不明

8

ランダムな16進エンコードされた文字列を生成するこのソリューションを見つけました。提供されている単体テストは、私の主要なユースケースに対応しているようです。ただし、他のいくつかの回答よりも少し複雑です。

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

純粋に確率に基づいたトークンの重複が失敗するのは公平ではないと私は思います。
Thom Wiggers

8
  1. 必要に応じて文字列文字を変更します。

  2. 文字列は不変です。ここでStringBuilder.appendは、文字列の連結よりも効率的です。


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
これは、以前に与えられなかった数十の回答がカバーしなかったことを何も追加しません。またRandom、ループの各反復で新しいインスタンスを作成することは非効率的です。
エリクソン2014

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

「単純な」解決策に関するこの回答のどれも本当に好きではありません:S

私は単純な;)、純粋なJava、1つのライナー(エントロピーはランダムな文字列の長さと指定された文字セットに基づいています)を使用します。

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

または(少し読みやすい古い方法)

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

しかし一方で、かなり良いエントロピーを持つhttps://en.wikipedia.org/wiki/Universally_unique_identifier#Collisionsの UUIDを使用することもできます

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

お役に立てば幸いです。


6

「シンプル」とおっしゃっていますが、より厳しいセキュリティ要件を満たすものを他の誰かが探している場合に備えて、jpwgenを調べてみてください。jpwgenはUnixのpwgenをモデルにしており、非常に構成可能です。


ありがとう、修正しました。したがって、少なくともソースがあり、リンクは有効です。マイナス面としては、しばらく更新されていないようですが、pwgenは最近更新されています。
マイケルク2017年

4

getLeastSignificantBits()メッセージでUUIDクラスを使用して64ビットのランダムデータを取得し、基数36の数値(つまり、0〜9、AZで構成される文字列)に変換できます。

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

これにより、最大13文字の文字列が生成されます。Math.abs()を使用して、マイナス記号がこっそり入っていないことを確認します。


2
なぜ世界でランダムなビットを取得するためにUUIDを使用するのですか?なぜ使用しないのrandom.nextLong()ですか?それともDouble.doubleToLongBits(Math.random())
エリクソン2013年

4

必須パスワードに数字のアルファベットの特殊文字が含まれている場合は、次のコードを使用できます。

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

AbacusUtilによる 1行のコードは次のとおりです

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

ランダムとは、それが一意でなければならないという意味ではありません。一意の文字列を取得するには、次を使用します:

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



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
その文字列連結は不必要に非効率的です。そして、クレイジーなインデントはコードをほとんど読めなくします。これはジェイミーのアイデアと同じですが、実行力が不十分です。
エリクソン2013年

3

これはここで最小のソリューション、またはほぼ最小のソリューションの1つだと思います。

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

コードは問題なく動作します。この方法を使用している場合は、10文字以上にすることをお勧めします。衝突は5文字/ 30362回の反復で発生します。これには9秒かかりました。


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
すごくいい!ただし、forループでは lengthなく、次のようにする必要がありますchars.lengthfor (int i = 0; i < length; i++)
焼却炉

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.