고유 한 임의 문자열 생성


97

예를 들어 MSDN 라이브러리에서 생성되는 것과 같은 임의의 고유 문자열을 생성하고 싶습니다. ( Error Object ). 't9zk6eay'와 같은 문자열이 생성되어야합니다.


1
이것을 string randoms = Guid.NewGuid().ToString().Replace("-", string.Empty).Replace("+", string.Empty).Substring(0, 4);더 여기
— shaijut

1
무언가가 완전히 독특하려면 시간, 위치 등과 같이 무작위가 아닌 무언가를 기반으로해야하므로 실제로 완전히 무작위 일 수는 없습니다. Guid는 무작위로 보일 수 있지만 실제로는 그렇지 않습니다. IMO의 유일한 희망은 모든 실제 목적을 위해 값이 고유 할 수 있도록 무작위적이고 복잡하게 만드는 것입니다 (즉, 충돌 가능성이 매우 낮습니다).
— bytedev dec.

답변:


84

Guid를 사용하는 것은 꽤 좋은 방법이지만 예제와 같은 것을 얻으려면 아마도 Base64 문자열로 변환하고 싶을 것입니다.

    Guid g = Guid.NewGuid();
    string GuidString = Convert.ToBase64String(g.ToByteArray());
    GuidString = GuidString.Replace("=","");
    GuidString = GuidString.Replace("+","");

예제에 좀 더 가까워 지도록 "="및 "+"를 제거합니다. 그렇지 않으면 문자열 끝에 "=="가 표시되고 중간에 "+"가 표시됩니다. 다음은 출력 문자열의 예입니다.

"OZVV5TpP4U6wJthaCORZEQ"


15
/ 교체를 고려해야합니다.
— Jason Kealey

20
Guid는 시퀀스를 추측 할 수 있으므로 안전한 임의의 문자열로 간주해서는 안됩니다. Guid는 무작위가 아닌 키 충돌을 피하기 위해 설계되었습니다. 스택 오버플로에 대한 Guid의 무작위성에 대한 좋은 토론이 있습니다.
— Daniel Bradley

무엇의 명확하고 간단한 설명을 위해 Convert.ToBase64String약, 여기 좀 봐 .
— jwaliszko 2014 년

2
guid를 base64로 변환하고 + 및 =를 대체하여 충돌 확률을 높일 수 있습니까?
— Milan Aggarwal 2015 년

5
@SimonEjsing new Guid()"해킹"(시계 또는 내부 Windows 데이터 구조 조작)없이 사용할 때 충돌이 발생하는 애플리케이션을 실제로 작성할 수 있다면 맥주에 초대하겠습니다 . 원하는만큼 코어, 스레드, 동기화 기본 요소 등을 자유롭게 사용하십시오.
— Lucero

175

2016 년 1 월 23 일 업데이트

이 답변이 유용하다고 생각되면 내가 게시 한 간단한 (~ 500 SLOC) 암호 생성 라이브러리에 관심이있을 수 있습니다 .

Install-Package MlkPwgen

그런 다음 아래 답변과 같이 임의의 문자열을 생성 할 수 있습니다.

var str = PasswordGenerator.Generate(length: 10, allowed: Sets.Alphanumerics);

라이브러리의 한 가지 장점은 코드가 더 잘 분해되어 문자열을 생성하는 것보다 더 안전한 임의성 을 사용할 수 있다는 것 입니다. 체크 아웃 프로젝트 사이트를 자세한 내용은.

원래 답변

아직 아무도 보안 코드를 제공하지 않았기 때문에 누군가 유용하다고 생각되면 다음을 게시합니다.

string RandomString(int length, string allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") {
    if (length < 0) throw new ArgumentOutOfRangeException("length", "length cannot be less than zero.");
    if (string.IsNullOrEmpty(allowedChars)) throw new ArgumentException("allowedChars may not be empty.");

    const int byteSize = 0x100;
    var allowedCharSet = new HashSet<char>(allowedChars).ToArray();
    if (byteSize < allowedCharSet.Length) throw new ArgumentException(String.Format("allowedChars may contain no more than {0} characters.", byteSize));

    // Guid.NewGuid and System.Random are not particularly random. By using a
    // cryptographically-secure random number generator, the caller is always
    // protected, regardless of use.
    using (var rng = System.Security.Cryptography.RandomNumberGenerator.Create()) {
        var result = new StringBuilder();
        var buf = new byte[128];
        while (result.Length < length) {
            rng.GetBytes(buf);
            for (var i = 0; i < buf.Length && result.Length < length; ++i) {
                // Divide the byte into allowedCharSet-sized groups. If the
                // random value falls into the last group and the last group is
                // too small to choose from the entire allowedCharSet, ignore
                // the value in order to avoid biasing the result.
                var outOfRangeStart = byteSize - (byteSize % allowedCharSet.Length);
                if (outOfRangeStart <= buf[i]) continue;
                result.Append(allowedCharSet[buf[i] % allowedCharSet.Length]);
            }
        }
        return result.ToString();
    }
}

.NET Core에서 코드를 작동시키는 방법을 알려준 Ahmad에게 감사드립니다.


@Keltex 솔루션이 meh에 대해 제대로 작동하지 않았습니다 (몇 번 사용 후 동일한 문자열을 반환했습니다). 이 솔루션은 완벽한 :) 작동
— JoanComasFdz

2
@LeeGrissom, 바이어 싱은 중요한 측면입니다. 예를 들어 알파벳이 255자를 포함하고 0-255 사이의 임의 값을 얻는다고 가정 해 보겠습니다. 링 버퍼에서 값 0과 255는 알파벳의 첫 번째 문자에 유리하게 결과를 왜곡하는 동일한 문자에 해당하며 덜 무작위입니다. 이 문제는 물론 응용 프로그램에 따라 다릅니다.
— Oskar Sjöberg 2015 년

4
누가 겨냥한 .netcore: 교체 var rng = new RNGCryptoServiceProvider()와 함께var rng = RandomNumberGenerator.Create()
— AMD

1
왜 'var outOfRangeStart = byteSize-(byteSize % allowedCharSet.Length);'를 계산합니까? 각 반복에 대해? '사용하기'전에 계산할 수 있습니다.
— mtkachenko 2011

1
@BartCalixto 고정. 감사!
— Michael Kropat

38

GUID는 임의의 숫자 가 아니라는 점에주의해야 합니다 . 완전히 무작위로 예상되는 것을 생성하기위한 기초로 사용해서는 안됩니다 ( http://en.wikipedia.org/wiki/Globally_Unique_Identifier 참조 ).

WinAPI GUID 생성기의 암호 분석은 V4 GUID 시퀀스가 ​​의사 랜덤이므로 초기 상태가 주어지면 UuidCreate 함수에서 반환되는 다음 250,000 개의 GUID를 예측할 수 있음을 보여줍니다. 이것이 GUID가 암호화에서, 예를 들어 임의의 키로 사용되어서는 안되는 이유입니다.

대신 C # Random 메서드를 사용하십시오. 다음과 같은 것 ( 코드는 여기에 있음 ) :

private string RandomString(int size)
{
  StringBuilder builder = new StringBuilder();
  Random random = new Random();
  char ch ;
  for(int i=0; i<size; i++)
  {
    ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))) ;
    builder.Append(ch);
  }
  return builder.ToString();
}

GUID는 고유 한 항목 (예 : 데이터베이스의 고유 한 파일 이름 또는 키) 을 원할 경우 괜찮지 만 무작위로 원하는 항목 (예 : 암호 또는 암호화 키) 에는 적합하지 않습니다 . 따라서 응용 프로그램에 따라 다릅니다.

편집 . Microsoft는 Random도 그다지 좋지 않다고 말합니다 ( http://msdn.microsoft.com/en-us/library/system.random(VS.71).aspx ) :

예를 들어 임의 암호를 만드는 데 적합한 암호화 보안 난수를 생성하려면 System.Security.Cryptography.RNGCryptoServiceProvider와 같은 System.Security.Cryptography.RandomNumberGenerator에서 파생 된 클래스를 사용합니다.


5
C # 랜덤 클래스는 특정 시드 번호에서 시작하는 클래식 랜덤 생성기이기 때문에 "무작위"가 아니며 모든 암호화 코드에 적합하지 않습니다. 동일한 시드는 또한 반환 된 동일한 일련의 숫자를 반환합니다. GUID 접근 방식은 이미 여기에서 훨씬 낫습니다 ( "무작위"가 아니라 "고유").
— Lucero

3
@Lucero : 맞아요. Microsoft는 "예를 들어 임의 암호를 만드는 데 적합한 암호화 보안 난수를 생성하려면 System.Security.Cryptography.RNGCryptoServiceProvider와 같은 System.Security.Cryptography.RandomNumberGenerator에서 파생 된 클래스를 사용하십시오."라고 권장합니다.
— Keltex 2009

글쎄, 질문은 이미 그가 (의사) 임의의 고유 문자열을 원하므로 암호화 요구 사항이 없거나 특정 무작위 배포를 따를 필요가 없다고 이미 명시했습니다. 따라서 GUID는 아마도 가장 쉬운 방법 일 것입니다.
— Joey

1
"초기 상태가 주어지면 다음 250,000 개의 GUID를 예측할 수 있습니다"라는 진술은 본질적으로 모든 PRNG에 대한 진정한 진술처럼 보입니다. 또한 안전하지는 않지만 생성에 많은 가치가 있는지는 잘 모르겠습니다. 진정한 임의의 URL, 그게 OP의 목적이라면. ;)
— ojrac

1
(어쨌든 +1-PRNG 교육이 중요합니다.)
— ojrac

13

@Michael Kropats 솔루션을 단순화하고 LINQ-esque 버전을 만들었습니다.

string RandomString(int length, string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
{       
    var outOfRange = byte.MaxValue + 1 - (byte.MaxValue + 1) % alphabet.Length;

    return string.Concat(
        Enumerable
            .Repeat(0, int.MaxValue)
            .Select(e => RandomByte())
            .Where(randomByte => randomByte < outOfRange)
            .Take(length)
            .Select(randomByte => alphabet[randomByte % alphabet.Length])
    );
}

byte RandomByte()
{
    using (var randomizationProvider = new RNGCryptoServiceProvider())
    {
        var randomBytes = new byte[1];
        randomizationProvider.GetBytes(randomBytes);
        return randomBytes.Single();
    }   
}

11

나는 그들이 실제로 무작위라고 생각하지 않지만 내 추측은 그것들이 해시라는 것입니다.

임의의 식별자가 필요할 때마다 일반적으로 GUID를 사용하여 "네이 키드"표현으로 변환합니다.

Guid.NewGuid().ToString("n");

@Keltex가 지적했듯이 WinAPI GUID 생성기의 암호 분석은 V4 GUID의 시퀀스가 ​​의사 랜덤이기 때문에 초기 상태가 주어지면 UuidCreate 함수에서 반환되는 다음 250,000 개의 GUID를 예측할 수 있음을 보여줍니다.
— JoanComasFdz

4

Guid와 Time을 조합 해보십시오.

 var randomNumber = Convert.ToBase64String(Guid.NewGuid().ToByteArray()) + DateTime.Now.Ticks;
     randomNumber = System.Text.RegularExpressions.Regex.Replace(randomNumber, "[^0-9a-zA-Z]+", "");

3

CrytpoGraphic 솔루션이없는 이유에 놀랐습니다. GUID는 고유하지만 암호화로 안전하지 않습니다 . 이 Dotnet Fiddle을 참조하십시오.

var bytes = new byte[40]; // byte size
using (var crypto = new RNGCryptoServiceProvider())
  crypto.GetBytes(bytes);

var base64 = Convert.ToBase64String(bytes);
Console.WriteLine(base64);

Guid를 앞에 추가하려는 경우 :

var result = Guid.NewGuid().ToString("N") + base64;
Console.WriteLine(result);

더 깨끗한 영숫자 문자열 :

result = Regex.Replace(result,"[^A-Za-z0-9]","");
Console.WriteLine(result);

1

VB.net의 Michael Kropats 솔루션

Private Function RandomString(ByVal length As Integer, Optional ByVal allowedChars As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") As String
    If length < 0 Then Throw New ArgumentOutOfRangeException("length", "length cannot be less than zero.")
    If String.IsNullOrEmpty(allowedChars) Then Throw New ArgumentException("allowedChars may not be empty.")


    Dim byteSize As Integer = 256
    Dim hash As HashSet(Of Char) = New HashSet(Of Char)(allowedChars)
    'Dim hash As HashSet(Of String) = New HashSet(Of String)(allowedChars)
    Dim allowedCharSet() = hash.ToArray

    If byteSize < allowedCharSet.Length Then Throw New ArgumentException(String.Format("allowedChars may contain no more than {0} characters.", byteSize))


    ' Guid.NewGuid and System.Random are not particularly random. By using a
    ' cryptographically-secure random number generator, the caller is always
    ' protected, regardless of use.
    Dim rng = New System.Security.Cryptography.RNGCryptoServiceProvider()
    Dim result = New System.Text.StringBuilder()
    Dim buf = New Byte(128) {}
    While result.Length < length
        rng.GetBytes(buf)
        Dim i
        For i = 0 To buf.Length - 1 Step +1
            If result.Length >= length Then Exit For
            ' Divide the byte into allowedCharSet-sized groups. If the
            ' random value falls into the last group and the last group is
            ' too small to choose from the entire allowedCharSet, ignore
            ' the value in order to avoid biasing the result.
            Dim outOfRangeStart = byteSize - (byteSize Mod allowedCharSet.Length)
            If outOfRangeStart <= buf(i) Then
                Continue For
            End If
            result.Append(allowedCharSet(buf(i) Mod allowedCharSet.Length))
        Next
    End While
    Return result.ToString()
End Function

1

이것은 나를 위해 완벽하게 작동합니다.

    private string GeneratePasswordResetToken()
    {
        string token = Guid.NewGuid().ToString();
        var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(token);
        return Convert.ToBase64String(plainTextBytes);
    }

0

이것은 다양한 언어로 요청되었습니다. 여기에도 적용 할 수있는 암호에 대한 한 가지 질문 이 있습니다.

URL 단축에 문자열을 사용하려면 생성 된 ID가 이미 사용되었는지 여부를 확인하기 위해 Dictionary <> 또는 데이터베이스 검사가 필요합니다.


0

소문자 와 대문자 ([a-zA-Z0-9])가 있는 영숫자 문자열을 원하는 경우 빠르고 간단한 솔루션을 위해 Convert.ToBase64String ()을 사용할 수 있습니다.

고유성에 관해서는 생일 문제 를 확인 하여 충돌이 (A) 생성 된 문자열의 길이와 (B) 생성 된 문자열의 수가 주어질 가능성을 계산하십시오.

Random random = new Random();

int outputLength = 10;
int byteLength = (int)Math.Ceiling(3f / 4f * outputLength); // Base64 uses 4 characters for every 3 bytes of data; so in random bytes we need only 3/4 of the desired length
byte[] randomBytes = new byte[byteLength];
string output;
do
{
    random.NextBytes(randomBytes); // Fill bytes with random data
    output = Convert.ToBase64String(randomBytes); // Convert to base64
    output = output.Substring(0, outputLength); // Truncate any superfluous characters and/or padding
} while (output.Contains('/') || output.Contains('+')); // Repeat if we contain non-alphanumeric characters (~25% chance if length=10; ~50% chance if length=20; ~35% chance if length=32)

-1
  • Microsoft의 링크가 무작위로 생성되었는지 확실하지 않음
  • new Guid (). ToString () 살펴보기

4
Guid.NewGuid (). ToString ()-Guid에는 공개 생성자가 없습니다
— cjk apr

3
당신의 말이 맞습니다. 검증없이 타이핑하고있었습니다. 나는 원래 포스터에 요점이 있다고 확신합니다.
— Fabian Vilers

-1

GUID 해시 코드를 사용하여 고유 키 가져 오기

public static string GetUniqueKey(int length)
{
    string guidResult = string.Empty;

    while (guidResult.Length < length)
    {
        // Get the GUID.
        guidResult += Guid.NewGuid().ToString().GetHashCode().ToString("x");
    }

    // Make sure length is valid.
    if (length <= 0 || length > guidResult.Length)
        throw new ArgumentException("Length must be between 1 and " + guidResult.Length);

    // Return the first length bytes.
    return guidResult.Substring(0, length);
}

이것은 완벽하게 작동하지만 임의의 단어에는 고유 한 문자가 포함되어 있지 않습니다. 문자는 114e3 (2 개의 1), eaaea (3 개의 a와 2 개의 e), 60207 (2 개의 0) 등과 같이 반복됩니다. 영숫자 조합으로 문자를 반복하지 않고 임의의 문자열을 생성하는 방법은 무엇입니까?
— vijay

@vijay : 16 진수를 출력하기 때문에 16 자, 16 자로 제한됩니다! 가능한 출력. 무작위 문자열은 그저 무작위입니다. 이론적으로 모든 a의 문자열 (aaaaaaaaaaaaaaa)을 얻을 수 있습니다. 매우 가능성이 없지만 다른 임의의 문자열보다 더 많지 않습니다. 왜 그 제약이 필요한지 잘 모르겠지만 문자열에 문자를 추가 할 때 HashSet <T>에서 문자를 팝하고 존재를 확인한 다음 문자열에 추가하거나 그에 따라 건너 뜁니다.
— Chris Doggett
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.