programing tip

System.Security.Cryptography.RandomNumberGenerator 대신 C # 클래스 System.Random을 사용하는 이유는 무엇입니까?

itbloger 2020. 10. 13. 07:32
반응형

System.Security.Cryptography.RandomNumberGenerator 대신 C # 클래스 System.Random을 사용하는 이유는 무엇입니까?


왜 누군가 System.Security.Cryptography.RandomNumberGenerator (또는 RandomNumberGenerator가 추상이기 때문에 하위 클래스) 의 암호화 보안 난수 생성기를 사용하는 대신 System.Random 의 "표준"난수 생성기를 사용하는 이유는 무엇입니까?

Nate Lawson은 13:11 분의 Google Tech Talk 프레젠테이션 " Crypto Strikes Back "에서 Python, Java 및 C #의 "표준"난수 생성기를 사용하지 말고 대신 암호화 보안 버전을 사용하라고 말합니다.

난수 생성기의 두 가지 버전의 차이점을 알고 있습니다 ( 질문 101337 참조 ).

그러나 보안 난수 생성기를 항상 사용하지 않는 이유는 무엇입니까? 왜 System.Random을 사용합니까? 아마도 성능?


속도와 의도. 난수를 생성하고 보안이 필요하지 않은 경우 왜 느린 암호화 기능을 사용합니까? 보안이 필요하지 않은데 왜 다른 사람이 그 번호가 안전하지 않을 때 보안을 위해 사용될 수 있다고 생각하게 만들까요?


속도와 더 유용한 인터페이스 외에도 NextDouble()고정 시드 값을 사용하여 반복 가능한 무작위 시퀀스를 만들 수도 있습니다. 이것은 테스팅 중에 매우 유용합니다.

Random gen1 = new Random();     // auto seeded by the clock
Random gen2 = new Random(0);    // Next(10) always yields 7,8,7,5,2,....

먼저 연결 한 프레젠테이션은 보안 목적으로 난수에 대해서만 설명합니다. 따라서 Random비 보안 목적으로 나쁘다고 주장하지 않습니다 .

그러나 나는 그것이 있다고 주장한다. .net 4 구현에는 Random여러 가지 결함이 있습니다. 난수의 품질에 신경 쓰지 않는 경우에만 사용하는 것이 좋습니다. 더 나은 타사 구현을 사용하는 것이 좋습니다.

결함 1 : 시딩

기본 생성자는 현재 시간으로 시드됩니다. 따라서 Random짧은 시간 프레임 (약 10ms) 내에 기본 생성자 생성 된 모든 인스턴스 는 동일한 시퀀스를 반환합니다. 이것은 문서화되고 "설계에 따라"입니다. Random각 스레드 실행의 시작 부분에 단순히 인스턴스를 만들 수 없기 때문에 코드를 다중 스레드하려는 경우 특히 짜증이납니다 .

해결 방법은 기본 생성자를 사용할 때 각별히주의하고 필요한 경우 수동으로 시드하는 것입니다.

여기서 또 다른 문제는 시드 공간이 다소 작다는 것입니다 (31 비트). 따라서 Random완벽하게 임의의 시드로 50k 인스턴스를 생성 하면 아마도 생일 역설 로 인해 한 시퀀스의 난수를 두 번 얻을 수 있습니다. 따라서 수동 파종도 쉽지 않습니다.

결함 2 :에서 반환 된 난수의 분포 Next(int maxValue)가 편향 됨

Next(int maxValue)분명히 균일하지 않은 매개 변수가 있습니다 . 예를 들어 계산 r.Next(1431655765) % 2하면 0샘플의 약 2/3를 얻을 수 있습니다. (답변 끝에 샘플 코드가 있습니다.)

결함 3 : NextBytes()방법이 비효율적입니다.

의 바이트 당 비용은를 NextBytes()사용하여 전체 정수 샘플을 생성하는 비용만큼 큽니다 Next(). 이것으로부터 나는 그들이 실제로 바이트 당 하나의 샘플을 생성한다고 생각합니다.

각 샘플에서 3 바이트를 사용하는 더 나은 구현 NextBytes()은 거의 3 배 빨라 집니다.

이 결함 덕분 에 내 컴퓨터 (Win7, Core i3 2600MHz) Random.NextBytes()보다 약 25 % 더 빠릅니다 System.Security.Cryptography.RNGCryptoServiceProvider.GetBytes.

누군가가 소스 / 디 컴파일 된 바이트 코드를 조사하면 블랙 박스 분석에서 찾은 것보다 훨씬 더 많은 결함을 발견 할 것이라고 확신합니다.


코드 샘플

r.Next(0x55555555) % 2 강하게 편향되어 있습니다.

Random r = new Random();
const int mod = 2;
int[] hist = new int[mod];
for(int i = 0; i < 10000000; i++)
{
    int num = r.Next(0x55555555);
    int num2 = num % 2;
    hist[num2]++;
}
for(int i=0;i<mod;i++)
    Console.WriteLine(hist[i]);

공연:

byte[] bytes=new byte[8*1024];
var cr=new System.Security.Cryptography.RNGCryptoServiceProvider();
Random r=new Random();

// Random.NextBytes
for(int i=0;i<100000;i++)
{
    r.NextBytes(bytes);
}

//One sample per byte
for(int i=0;i<100000;i++)
{   
    for(int j=0;j<bytes.Length;j++)
      bytes[j]=(byte)r.Next();
}

//One sample per 3 bytes
for(int i=0;i<100000;i++)
{
    for(int j=0;j+2<bytes.Length;j+=3)
    {
        int num=r.Next();
        bytes[j+2]=(byte)(num>>16);   
        bytes[j+1]=(byte)(num>>8);
        bytes[j]=(byte)num;
    }
    //Yes I know I'm not handling the last few bytes, but that won't have a noticeable impact on performance
}

//Crypto
for(int i=0;i<100000;i++)
{
    cr.GetBytes(bytes);
}

System.Random은 암호화 보안 난수를 생성하지 않기 때문에 훨씬 더 성능이 좋습니다.

무작위 데이터로 4 바이트의 버퍼를 1,000,000 번 채우는 간단한 테스트는 Random의 경우 49ms, RNGCryptoServiceProvider의 경우 2845ms가 걸립니다. 채우는 버퍼의 크기를 늘리면 RNGCryptoServiceProvider의 오버 헤드가 덜 관련성이 있으므로 차이가 좁아집니다.


The most obvious reasons have already been mentioned, so here's a more obscure one: cryptographic PRNGs typically need to be continually be reseeded with "real" entropy. Thus, if you use a CPRNG too often, you could deplete the system's entropy pool, which (depending on the implementation of the CPRNG) will either weaken it (thus allowing an attacker to predict it) or it will block while trying to fill up its entropy pool (thus becoming an attack vector for a DoS attack).

Either way, your application has now become an attack vector for other, totally unrelated applications which – unlike yours – actually vitally depend on the cryptographic properties of the CPRNG.

This is an actual real world problem, BTW, that has been observed on headless servers (which naturally have rather small entropy pools because they lack entropy sources such as mouse and keyboard input) running Linux, where applications incorrectly use the /dev/random kernel CPRNG for all sorts of random numbers, whereas the correct behavior would be to read a small seed value from /dev/urandom and use that to seed their own PRNG.


If you're programming an online card game or lotter then you would want to make sure the sequence is next to impossible to guess. However, if you are showing users, say, a quote of the day the performance is more important than security.


Note that the System.Random class in C# is coded incorrectly, so should be avoided.

https://connect.microsoft.com/VisualStudio/feedback/details/634761/system-random-serious-bug#tabs


This has been discussed at some length, but ultimately, the issue of performance is a secondary consideration when selecting a RNG. There are a vast array of RNGs out there, and the canned Lehmer LCG that most system RNGs consists of is not the best nor even necessarily the fastest. On old, slow systems it was an excellent compromise. That compromise is seldom ever really relevant these days. The thing persists into present day systems primarily because A) the thing is already built, and there is no real reason to 'reinvent the wheel' in this case, and B) for what the vast bulk of people will be using it for, it's 'good enough'.

Ultimately, the selection of an RNG comes down to Risk/Reward ratio. In some applications, for example a video game, there is no risk whatsoever. A Lehmer RNG is more than adequate, and is small, concise, fast, well-understood, and 'in the box'.

If the application is, for example, an on-line poker game or lottery where there are actual prizes involved and real money comes into play at some point in the equation, the 'in the box' Lehmer is no longer adequate. In a 32-bit version, it only has 2^32 possible valid states before it begins to cycle at best. These days, that's an open door to a brute force attack. In a case like this, the developer will want to go to something like a Very Long Period RNG of some species, and probably seed it from a cryptographically strong provider. This gives a good compromise between speed and security. In such a case, the person will be out looking for something like the Mersenne Twister, or a Multiple Recursive Generator of some kind.

If the application is something like communicating large quantities of financial information over a network, now there is a huge risk, and it heavily outweights any possible reward. There are still armored cars because sometimes heavily armed men is the only security that's adequate, and trust me, if a brigade of special ops people with tanks, fighters, and helicopters was financially feasible, it would be the method of choice. In a case like this, using a cryptographically strong RNG makes sense, because whatever level of security you can get, it's not as much as you want. So you'll take as much as you can find, and the cost is a very, very remote second-place issue, either in time or money. And if that means that every random sequence takes 3 seconds to generate on a very powerful computer, you're going to wait the 3 seconds, because in the scheme of things, that is a trivial cost.


Not everyone needs cryptographically secure random numbers, and they might benefit more from a speedier plain prng. Perhaps more importantly is that you can control the sequence for System.Random numbers.

In a simulation utilizing random numbers you might want to recreate, you rerun the simulation with the same seed. It can be handy for tracking bugs when you want to regenerate a given faulty scenario as well - running your program with the exact same sequence of random numbers that crashed the program.


If I don't need the security, i.e., I just want a relatively indeterminate value not one that's cryptographically strong, Random has a much easier interface to use.


Different needs call for different RNGs. For crypto, you want your random numbers to be as random as possible. For Monte Carlo simulations, you want them to fill the space evenly and to be able to start the RNG from a known state.


Random is not a random number generator, it is a deterministic pseudo-random sequence generator, which takes its name for historical reasons.

The reason to use System.Random is if you want these properties, namely a deterministic sequence, which is guaranteed to produce the same sequence of results when initialized with the same seed.

If you want to improve the "randomness" without sacrificing the interface, you can inherit from System.Random overriding several methods.

Why would you want a deterministic sequence

One reason to have a deterministic sequence rather than true randomness is because it is repeatable.

For example, if you are running a numerical simulation, you can initialize the sequence with a (true) random number, and record what number was used.

Then, if you wish to repeat the exact same simulation, e.g. for debugging purposes, you can do so by instead initializing the sequence with the recorded value.

Why would you want this particular, not very good, sequence

The only reason I can think of would be for backwards compatibility with existing code which uses this class.

In short, if you want to improve the sequence without changing the rest of your code, go ahead.


I wrote a game (Crystal Sliders on the iPhone: Here) that would put up a "random" series of gems (images) on the map and you would rotate the map how you wanted it and select them and they went away. - Similar to Bejeweled. I was using Random(), and it was seeded with the number of 100ns ticks since the phone booted, a pretty random seed.

I found it amazing that it would generate games that were nearly identical to each other -of the 90 or so gems, of 2 colors, I would get two EXACTLY the same except for 1 to 3 gems! If you flip 90 coins and get the same pattern except for 1-3 flips, that is VERY unlikely! I have several screen shots that show them the same. I was shocked at how bad System.Random() was! I assumed, that I MUST have written something horribly wrong in my code and was using it wrong. I was wrong though, it was the generator.

As an experiment - and a final solution, I went back to the random number generator that I've been using since 1985 or so - which is VASTLY better. It is faster, has a period of 1.3 * 10^154 (2^521) before it repeats. The original algorithm was seeded with a 16 bit number, but I changed that to a 32 bit number, and improved the initial seeding.

The original one is here:

ftp://ftp.grnet.gr/pub/lang/algorithms/c/jpl-c/random.c

Over the years, I've thrown every random number test I could think of at this, and it past all of them. I don't expect that it is of any value as a cryptographic one, but it returns a number as fast as "return *p++;" until it runs out of the 521 bits, and then it runs a quick process over the bits to create new random ones.

I created a C# wrapper - called it JPLRandom() implemented the same interface as Random() and changed all the places where I called it in the code.

The difference was VASTLY better - OMG I was amazed - there should be no way I could tell from just looking at the screens of 90 or so gems in a pattern, but I did an emergency release of my game following this.

And I would never use System.Random() for anything ever again. I'm SHOCKED that their version is blown away by something that is now 30 years old!

-Traderhut Games

참고URL : https://stackoverflow.com/questions/1257299/why-use-the-c-sharp-class-system-random-at-all-instead-of-system-security-crypto

반응형