[java] 임의의 영숫자 문자열을 생성하는 방법은 무엇입니까?

나는 찾고 있었다 의사 난수 영숫자 문자열을 생성하는 간단한 Java 알고리즘을 있습니다. 내 상황에서 그것은 500K+세대를 초월하여 “아마도”독창적 인 고유 세션 / 키 식별자로 사용됩니다 (필요한 것은 훨씬 더 복잡한 것을 요구하지 않습니다).

이상적으로는 고유 요구 사항에 따라 길이를 지정할 수 있습니다. 예를 들어 길이가 12 인 생성 된 문자열은 다음과 같습니다 "AEYGF7K0DM1X".



답변

연산

임의의 문자열을 생성하려면 문자열이 원하는 길이에 도달 할 때까지 허용되는 기호 세트에서 임의로 그려진 문자를 연결하십시오.

이행

다음은 임의 식별자를 생성하기위한 매우 간단하고 유연한 코드입니다. 중요한 응용 참고 사항에 대해서는 다음 정보를 읽으십시오 .

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에 포함되거나 직접 입력되면 인코딩 문제가 발생할 수 있습니다.

세션 식별자에 대한 기본 난수 또는 엔트로피 소스는 암호화를 위해 설계된 난수 생성기에서 가져와야합니다. 그러나 이러한 생성기의 초기화는 때때로 계산 비용이 많이 들거나 느릴 수 있으므로 가능하면 재사용하기 위해 노력해야합니다.

객체 식별자로 사용

모든 응용 프로그램에 보안이 필요한 것은 아닙니다. 무작위 할당은 여러 엔티티가 조정 또는 파티셔닝없이 공유 공간에서 식별자를 생성하는 효율적인 방법 일 수 있습니다. 특히 클러스터 또는 분산 환경에서 조정이 느려질 수 있으며 공간을 분할하면 엔터티가 너무 작거나 큰 공유로 끝날 때 문제가 발생합니다.

대부분의 웹 응용 프로그램에서와 같이 공격자가이를보고 조작 할 수있는 경우 예측할 수없는 조치를 취하지 않고 생성 된 식별자는 다른 방법으로 보호해야합니다. 액세스 권한없이 공격자가 식별자를 추측 할 수있는 개체를 보호하는 별도의 권한 부여 시스템이 있어야합니다.

예상되는 총 식별자 수를 고려할 때 충돌을 일으키지 않을 정도로 긴 식별자를 사용하도록주의를 기울여야합니다. 이것을 “생일 역설”이라고합니다. 충돌 확률 p 는 대략 n 2 / (2q x )이며, 여기서 n 은 실제로 생성 된 식별자의 수, q 는 알파벳의 고유 한 기호의 수, x 는 식별자의 길이입니다. 2-50 이하 의 매우 작은 숫자 여야합니다 .

이 작업을 수행하면 500k 15 자 식별자 간의 충돌 가능성이 약 2‑52 임을 알 수 있습니다. 이므로 우주 광선 등에서 감지되지 않은 오류보다 가능성이 적습니다.

UUID와 비교

사양에 따라 UUID 는 예측할 수 없도록 설계 되지 않았으므로 세션 식별자로 사용 해서는 안됩니다 .

표준 형식의 UUID는 많은 공간을 차지합니다. 122 비트 엔트로피의 경우 36 자입니다. “임의의”UUID의 모든 비트가 무작위로 선택되는 것은 아닙니다. 무작위로 선택한 영숫자 문자열은 21 자만으로 더 많은 엔트로피를 압축합니다.

UUID는 융통성이 없습니다. 그것들은 표준화 된 구조와 레이아웃을 가지고 있습니다. 이것이 그들의 주요 미덕이며 주요 약점입니다. 외부 당사자와 공동 작업 할 때 UUID에서 제공하는 표준화가 도움이 될 수 있습니다. 순전히 내부 사용의 경우 비효율적 일 수 있습니다.


답변

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


답변

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


답변

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더 이상 사용되지 않습니다.


답변

이를 위해 Apache 라이브러리를 사용할 수 있습니다. RandomStringUtils

RandomStringUtils.randomAlphanumeric(20).toUpperCase();


답변

한 줄로 :

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

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


답변

외부 라이브러리 없이도 쉽게 달성 할 수 있습니다.

1. 암호화 의사 랜덤 데이터 생성

먼저 암호화 PRNG가 필요합니다. Java는이를 SecureRandom위해 있으며 일반적으로 머신에서 최상의 엔트로피 소스를 사용합니다 (예 🙂 /dev/random. 자세한 내용은 여기를 참조하십시오.

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

참고 : SecureRandom 임의의 바이트를 생성하는 Java에서 가장 느리지 만 가장 안전한 방법입니다. 그러나 초당 수백만 개의 토큰을 생성하지 않는 한 일반적으로 응용 프로그램에 실제로 영향을 미치지 않으므로 성능을 고려하지 않는 것이 좋습니다.

2. 가능한 가치의 공간

다음으로, 토큰의 “유일성”을 결정해야합니다. 엔트로피를 고려하는 전체적이고 유일한 요점은 시스템이 무차별 대입 공격에 저항 할 수 있는지 확인하는 것입니다. 가능한 값의 공간이 너무 커야 만 공격자가 무해한 시간에 무시할만한 비율의 값만 시도 할 수 있습니다 1 . 랜덤과 같은 고유 식별자 UUID는 122 비트의 엔트로피 (즉, 2 ^ 122 = 5.3×10 ^ 36)를 갖습니다. 충돌 가능성은 “* (…)입니다. 10 억 번의 중복 가능성이있는 103 억 개의 버전이 있습니다. 4 개의 UUID가 2 ” 로 생성되어야합니다 . 정확히 16 바이트에 맞고 충분히 높은 것으로 간주되므로 128 비트를 선택합니다.기본적으로 모든 경우에 고유하지만 가장 극단적 인 사용 사례이므로 중복에 대해 생각할 필요가 없습니다. 생일 문제 의 간단한 분석을 포함하여 엔트로피의 간단한 비교표는 다음과 같습니다 .

토큰 크기 비교

간단한 요구 사항의 경우 8 바이트 또는 12 바이트 길이이면 충분하지만 16 바이트이면 “안전한 쪽”에 있습니다.

그리고 그것은 기본적으로입니다. 마지막으로 인코딩에 대해 생각하여 인쇄 가능한 텍스트 (읽기, a String) 로 표시 할 수 있습니다 .

3. 바이너리에서 텍스트 인코딩

일반적인 인코딩에는 다음이 포함됩니다.

  • Base64모든 문자는 6 비트를 인코딩하여 33 %의 오버 헤드를 생성합니다. 다행히도 Java 8+Android 에는 표준 구현이 있습니다 . 오래된 Java를 사용하면 수많은 타사 라이브러리를 사용할 수 있습니다 . 토큰을 URL 안전 상태로 유지하려면 URL 안전 버전의 RFC4648 (일반적으로 대부분의 구현에서 지원됨)을 사용하십시오. 패딩으로 16 바이트를 인코딩하는 예 :XfJhfv3C0P6ag7y9VQxSbw==

  • Base32모든 문자는 5 비트를 인코딩하여 40 %의 오버 헤드를 생성합니다. 이것은 대소 문자를 구분하지 않고 영숫자 인 공간을 효율적으로 사용 A-Z하고 2-7효율적으로 만듭니다. JDK 에는 표준 구현 이 없습니다 . 패딩없이 16 바이트를 인코딩하는 예 :WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16(16 진수) 모든 문자는 바이트 당 2 문자를 요구하는 4 비트를 인코딩합니다 (즉, 16 바이트는 길이가 32 인 문자열을 만듭니다). 따라서 16 진은 공간 효율적이지는 Base32않지만 0-9and 만 사용 A하기 때문에 대부분의 경우 URL을 사용하는 것이 안전 합니다 F. 16 바이트 인코딩 예 : 4fa3dd0f57cb3bf331441ed285b27735. 16 진수로 변환하는 것에 대한 SO 토론을 참조하십시오.

같은 추가적인 인코딩 Base85 와 이국적인 Base122는 더 좋은 / 나쁜 공간 효율성이 존재한다. 자신의 인코딩을 만들 수 있지만 (기본적 으로이 스레드에서 대부분의 답변이 수행함) 매우 구체적인 요구 사항이 없으면 권장하지 않습니다. 참조 위키 피 디아 문서에서 자세한 인코딩 방식을.

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

예 : Base64 토큰 생성기 (Url Safe)

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- 마스크

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