java - 表示 - randomstringutils 非推奨




ランダムな英数字の文字列を生成するには? (20)

アルゴリズム

ランダムな文字列を生成するには、文字列が希望の長さになるまで、受け入れ可能なシンボルのセットからランダムに描画された文字を連結します。

実装

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

import java.security.SecureRandom;
import java.util.Locale;
import java.util.Objects;
import java.util.Random;

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によって提供される標準化が役立ちます。 純粋に内部使用のために、それらは非効率的であり得る。

私は疑似乱数の英数字の文字列を生成するための単純な Javaアルゴリズムを探していました。 私の状況では、500K +世代以上のユニークなセッション/キー識別子として使用されます(私のニーズは、もっと洗練されたものを必要としません)。 理想的には、自分のユニークさの必要性に応じて長さを指定することができます。 例えば、長さ12の生成された文字列は、 "AEYGF7K0DM1X"ように見えるかもしれません。


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

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


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

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は非推奨です。


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

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と文字azを含むランダムな文字を取得するには、各文字の1つを取得するために0〜35の乱数が必要です。 BigIntegerは、 0 to (2^numBits - 1)の範囲で一様に分布する乱数を生成するコンストラクタを提供します。 残念なことに35は2 ^ numBits - 1で受信できる数字ではありません。したがって2つのオプションがあります: 2^5-1=31または2^6-1=63いずれかになります。 私たちが2^6を選択すると、 "不必要な" / "より長い"数が多くなります。 したがって、たとえ4文字(wz)が緩んだとしても、 2^5がより良い選択肢です。 特定の長さの文字列を生成するには、単純に2^(length*numBits)-1数字を使用できます。 最後の問題は、特定の長さの文字列が必要な場合、randomは小さな数値を生成する可能性があるため、長さが満たされていないため、文字列を必要な長さの前にゼロで埋めなければなりません。


Dollarを使用して簡単にする必要があります:

// "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

ここは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

ここはScalaのソリューションです:

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

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

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

まず、暗号化PRNGが必要です。 JavaはSecureRandomを持っているため、通常はマシン上で最高のエントロピーソースを使用します(例: /dev/random )。 詳細はこちらをご覧ください。

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

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

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

次に、あなたのトークンがどのくらいユニークである必要があるかを決めなければなりません。 エントロピーを考慮する唯一の点は、システムがブルートフォース攻撃に耐えることができるかどうかを確認することです1つの可能な値のスペースは非常に大きくなければならないため、攻撃者は非懐疑的な時間に値のほんのわずかな部分しか試すことができません。 ランダムUUIDようなユニークな識別子は122bitのエントロピー(すなわち2 ^ 122 = 5.3x10 ^ 36)を持っています - 衝突の可能性は、10億回の複製の可能性、103兆バージョン4のUUIDは2 "生成する必要があります。 私たちは128バイトを選択します.16バイトに正確に収まるため、基本的にすべての、しかし最も極端なユースケースでユニークであると思われ、重複について考える必要はありません。 ここに、 誕生日問題の簡単な分析を含むエントロピーの簡単な比較表があります

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

それは基本的にそれです。 最後に、エンコーディングについて考えて、印刷可能なテキスト(読み込み、 String )として表現できるようにします。

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

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

  • Base64すべての文字は6ビットをエンコードし、33%のオーバーヘッドを生成します。 残念ながら、JDKには標準実装がありません( Android 7およびJava 8以降には7以下があります )。 しかし、これを追加するライブラリが数多く存在します。 欠点は、標準のBase64が安全でないことです。 urlや追加のエンコーディング( URLエンコーディングなど )を必要とするほとんどのファイルシステムではファイル名として使用されURLセーフバージョンのBase64が使用されます。 パディング付き16バイトのエンコード例: XfJhfv3C0P6ag7y9VQxSbw==

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

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

Base85やエキゾチックなBase122ような追加のエンコーディングは、より良い/悪い空間効率で存在します。 あなたは独自のエンコーディングを作成することができますが(このスレッドではほとんどの答えがあります)、非常に具体的な要件がない場合は、それに対して助言します。 Wikipediaの記事で、より多くのエンコーディングスキームを参照してください

4.要約と例

  • SecureRandom使用する
  • 可能な値の少なくとも16バイト(2 ^ 128)を使用する
  • あなたの要件に応じてエンコードします(通常はhexまたはbase32する必要があります)

しないでください

  • ...あなたの家庭用エンコーディングを使用してください: 一度に文字を作成する奇妙なforループではなく、あなたがどのような標準エンコーディングを使用するかを見れば、
  • ... 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

例:ツール

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


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

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

これにより、最大13文字の文字列が生成されます。私たちはMath.abs()を使用して、息を呑むようなマイナス記号がないことを確認します。


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

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

}

ベストランダムストリングジェネレーターメソッド

public class RandomStringGenerator{

    private static int randomStringLength = 25 ;
    private static boolean allowSpecialCharacters = true ;
    private static String specialCharacters = "[email protected]$%*-_+:";
    private static boolean allowDuplicates = false ;

    private static boolean isAlphanum = false;
    private static boolean isNumeric = false;
    private static boolean isAlpha = false;
    private static final String alphabet = "abcdefghijklmnopqrstuvwxyz";
    private static boolean mixCase = false;
    private static final String capAlpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String num = "0123456789";

    public static String getRandomString() {
        String returnVal = "";
        int specialCharactersCount = 0;
        int maxspecialCharacters = randomStringLength/4;

        try {
            StringBuffer values = buildList();
            for (int inx = 0; inx < randomStringLength; inx++) {
                int selChar = (int) (Math.random() * (values.length() - 1));
                if (allowSpecialCharacters)
                {
                    if (specialCharacters.indexOf("" + values.charAt(selChar)) > -1)
                    {
                        specialCharactersCount ++;
                        if (specialCharactersCount > maxspecialCharacters)
                        {
                            while (specialCharacters.indexOf("" + values.charAt(selChar)) != -1)
                            {
                                selChar = (int) (Math.random() * (values.length() - 1));
                            }
                        }
                    }
                }
                returnVal += values.charAt(selChar);
                if (!allowDuplicates) {
                    values.deleteCharAt(selChar);
                }
            }
        } catch (Exception e) {
            returnVal = "Error While Processing Values";
        }
        return returnVal;
    }

    private static StringBuffer buildList() {
        StringBuffer list = new StringBuffer(0);
        if (isNumeric || isAlphanum) {
            list.append(num);
        }
        if (isAlpha || isAlphanum) {
            list.append(alphabet);
            if (mixCase) {
                list.append(capAlpha);
            }
        }
        if (allowSpecialCharacters)
        {
            list.append(specialCharacters);
        }
        int currLen = list.length();
        String returnVal = "";
        for (int inx = 0; inx < currLen; inx++) {
            int selChar = (int) (Math.random() * (list.length() - 1));
            returnVal += list.charAt(selChar);
            list.deleteCharAt(selChar);
        }
        list = new StringBuffer(returnVal);
        return list;
    }   

}

多分これは役に立つ

package password.generater;

import java.util.Random;

/**
 *
 * @author dell
 */
public class PasswordGenerater {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        int length= 11;
        System.out.println(generatePswd(length));

        // TODO code application logic here
    }
    static char[] generatePswd(int len){
        System.out.println("Your Password ");
        String charsCaps="ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 
        String Chars="abcdefghijklmnopqrstuvwxyz";
        String nums="0123456789";
        String symbols="[email protected]#$%^&*()_+-=.,/';:?><~*/-+";
        String passSymbols=charsCaps + Chars + nums +symbols;
        Random rnd=new Random();
        char[] password=new char[len];

        for(int i=0; i<len;i++){
            password[i]=passSymbols.charAt(rnd.nextInt(passSymbols.length()));
        }
      return password;

    }
}

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

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

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


あなたは「シンプル」と言いますが、他の誰かがより厳しいセキュリティ要件を満たすものを探している場合には、jpwgen見てみるといいかもしれません。jpwgenはUnixのpwgen後にモデル化されてpwgen、非常に設定可能です。


ここでは、AbacusUtil 1行のコードAbacusUtil

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

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

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 = "[email protected]#$%^&*";
    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);
    }
}

だから、これは文字列にパスワードを追加するだけです。そして、うまく動作します...非常に簡単です。 私はそれを書きました


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

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

        return randomStr;
    }

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






alphanumeric