나는 찾고 있었다 의사 난수 영숫자 문자열을 생성하는 간단한 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-9
and 만 사용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);