C #에서 임의의 8 자 영숫자 문자열을 생성하려면 어떻게해야합니까?
Random비밀번호를 생성하기 위해 클래스를 기반으로하는 메소드를 사용해서는 안됩니다 . 시딩은 Random엔트로피가 매우 낮으므로 실제로 안전하지 않습니다. 암호에는 암호화 PRNG를 사용하십시오.
C #에서 임의의 8 자 영숫자 문자열을 생성하려면 어떻게해야합니까?
Random비밀번호를 생성하기 위해 클래스를 기반으로하는 메소드를 사용해서는 안됩니다 . 시딩은 Random엔트로피가 매우 낮으므로 실제로 안전하지 않습니다. 암호에는 암호화 PRNG를 사용하십시오.
답변:
LINQ가 새로운 블랙이라고 들었습니다. LINQ를 사용한 시도는 다음과 같습니다.
private static Random random = new Random();
public static string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
(참고 : Random클래스를 사용하면 암호 또는 토큰 생성과 같은 보안 관련 항목에 적합하지 않습니다 . RNGCryptoServiceProvider강력한 난수 생성기가 필요한 경우 클래스를 사용하십시오 .)
return new string(Enumerable.Range(1, length).Select(_ => chars[random.Next(chars.Length)]).ToArray());
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringChars = new char[8];
var random = new Random();
for (int i = 0; i < stringChars.Length; i++)
{
stringChars[i] = chars[random.Next(chars.Length)];
}
var finalString = new String(stringChars);
Linq 솔루션만큼 우아하지는 않습니다.
(참고 : Random클래스를 사용하면 암호 또는 토큰 생성과 같은 보안 관련 항목에 적합하지 않습니다 . RNGCryptoServiceProvider강력한 난수 생성기가 필요한 경우 클래스를 사용하십시오 .)
GetRandomFileName솔루션은 더 빠르지 만 사용 된 문자를 제어 할 수 없으며 가능한 최대 길이는 11 자입니다. Douglas의 Guid솔루션은 매우 빠르지 만 문자는 A-F0-9로 제한되며 가능한 최대 길이는 32 자입니다.
GetRandomFileName있지만 (a) 성능 이점을 잃게되고 (b) 코드가 더 복잡해집니다.
System.Random보안에 적합하지 않습니다.
주석을 기반으로 업데이트되었습니다. 원래 구현은 ~ 1.95 %의 시간과 나머지 문자 ~ 1.56 %의 시간을 생성했습니다. 업데이트는 ~ 1.61 %의 모든 문자를 생성합니다.
프레임 워크 지원 -.NET Core 3 (및 .NET Standard 2.1 이상을 지원하는 향후 플랫폼)은 원하는 범위 내에서 임의의 정수를 생성하기 위해 암호 적으로 안전한 방법 인 RandomNumberGenerator.GetInt32 () 를 제공합니다 .
제시된 대안 중 일부와 달리이 방법은 암호 적으로 안전 합니다.
using System;
using System.Security.Cryptography;
using System.Text;
namespace UniqueKey
{
public class KeyGenerator
{
internal static readonly char[] chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
public static string GetUniqueKey(int size)
{
byte[] data = new byte[4*size];
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
{
crypto.GetBytes(data);
}
StringBuilder result = new StringBuilder(size);
for (int i = 0; i < size; i++)
{
var rnd = BitConverter.ToUInt32(data, i * 4);
var idx = rnd % chars.Length;
result.Append(chars[idx]);
}
return result.ToString();
}
public static string GetUniqueKeyOriginal_BIASED(int size)
{
char[] chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
byte[] data = new byte[size];
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
{
crypto.GetBytes(data);
}
StringBuilder result = new StringBuilder(size);
foreach (byte b in data)
{
result.Append(chars[b % (chars.Length)]);
}
return result.ToString();
}
}
}
대안의 논의를 바탕으로 여기에 아래 의견에 따라 업데이트 / 수정 .
다음은 이전 및 업데이트 된 출력의 문자 분포를 보여주는 작은 테스트 장치입니다. randomness 분석에 대한 자세한 내용은 random.org를 확인하십시오.
using System;
using System.Collections.Generic;
using System.Linq;
using UniqueKey;
namespace CryptoRNGDemo
{
class Program
{
const int REPETITIONS = 1000000;
const int KEY_SIZE = 32;
static void Main(string[] args)
{
Console.WriteLine("Original BIASED implementation");
PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKeyOriginal_BIASED);
Console.WriteLine("Updated implementation");
PerformTest(REPETITIONS, KEY_SIZE, KeyGenerator.GetUniqueKey);
Console.ReadKey();
}
static void PerformTest(int repetitions, int keySize, Func<int, string> generator)
{
Dictionary<char, int> counts = new Dictionary<char, int>();
foreach (var ch in UniqueKey.KeyGenerator.chars) counts.Add(ch, 0);
for (int i = 0; i < REPETITIONS; i++)
{
var key = generator(KEY_SIZE);
foreach (var ch in key) counts[ch]++;
}
int totalChars = counts.Values.Sum();
foreach (var ch in UniqueKey.KeyGenerator.chars)
{
Console.WriteLine($"{ch}: {(100.0 * counts[ch] / totalChars).ToString("#.000")}%");
}
}
}
}
RNGCSP처음에 사용 하는가?) mod를 사용하여 chars배열 을 색인 하는 것은 chars.Length256의 제수가 되지 않는 한 편향된 결과를 얻는다는 것을 의미합니다.
4*maxSize임의 바이트를 요청한 다음을 사용하는 것 (UInt32)(BitConverter.ToInt32(data,4*i)% chars.Length입니다. GetBytes대신을 사용 합니다 GetNonZeroBytes. 마지막으로에 대한 첫 번째 호출을 제거 할 수 있습니다 GetNonZeroBytes. 결과를 사용하지 않습니다.
해결 방법 1-가장 유연한 길이의 가장 큰 '범위'
string get_unique_string(int string_length) {
using(var rng = new RNGCryptoServiceProvider()) {
var bit_count = (string_length * 6);
var byte_count = ((bit_count + 7) / 8); // rounded up
var bytes = new byte[byte_count];
rng.GetBytes(bytes);
return Convert.ToBase64String(bytes);
}
}
이 솔루션은 GUID를 사용하는 것보다 범위가 넓습니다. GUID에는 항상 동일하고 임의적이지 않은 고정 된 비트가 두 개 있기 때문에 16 진수로 된 13자는 항상 "4"입니다. 최소한 버전 6 GUID에서는.
이 솔루션을 사용하면 모든 길이의 문자열을 생성 할 수 있습니다.
해결 방법 2-한 줄의 코드-최대 22 자에 적합
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0, 8);
솔루션 1 만큼 문자열을 생성 할 수 없으며 GUID의 고정 비트로 인해 문자열의 범위가 동일하지 않지만 대부분의 경우이 작업을 수행합니다.
해결 방법 3-약간 적은 코드
Guid.NewGuid().ToString("n").Substring(0, 8);
대부분 역사적 목적으로 여기에 보관하십시오. 약간 적은 코드를 사용하지만 범위를 줄이는 데 드는 비용이 들지만 base64 대신 16 진수를 사용하기 때문에 다른 솔루션과 동일한 범위를 나타내는 데 더 많은 문자가 필요합니다.
이는 더 많은 충돌 가능성을 의미합니다. 8 개의 문자열을 100,000 번 반복하여 테스트하면 하나의 사본이 생성됩니다.
다음은 Dot Net Perls의 Sam Allen 예제에서 얻은 예입니다.
8 자만 필요한 경우 System.IO 네임 스페이스에서 Path.GetRandomFileName ()을 사용하십시오. "Path.GetRandomFileName 메서드를 사용하면 RNGCryptoServiceProvider를 사용하여 임의성을 향상시킬 수 있기 때문에 때때로 우수합니다. 그러나 11 개의 임의 문자로 제한됩니다."
GetRandomFileName은 항상 9 번째 문자에 마침표가있는 12 자 문자열을 반환합니다. 따라서 임의의 것이 아니므로 마침표를 제거한 다음 문자열에서 8자를 가져와야합니다. 실제로 처음 8자를 가져 와서 마침표에 대해 걱정하지 않아도됩니다.
public string Get8CharacterRandomString()
{
string path = Path.GetRandomFileName();
path = path.Replace(".", ""); // Remove period.
return path.Substring(0, 8); // Return 8 character string
}
추신 : 감사합니다 샘
내 코드의 주요 목표는 다음과 같습니다.
첫 번째 속성은 알파벳 크기의 64 비트 값 모듈로를 취함으로써 달성됩니다. 작은 알파벳 (예 : 질문의 62 자)의 경우 이는 무시할만한 편향으로 이어집니다. 두 번째 및 세 번째 속성은 RNGCryptoServiceProvider대신을 사용하여 수행됩니다 System.Random.
using System;
using System.Security.Cryptography;
public static string GetRandomAlphanumericString(int length)
{
const string alphanumericCharacters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"abcdefghijklmnopqrstuvwxyz" +
"0123456789";
return GetRandomString(length, alphanumericCharacters);
}
public static string GetRandomString(int length, IEnumerable<char> characterSet)
{
if (length < 0)
throw new ArgumentException("length must not be negative", "length");
if (length > int.MaxValue / 8) // 250 million chars ought to be enough for anybody
throw new ArgumentException("length is too big", "length");
if (characterSet == null)
throw new ArgumentNullException("characterSet");
var characterArray = characterSet.Distinct().ToArray();
if (characterArray.Length == 0)
throw new ArgumentException("characterSet must not be empty", "characterSet");
var bytes = new byte[length * 8];
var result = new char[length];
using (var cryptoProvider = new RNGCryptoServiceProvider())
{
cryptoProvider.GetBytes(bytes);
}
for (int i = 0; i < length; i++)
{
ulong value = BitConverter.ToUInt64(bytes, i * 8);
result[i] = characterArray[value % (uint)characterArray.Length];
}
return new string(result);
}
가장 간단한 :
public static string GetRandomAlphaNumeric()
{
return Path.GetRandomFileName().Replace(".", "").Substring(0, 8);
}
char 배열을 하드 코딩하고 다음을 사용하면 더 나은 성능을 얻을 수 있습니다 System.Random.
public static string GetRandomAlphaNumeric()
{
var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}
영어 알파벳이 언젠가 변경되어 비즈니스를 잃을 수 있다고 걱정하는 경우 하드 코딩을 피할 수는 있지만 약간 더 나빠질 수 있습니다 ( Path.GetRandomFileName접근하는 데 비해 )
public static string GetRandomAlphaNumeric()
{
var chars = 'a'.To('z').Concat('0'.To('9')).ToList();
return new string(chars.Select(c => chars[random.Next(chars.Length)]).Take(8).ToArray());
}
public static IEnumerable<char> To(this char start, char end)
{
if (end < start)
throw new ArgumentOutOfRangeException("the end char should not be less than start char", innerException: null);
return Enumerable.Range(start, end - start + 1).Select(i => (char)i);
}
마지막 두 가지 접근 방식을 확장 방법으로 만들면 더 좋아 보입니다. System.Random 인스턴스 .
chars.Select출력 크기가 최대 알파벳 크기에 의존하기 때문에 사용 이 크게 추악합니다.
'a'.To('z')접근 에서 의미 합니까?
chars.Select().Take (n)`은 경우에만 작동합니다 chars.Count >= n. 실제로 사용하지 않는 시퀀스를 선택하는 것은 특히 암시 적 길이 제약 조건과 관련하여 약간 직관적이지 않습니다. 오히려 Enumerable.Range또는을 사용하고 싶습니다 Enumerable.Repeat. 2) "끝 문자는 시작 문자보다 작아야합니다"라는 오류 메시지가 잘못 표시 not됩니다.
chars.Count에는 방법 > n입니다. 또한 나는 직관적이지 않은 부분을 얻지 못합니다. 그것은 모든 사용이 Take직관적이지 않습니까? 나는 그것을 믿지 않는다. 오타를 지적 해 주셔서 감사합니다.
이 스레드의 다양한 답변에 대한 성능 비교는 다음과 같습니다.
// what's available
public static string possibleChars = "abcdefghijklmnopqrstuvwxyz";
// optimized (?) what's available
public static char[] possibleCharsArray = possibleChars.ToCharArray();
// optimized (precalculated) count
public static int possibleCharsAvailable = possibleChars.Length;
// shared randomization thingy
public static Random random = new Random();
// http://stackoverflow.com/a/1344242/1037948
public string LinqIsTheNewBlack(int num) {
return new string(
Enumerable.Repeat(possibleCharsArray, num)
.Select(s => s[random.Next(s.Length)])
.ToArray());
}
// http://stackoverflow.com/a/1344258/1037948
public string ForLoop(int num) {
var result = new char[num];
while(num-- > 0) {
result[num] = possibleCharsArray[random.Next(possibleCharsAvailable)];
}
return new string(result);
}
public string ForLoopNonOptimized(int num) {
var result = new char[num];
while(num-- > 0) {
result[num] = possibleChars[random.Next(possibleChars.Length)];
}
return new string(result);
}
public string Repeat(int num) {
return new string(new char[num].Select(o => possibleCharsArray[random.Next(possibleCharsAvailable)]).ToArray());
}
// http://stackoverflow.com/a/1518495/1037948
public string GenerateRandomString(int num) {
var rBytes = new byte[num];
random.NextBytes(rBytes);
var rName = new char[num];
while(num-- > 0)
rName[num] = possibleCharsArray[rBytes[num] % possibleCharsAvailable];
return new string(rName);
}
//SecureFastRandom - or SolidSwiftRandom
static string GenerateRandomString(int Length) //Configurable output string length
{
byte[] rBytes = new byte[Length];
char[] rName = new char[Length];
SolidSwiftRandom.GetNextBytesWithMax(rBytes, biasZone);
for (var i = 0; i < Length; i++)
{
rName[i] = charSet[rBytes[i] % charSet.Length];
}
return new string(rName);
}
LinqPad에서 테스트되었습니다. 문자열 크기가 10 인 경우 다음을 생성합니다.
- Linq에서 = chdgmevhcy [10]
- from 루프 = gtnoaryhxr [10]
- 선택에서 = rsndbztyby [10]
- From GenerateRandomString = owyefjjakj [10]
- SecureFastRandom에서 = VzougLYHYP [10]
- SecureFastRandom-NoCache에서 = oVQXNGmO1S [10]
그리고 성능 수치는 아주 가끔, 약간 다를하는 경향이 NonOptimized실제로 빠르게, 때로는 ForLoop및 GenerateRandomString리드에 누가 전환합니다.
- LinqIsTheNewBlack (10000x) = 96762 틱 경과 (9.6762ms)
- ForLoop (10000x) = 28970 틱 (2.897ms) 경과
- ForLoopNonOptimized (10000x) = 33336 틱 경과 (3.3336ms)
- 반복 (10000x) = 78547 틱 경과 (7.8547ms)
- GenerateRandomString (10000x) = 27416 틱 경과 (2.7416ms)
- SecureFastRandom (10000x) = 최저 13176 틱 (5ms) 최저 [다른 시스템]
- SecureFastRandom-NoCache (10000x) = 39541 틱 (17ms) 최저 [다른 시스템]
var many = 10000; Assert.AreEqual(many, new bool[many].Select(o => EachRandomizingMethod(10)).Distinct().Count());위해 EachRandomizingMethod... 각 방법으로 대체
한 줄의 코드 Membership.GeneratePassword()가 트릭을 수행합니다. :)
다음은 동일한 데모 입니다.
Eric J.가 작성한 코드는 매우 조잡합니다 (6 년 전의 코드임을 분명히 알 수 있습니다. 아마도 그는 오늘 그 코드를 쓰지 않았을 것입니다).
제시된 대안 중 일부와는 달리, 이것은 암호화 적으로 건전합니다.
사실이 아닙니다 ... (비고에 쓰여진) 비밀번호에 편견 bcdefgh이 있고 다른 것보다 조금 더 가능성이 있습니다 ( 값이 0 인 바이트를 생성 a하지 않기 때문에 GetNonZeroBytes그렇지 않습니다) 를 위해a 의해 균형이 잡히므로) 실제로 암호 적으로 소리가 나지 않습니다.
모든 문제를 해결해야합니다.
public static string GetUniqueKey(int size = 6, string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
{
using (var crypto = new RNGCryptoServiceProvider())
{
var data = new byte[size];
// If chars.Length isn't a power of 2 then there is a bias if
// we simply use the modulus operator. The first characters of
// chars will be more probable than the last ones.
// buffer used if we encounter an unusable random byte. We will
// regenerate it in this buffer
byte[] smallBuffer = null;
// Maximum random number that can be used without introducing a
// bias
int maxRandom = byte.MaxValue - ((byte.MaxValue + 1) % chars.Length);
crypto.GetBytes(data);
var result = new char[size];
for (int i = 0; i < size; i++)
{
byte v = data[i];
while (v > maxRandom)
{
if (smallBuffer == null)
{
smallBuffer = new byte[1];
}
crypto.GetBytes(smallBuffer);
v = smallBuffer[0];
}
result[i] = chars[v % chars.Length];
}
return new string(result);
}
}
우리는 또한 사용자 정의 문자열을 무작위로 사용하지만 문자열의 도우미로 구현되었으므로 약간의 유연성을 제공합니다 ...
public static string Random(this string chars, int length = 8)
{
var randomString = new StringBuilder();
var random = new Random();
for (int i = 0; i < length; i++)
randomString.Append(chars[random.Next(chars.Length)]);
return randomString.ToString();
}
용법
var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".Random();
또는
var random = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".Random(16);
내 간단한 한 줄 코드가 나를 위해 작동합니다 :)
string random = string.Join("", Guid.NewGuid().ToString("n").Take(8).Select(o => o));
Response.Write(random.ToUpper());
Response.Write(random.ToLower());
길이 문자열에 대해 이것을 확장하려면
public static string RandomString(int length)
{
//length = length < 0 ? length * -1 : length;
var str = "";
do
{
str += Guid.NewGuid().ToString().Replace("-", "");
}
while (length > str.Length);
return str.Substring(0, length);
}
질문 :Enumerable.Range 입력 하는 대신 시간을 낭비해야하는 이유는 무엇 "ABCDEFGHJKLMNOPQRSTUVWXYZ0123456789"입니까?
using System;
using System.Collections.Generic;
using System.Linq;
public class Test
{
public static void Main()
{
var randomCharacters = GetRandomCharacters(8, true);
Console.WriteLine(new string(randomCharacters.ToArray()));
}
private static List<char> getAvailableRandomCharacters(bool includeLowerCase)
{
var integers = Enumerable.Empty<int>();
integers = integers.Concat(Enumerable.Range('A', 26));
integers = integers.Concat(Enumerable.Range('0', 10));
if ( includeLowerCase )
integers = integers.Concat(Enumerable.Range('a', 26));
return integers.Select(i => (char)i).ToList();
}
public static IEnumerable<char> GetRandomCharacters(int count, bool includeLowerCase)
{
var characters = getAvailableRandomCharacters(includeLowerCase);
var random = new Random();
var result = Enumerable.Range(0, count)
.Select(_ => characters[random.Next(characters.Count)]);
return result;
}
}
답 : 마법의 줄은 나쁘다. 누구든지 "I내 문자열 맨 위에 " ? 어머니는 이런 이유로 마법 줄을 사용하지 말라고 가르쳐주었습니다.
nb 1 : @dtb와 같은 많은 사람들이 말했듯 System.Random이 암호화 보안이 필요한 경우 사용하지 마십시오 ...
nb 2 :이 답변은 가장 효율적이거나 가장 짧지는 않지만 답변과 질문을 분리 할 공간이 필요했습니다. 내 대답의 목적은 멋진 혁신적인 대답을 제공하는 것보다 마법의 끈에 대해 경고하는 것보다 낫습니다.
I입니까?
[A-Z0-9]입니다. 실수로 임의의 문자열 만 [A-HJ-Z0-9]결과를 덮는 경우 전체 허용 범위를 다루지 않는 경우 문제가 될 수 있습니다.
I. 캐릭터가 적고 깨지기 쉽기 때문입니까? 36보다 35 문자를 포함하는 크래킹 가능한 암호에 대한 통계는 무엇입니까? 코드에 여분의 쓰레기를 모두 포함시키는 것보다 위험을 감수 할 것입니다. 그러나 그것은 저입니다. 엉덩이 구멍이 아니라는 말입니다. 때로는 프로그래머가 복잡한 것을 피하기 위해 여분의 복잡한 길을가는 경향이 있다고 생각합니다.
I과 같은 문자를 제외 하고 O사람이 1and와 혼동하지 않도록하는 것이 매우 일반적 0입니다. 사람이 읽을 수있는 문자열에 신경 쓰지 않아도 괜찮지 만 누군가 입력해야 할 경우 실제로 해당 문자를 제거하는 것이 좋습니다.
CodeInChaos와 함께 다른 답변을 검토하고 CodeInChaos의 의견을 고려한 후에도 여전히 치우친 답변이 있지만 최종 최종 잘라 내기 및 붙여 넣기 솔루션 이 필요 하다고 생각했습니다 . 그래서 내 대답을 업데이트하는 동안 나는 모두 나가기로 결정했습니다.
이 코드의 최신 버전을 보려면 Bitbucket의 새로운 Hg 저장소를 방문하십시오. https://bitbucket.org/merarischroeder/secureswiftrandom) 하십시오 . 난 당신이 복사에서 코드를 붙여 추천 : https://bitbucket.org/merarischroeder/secureswiftrandom/src/6c14b874f34a3f6576b0213379ecdf0ffc7496ea/Code/Alivate.SolidSwiftRandom/SolidSwiftRandom.cs?at=default&fileviewer=file-view-default (메이크업 확실히 당신은 클릭 Raw 버튼을 사용하면 쉽게 복사하고 최신 버전을 사용할 수 있습니다.이 링크는 최신 버전이 아닌 특정 버전의 코드로 이동한다고 생각합니다.
업데이트 된 메모 :
궁금한 최종 솔루션 :
static char[] charSet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
static int byteSize = 256; //Labelling convenience
static int biasZone = byteSize - (byteSize % charSet.Length);
public string GenerateRandomString(int Length) //Configurable output string length
{
byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
char[] rName = new char[Length];
SecureFastRandom.GetNextBytesMax(rBytes, biasZone);
for (var i = 0; i < Length; i++)
{
rName[i] = charSet[rBytes[i] % charSet.Length];
}
return new string(rName);
}
그러나 당신은 나의 새로운 (예상치 않은) 수업이 필요합니다 :
/// <summary>
/// My benchmarking showed that for RNGCryptoServiceProvider:
/// 1. There is negligable benefit of sharing RNGCryptoServiceProvider object reference
/// 2. Initial GetBytes takes 2ms, and an initial read of 1MB takes 3ms (starting to rise, but still negligable)
/// 2. Cached is ~1000x faster for single byte at a time - taking 9ms over 1MB vs 989ms for uncached
/// </summary>
class SecureFastRandom
{
static byte[] byteCache = new byte[1000000]; //My benchmark showed that an initial read takes 2ms, and an initial read of this size takes 3ms (starting to raise)
static int lastPosition = 0;
static int remaining = 0;
/// <summary>
/// Static direct uncached access to the RNGCryptoServiceProvider GetBytes function
/// </summary>
/// <param name="buffer"></param>
public static void DirectGetBytes(byte[] buffer)
{
using (var r = new RNGCryptoServiceProvider())
{
r.GetBytes(buffer);
}
}
/// <summary>
/// Main expected method to be called by user. Underlying random data is cached from RNGCryptoServiceProvider for best performance
/// </summary>
/// <param name="buffer"></param>
public static void GetBytes(byte[] buffer)
{
if (buffer.Length > byteCache.Length)
{
DirectGetBytes(buffer);
return;
}
lock (byteCache)
{
if (buffer.Length > remaining)
{
DirectGetBytes(byteCache);
lastPosition = 0;
remaining = byteCache.Length;
}
Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
lastPosition += buffer.Length;
remaining -= buffer.Length;
}
}
/// <summary>
/// Return a single byte from the cache of random data.
/// </summary>
/// <returns></returns>
public static byte GetByte()
{
lock (byteCache)
{
return UnsafeGetByte();
}
}
/// <summary>
/// Shared with public GetByte and GetBytesWithMax, and not locked to reduce lock/unlocking in loops. Must be called within lock of byteCache.
/// </summary>
/// <returns></returns>
static byte UnsafeGetByte()
{
if (1 > remaining)
{
DirectGetBytes(byteCache);
lastPosition = 0;
remaining = byteCache.Length;
}
lastPosition++;
remaining--;
return byteCache[lastPosition - 1];
}
/// <summary>
/// Rejects bytes which are equal to or greater than max. This is useful for ensuring there is no bias when you are modulating with a non power of 2 number.
/// </summary>
/// <param name="buffer"></param>
/// <param name="max"></param>
public static void GetBytesWithMax(byte[] buffer, byte max)
{
if (buffer.Length > byteCache.Length / 2) //No point caching for larger sizes
{
DirectGetBytes(buffer);
lock (byteCache)
{
UnsafeCheckBytesMax(buffer, max);
}
}
else
{
lock (byteCache)
{
if (buffer.Length > remaining) //Recache if not enough remaining, discarding remaining - too much work to join two blocks
DirectGetBytes(byteCache);
Buffer.BlockCopy(byteCache, lastPosition, buffer, 0, buffer.Length);
lastPosition += buffer.Length;
remaining -= buffer.Length;
UnsafeCheckBytesMax(buffer, max);
}
}
}
/// <summary>
/// Checks buffer for bytes equal and above max. Must be called within lock of byteCache.
/// </summary>
/// <param name="buffer"></param>
/// <param name="max"></param>
static void UnsafeCheckBytesMax(byte[] buffer, byte max)
{
for (int i = 0; i < buffer.Length; i++)
{
while (buffer[i] >= max)
buffer[i] = UnsafeGetByte(); //Replace all bytes which are equal or above max
}
}
}
역사-이 대답에 대한 나의 오래된 해결책은 Random 객체를 사용했습니다.
private static char[] charSet =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();
static rGen = new Random(); //Must share, because the clock seed only has Ticks (~10ms) resolution, yet lock has only 20-50ns delay.
static int byteSize = 256; //Labelling convenience
static int biasZone = byteSize - (byteSize % charSet.Length);
static bool SlightlyMoreSecurityNeeded = true; //Configuration - needs to be true, if more security is desired and if charSet.Length is not divisible by 2^X.
public string GenerateRandomString(int Length) //Configurable output string length
{
byte[] rBytes = new byte[Length]; //Do as much before and after lock as possible
char[] rName = new char[Length];
lock (rGen) //~20-50ns
{
rGen.NextBytes(rBytes);
for (int i = 0; i < Length; i++)
{
while (SlightlyMoreSecurityNeeded && rBytes[i] >= biasZone) //Secure against 1/5 increased bias of index[0-7] values against others. Note: Must exclude where it == biasZone (that is >=), otherwise there's still a bias on index 0.
rBytes[i] = rGen.NextByte();
rName[i] = charSet[rBytes[i] % charSet.Length];
}
}
return new string(rName);
}
공연:
또한 확인하십시오 :
이 링크는 또 다른 접근법입니다. 이 새로운 코드베이스에 버퍼링을 추가 할 수 있었지만, 가장 중요한 것은 바이어스를 제거하고 속도와 장단점을 벤치마킹하는 다양한 방법을 모색하는 것이 었습니다.
charSet.Length대신 사용할 수 있습니다 62. 2) Random잠금이없는 정적 은이 코드가 스레드 안전하지 않다는 것을 의미합니다. 3) 0-255 mod 62를 줄이면 감지 가능한 바이어스가 발생합니다. 4) ToStringchar 배열 에는 사용할 수 없으며 항상을 반환합니다 "System.Char[]". new String(rName)대신 사용해야 합니다.
System.Random)로 시작한 다음 자신의 코드에서 편견을 조심스럽게 피하는 것은 약간 어리 석습니다 . "똥 깍기"라는 표현이 떠 오릅니다.
끔찍한 일이지만, 나는 스스로 도울 수 없었습니다.
namespace ConsoleApplication2
{
using System;
using System.Text.RegularExpressions;
class Program
{
static void Main(string[] args)
{
Random adomRng = new Random();
string rndString = string.Empty;
char c;
for (int i = 0; i < 8; i++)
{
while (!Regex.IsMatch((c=Convert.ToChar(adomRng.Next(48,128))).ToString(), "[A-Za-z0-9]"));
rndString += c;
}
Console.WriteLine(rndString + Environment.NewLine);
}
}
}
무작위 문자열의 형식을 제어 하고이 게시물을 발견 한 더 구체적인 답변을 찾고있었습니다. 예를 들어, (자동차의) 번호판은 (국가별로) 특정 형식을 가지며 임의의 번호판을 만들고 싶었습니다.
나는 이것을 위해 Random의 내 자신의 확장 방법을 작성하기로 결정했다. (멀티 스레딩 시나리오에서 두 배가 될 수 있으므로 동일한 랜덤 객체를 재사용하기위한 것입니다). 요점 ( https://gist.github.com/SamVanhoutte/808845ca78b9c041e928 )을 만들었지 만 확장 클래스를 여기에 복사합니다.
void Main()
{
Random rnd = new Random();
rnd.GetString("1-###-000").Dump();
}
public static class RandomExtensions
{
public static string GetString(this Random random, string format)
{
// Based on http://stackoverflow.com/questions/1344221/how-can-i-generate-random-alphanumeric-strings-in-c
// Added logic to specify the format of the random string (# will be random string, 0 will be random numeric, other characters remain)
StringBuilder result = new StringBuilder();
for(int formatIndex = 0; formatIndex < format.Length ; formatIndex++)
{
switch(format.ToUpper()[formatIndex])
{
case '0': result.Append(getRandomNumeric(random)); break;
case '#': result.Append(getRandomCharacter(random)); break;
default : result.Append(format[formatIndex]); break;
}
}
return result.ToString();
}
private static char getRandomCharacter(Random random)
{
string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
return chars[random.Next(chars.Length)];
}
private static char getRandomNumeric(Random random)
{
string nums = "0123456789";
return nums[random.Next(nums.Length)];
}
}
이제 하나의 라이너 맛.
private string RandomName()
{
return new string(
Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
.Select(s =>
{
var cryptoResult = new byte[4];
using (var cryptoProvider = new RNGCryptoServiceProvider())
cryptoProvider.GetBytes(cryptoResult);
return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
})
.ToArray());
}
RNGCryptoServiceProvider사용 후 폐기해야합니다.
고유 (순서, 카운터 또는 날짜) 및 임의의 두 부분을 결합하십시오.
public class RandomStringGenerator
{
public static string Gen()
{
return ConvertToBase(DateTime.UtcNow.ToFileTimeUtc()) + GenRandomStrings(5); //keep length fixed at least of one part
}
private static string GenRandomStrings(int strLen)
{
var result = string.Empty;
var Gen = new RNGCryptoServiceProvider();
var data = new byte[1];
while (result.Length < strLen)
{
Gen.GetNonZeroBytes(data);
int code = data[0];
if (code > 48 && code < 57 || // 0-9
code > 65 && code < 90 || // A-Z
code > 97 && code < 122 // a-z
)
{
result += Convert.ToChar(code);
}
}
return result;
}
private static string ConvertToBase(long num, int nbase = 36)
{
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //if you wish make algorithm more secure - change order of letter here
// check if we can convert to another base
if (nbase < 2 || nbase > chars.Length)
return null;
int r;
var newNumber = string.Empty;
// in r we have the offset of the char that was converted to the new base
while (num >= nbase)
{
r = (int) (num % nbase);
newNumber = chars[r] + newNumber;
num = num / nbase;
}
// the last number to convert
newNumber = chars[(int)num] + newNumber;
return newNumber;
}
}
테스트 :
[Test]
public void Generator_Should_BeUnigue1()
{
//Given
var loop = Enumerable.Range(0, 1000);
//When
var str = loop.Select(x=> RandomStringGenerator.Gen());
//Then
var distinct = str.Distinct();
Assert.AreEqual(loop.Count(),distinct.Count()); // Or Assert.IsTrue(distinct.Count() < 0.95 * loop.Count())
}
<=하고 >=대신 <과 >. 3) &&표현식에 불필요한 괄호를 추가하여 우선 순위가 있음을 분명히하지만 물론 스타일 선택입니다.
WinRT (Windows Store 앱)를위한 Eric J 솔루션의 변형, 즉 암호화 사운드는 다음과 같습니다.
public static string GenerateRandomString(int length)
{
var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
var result = new StringBuilder(length);
for (int i = 0; i < length; ++i)
{
result.Append(CryptographicBuffer.GenerateRandomNumber() % chars.Length);
}
return result.ToString();
}
성능이 중요한 경우 (특히 길이가 긴 경우) :
public static string GenerateRandomString(int length)
{
var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
var result = new System.Text.StringBuilder(length);
var bytes = CryptographicBuffer.GenerateRandom((uint)length * 4).ToArray();
for (int i = 0; i < bytes.Length; i += 4)
{
result.Append(BitConverter.ToUInt32(bytes, i) % chars.Length);
}
return result.ToString();
}
나는 이것이 최선의 방법이 아니라는 것을 안다. 그러나 당신은 이것을 시도 할 수 있습니다.
string str = Path.GetRandomFileName(); //This method returns a random file name of 11 characters
str = str.Replace(".","");
Console.WriteLine("Random string: " + str);
나는 이것이 암호 적으로 어떻게 들리는 지 모르겠지만, 훨씬 복잡한 솔루션보다 훨씬 읽기 쉽고 간결하며 System.Random기반 솔루션 보다 "무작위 적"이어야 합니다.
return alphabet
.OrderBy(c => Guid.NewGuid())
.Take(strLength)
.Aggregate(
new StringBuilder(),
(builder, c) => builder.Append(c))
.ToString();
이 버전 또는 다음 버전이 "더 예쁘다"고 생각할 수는 없지만 동일한 결과를 제공합니다.
return new string(alphabet
.OrderBy(o => Guid.NewGuid())
.Take(strLength)
.ToArray());
물론 속도에 최적화되어 있지 않으므로 매초마다 수백만 개의 임의 문자열을 생성하는 것이 중요하다면 다른 문자열을 시도하십시오!
참고 :이 솔루션은 알파벳의 기호 반복을 허용하지 않으며 알파벳은 출력 문자열보다 크거나 같아야하므로 일부 상황에서는이 방법이 바람직하지 않으므로 사용 사례에 따라 다릅니다.
public static class StringHelper
{
private static readonly Random random = new Random();
private const int randomSymbolsDefaultCount = 8;
private const string availableChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
private static int randomSymbolsIndex = 0;
public static string GetRandomSymbols()
{
return GetRandomSymbols(randomSymbolsDefaultCount);
}
public static string GetRandomSymbols(int count)
{
var index = randomSymbolsIndex;
var result = new string(
Enumerable.Repeat(availableChars, count)
.Select(s => {
index += random.Next(s.Length);
if (index >= s.Length)
index -= s.Length;
return s[index];
})
.ToArray());
randomSymbolsIndex = index;
return result;
}
}
random.Next직접 결과를 사용하는 대신 인덱스를 증가시키는 요점은 무엇입니까 ? 코드를 복잡하게 만들고 유용한 것을 얻지 못합니다.
다음은 알파벳과 숫자를 정의하지 않고 임의의 영숫자 문자열 (암호 및 테스트 데이터를 생성하는 데 사용)을 생성하는 메커니즘입니다.
CleanupBase64는 문자열에서 필요한 부분을 제거하고 임의의 영숫자를 반복적으로 추가합니다.
public static string GenerateRandomString(int length)
{
var numArray = new byte[length];
new RNGCryptoServiceProvider().GetBytes(numArray);
return CleanUpBase64String(Convert.ToBase64String(numArray), length);
}
private static string CleanUpBase64String(string input, int maxLength)
{
input = input.Replace("-", "");
input = input.Replace("=", "");
input = input.Replace("/", "");
input = input.Replace("+", "");
input = input.Replace(" ", "");
while (input.Length < maxLength)
input = input + GenerateRandomString(maxLength);
return input.Length <= maxLength ?
input.ToUpper() : //In my case I want capital letters
input.ToUpper().Substring(0, maxLength);
}
GenerateRandomString과에 전화를 걸 GetRandomString내에서 SanitiseBase64String. 또한을 (를) 선언 SanitiseBase64String 하고 호출 CleanUpBase64String했습니다 GenerateRandomString.
100 % 확실하지는 않습니다. 여기에서 모든 옵션을 테스트하지 않았지만 테스트 한 옵션 중 가장 빠릅니다. 스톱워치로 시간을 정하고 9-10 틱을 표시하므로 속도가 보안보다 중요하면 다음을 시도하십시오.
private static Random random = new Random();
public static string Random(int length)
{
var stringChars = new char[length];
for (int i = 0; i < length; i++)
{
stringChars[i] = (char)random.Next(0x30, 0x7a);
return new string(stringChars);
}
}