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

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

Nate Lawson은 Google Tech Talk 프레젠테이션 ” Crypto Strikes Back “(13 분 11 초)에서 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의 오버 헤드가 덜 관련성이 있으므로 차이가 좁아집니다.


답변

가장 명백한 이유가 이미 언급되었으므로 여기에 더 모호한 이유가 있습니다. 일반적으로 암호화 PRNG는 “실제”엔트로피로 계속해서 다시 시드되어야합니다. 따라서 CPRNG를 너무 자주 사용하면 시스템의 엔트로피 풀이 고갈 될 수 있으며 (CPRNG 구현에 따라) 시스템의 엔트로피 풀이 약해 지거나 (따라서 공격자가 예측할 수 있도록 허용) 충전을 시도하는 동안 차단됩니다. 엔트로피 풀 (따라서 DoS 공격에 대한 공격 벡터가 됨).

어느 쪽이든, 귀하의 애플리케이션은 이제 귀하와 달리 CPRNG의 암호화 속성에 크게 의존 하는 완전히 관련없는 다른 애플리케이션에 대한 공격 벡터가되었습니다 .

이것은 Linux를 실행하는 헤드리스 서버 (마우스 및 키보드 입력과 같은 엔트로피 소스가 없기 때문에 자연스럽게 엔트로피 풀이 다소 작은)에서 관찰 된 실제 실제 문제인 BTW이며, 응용 프로그램 /dev/random에서 모든 종류에 대해 커널 CPRNG를 잘못 사용합니다. 올바른 동작은 작은 시드 값을 읽고이를 /dev/urandom사용하여 자신의 PRNG 를 시드하는 것 입니다.


답변

온라인 카드 게임이나 로터를 프로그래밍하는 경우 순서가 추측하기 거의 불가능한지 확인하고 싶을 것입니다. 그러나 사용자에게 오늘의 인용문을 표시하는 경우 보안보다 성능이 더 중요합니다.


답변

이것은 어느 정도 길이 논의되었지만 궁극적으로 성능 문제는 RNG를 선택할 때 두 번째 고려 사항입니다. 방대한 RNG 배열이 있으며 대부분의 시스템 RNG가 구성하는 통조림 Lehmer LCG는 최고도 아니고 반드시 가장 빠른 것도 아닙니다. 오래되고 느린 시스템에서는 훌륭한 타협이었습니다. 그 타협은 요즘 거의 관련이 없습니다. A) 물건이 이미 만들어졌고,이 경우에 ‘바퀴를 재발 명’할 실제 이유가없고, B) 방대한 사람들이 그것을 위해 그것을 사용할 이유가 있기 때문입니다. ‘충분하다’.

궁극적으로 RNG의 선택은 위험 / 보상 비율로 귀결됩니다. 예를 들어 비디오 게임과 같은 일부 응용 프로그램에서는 위험이 전혀 없습니다. Lehmer RNG는 적절하고 작고 간결하며 빠르며 잘 이해되고 ‘기본 제공’입니다.

예를 들어 응용 프로그램이 실제 상금이 포함 된 온라인 포커 게임 또는 복권이고 방정식의 어느 시점에서 실제 돈이 사용되는 경우 ‘in the box’Lehmer는 더 이상 적합하지 않습니다. 32 비트 버전에서는 기껏 해야 순환 시작하기 전에 2 ^ 32 개의 유효한 상태 만 가능합니다 . 요즘은 무차별 대입 공격의 문이 열려 있습니다. 이와 같은 경우 개발자는 일부 종 의 매우 긴 기간 RNG 와 같은 것으로 이동 하고 아마도 암호 학적으로 강력한 공급자로부터 시드 를 원할 것입니다. 이것은 속도와 보안 사이에 좋은 절충안을 제공합니다. 이 경우, 그 사람은 Mersenne Twister 또는 Multiple Recursive Generator 와 같은 것을 찾고있을 것입니다. 어떤 종류 입니다.

응용 프로그램이 네트워크를 통해 대량의 재무 정보를 전달하는 것과 같은 것이라면 이제 엄청난 위험이 있으며 가능한 모든 보상을 크게 압도합니다. 장갑차는 여전히 존재합니다. 때때로 중무장 한 남자 만이 적절한 보안이기 때문입니다. 저를 믿으세요. 전차, 전투기, 헬리콥터가있는 특수 작전 부대가 재정적으로 타당하다면 선택 방법이 될 것입니다. 이와 같은 경우에는 암호화 된 강력한 RNG를 사용하는 것이 합리적입니다. 얻을 수있는 보안 수준이 원하는만큼 많지 않기 때문입니다. 그래서 당신은 당신이 찾을 수있는만큼 많이 가져갈 것이고, 비용은 시간이나 돈에있어서 매우 멀리 떨어진 2 위 문제입니다. 그리고 그것이 모든 임의의 시퀀스가 ​​매우 강력한 컴퓨터에서 생성되는 데 3 초가 걸린다는 것을 의미한다면, 3 초를 기다릴 것입니다.