자바의 난수생성기 Random, SecureRandom

이영진·2022년 4월 6일
0

프로젝트에서 비밀번호를 잊어버렸을때 임시비밀번호를 발급하기위한 6자리 숫자의 고유한 난수를 생성하기위해 Random을 사용하다가 정말 실무에서 이걸 쓸까..? 하는 궁금증이생겼다.

결론부터 말하자면 Java가 제공하는 Random은 암호학적으로 안전하지않다.

Random

https://docs.oracle.com/javase/8/docs/api/java/util/Random.html

java.util.Random의 인스턴스는 암호학적으로 안전하지 않습니다.
보안에 민감한 응용 프로그램에서 사용할 암호학적으로 안전한 의사 난수 생성기를 얻으려면 SecureRandom을 대신 사용하는 것이 좋습니다.

Random함수는 난수를 생성하긴한다.
그러나 진짜 난수가 생성되는게아니라, 난수처럼 보이는 알고리즘을 통한 규칙적인 난수를 생성하는것이다.

컴퓨터는 사람과 달리 무의식적인 선택, 또는 우연에 의하는 선택을 할 수 없기에 기본적으로 정해진 입력에 따라 정해진 값만 낼수있다.
흔히 보는 랜덤은 정말로 임의의 값이 아니고 특정한 방법으로 여러 계산 과정을 거쳐 사람이 볼 때에 마치 임의의 값인 것처럼 보이게 하는 것이다. '의사난수(Pseudo Random)'라고 한다.
이를 해결하기 위하는 방법은 난수표를 여러 개 만들어 놓고 매번 다른 난수표를 읽히는 것이다.
이 난수표를 선택하는 것을 '시드'라고 한다. 그런데 시드값이 똑같으면 선택되는 난수표도 똑같기 때문에 시드값도 난수여야 한다. 즉, 난수를 만들려면 난수가 필요한 문제가 발생하는 것.

https://namu.wiki/w/%EB%82%9C%EC%88%98%EC%83%9D%EC%84%B1

즉, 기준이 되는값을 세팅해주고 난수인것처럼 보이는 랜덤의값을 보여주는것 같지만
시드값이 동일하면 매번같은값을 보여주는것이다.

    @Test
    @DisplayName("Random함수 테스트")
    void randomTest() {
        int i = 0;
        while (i < 3) {
            Random random = new Random(10);
            for (int j = 0; j < 5; j++) {
			   System.out.println(random.nextDouble());
               i++;
        	}
    	}
    } 

반복되는 패턴이 보일것이다. 정확한 랜덤이 아니다.
이를해결하기위해서, Seed값을 시간으로 지정하는것이 기본값이지만 공격자가 시드 생성시간을 알고있으면 위험할수있다.

SecureRandom

  • SecureRandom은 48비트만 있는 Random함수와 달리 최대 128비트를 포함할수있기때문에 반복할가능성이 더적다. (2^48 번의 시도와 2^128번의 시도의 차이)

  • Random클래스는 난수 생성알고리즘에 시스템시간을 입력으로 사용하는반면, SecureRandom 클래스는 운영체제의 임의 데이터를 사용합니다.

    • /dev/random 경로에 있는 유사난수 생성역할(키 입력 사이의 간격 시간 등을 기록)을 하는 파일을 사용한다.
    • 시스템 시간을 공격자가 알게된다면 재생성 되기가 쉽다.
  • FIPS 140-2(암호학에서 사용되는 생성기표준) 에서 지정한 테스트를 준수하기때문에 좀더 안전하다

  • 조금더 안전한 알고리즘을 구현한다 (SHA1PRNG)

   @Test
    @DisplayName("SecureRandom함수 테스트")
    void secureRandomTest() throws NoSuchAlgorithmException {
        int i = 0;
        while (i < 3) {
            SecureRandom random = SecureRandom.getInstanceStrong();
            //  getInstanceStrong() : 보안속성에 지정된 알고리즘을 사용하여 SecureRandom 객체를 반환한다.
            for (int j = 0; j < 5; j++) {
            	System.out.printf("%.5f  ", random.nextDouble());
            	i++;
            	System.out.println();
             }
        }
    }

thread-safe

자바7부터 내부적으로 동기화되어 명시적으로 스레드안전성을 보장한다.

현재, 내 프로젝트에선 6자리의 고유한 수이므로 중복값이 나올확률이 10^6의 1로 현저히 낮다고 볼수있으며 적용해볼 수 있다는 생각이든다.

references:
https://docs.oracle.com/javase/8/docs/api/java/util/Random.html
https://docs.oracle.com/javase/8/docs/api/java/security/SecureRandom.html
https://withseungryu.tistory.com/1
https://kdhyo98.tistory.com/m/48

0개의 댓글