RSA 암호화 알고리즘

Javascript로 구현하기

RSA의 암호화 순서도는 간단히 다음과 같습니다.

공개키와 개인키는 다음과 같은 과정을 거칩니다.

  1. 두개의 큰 소수 p와 q를 선택하고, 이들의 곱 n = pq를 계산한다.
  2. φ(n) = (p-1)⋅(q-1)
  3. n과 서로소인 e를 선택하고 d⋅e ≡ 1 mod φ(n) 을 만족하는 수 d를 계산한다.
  4. (n, e)는 공개키로 공개하고, d는 개인키로 보관한다.

그럼 소수는 어떻게 구할까요?

에라토스테네스의 체

  1. 2부터 구할 값 (위에서는 120) 까지 배수값들을 지웁니다.
  2. 그런데 지운값은 검사할 필요가 없겠지요?
  3. 추가로 m = a*b 일 때 a와 b 둘 중 하나는 √n 이하입니다. 즉, √n까지 검사하면 됩니다.

즉, 이전에 지워진 값은 제외하고 2부터 √n까지 배수값들을 지우면 됩니다.

d값을 구하는 방법은?

유클리드 호제법

우선 확장 유클리드 호제법 전에 유클리드 호제법부터..

우선 유클리드 호제법의 정의는 다음과 같습니다.

두 양의정수 a, b ( b > a )에 대하여 b = a⋅q + r, (0 ≤ r < a) 이라 하면 gcd(a, b) = gcd(a, r)

그럼 증명해 보겠습니다.

G = gcd(a, b)
a = G⋅A
b = G⋅B
A 와 B는 서로소
b = a⋅q + r (0 ≤ r < a)
GB = GA⋅q + r
G⋅(B — A⋅q) = r
G⋅A = a
즉, G는 a와 r의 공약수.
B-A⋅q 와 A가 서로소이면 증명 끝
gcd(A, B-A⋅q) = m
m = 1 을 보이면 증명 끝 (m = 1 이면 B-A⋅q 와 A가 서로소)
적당한 정수 k, i
A = m⋅k
B — A⋅q = m⋅i
B = m⋅i + A⋅q = m⋅i + m⋅k⋅q = m⋅(i + k⋅q)
즉, A와 B의 공약수는 m
A, B는 서로소 이므로 m = 1

확장 유클리드 호제법

확장 유클리드의 호제법의 정의는 다음과 같습니다.

두 양의정수 a, b ( b > a )에 대하여 b = a⋅q + r, (0 ≤ r < a) 이라 하면 gcd(a, b) 일때 a⋅x + b⋅y = gcd(a, b) 인 x, y를 구하는 방법

그럼 한번 구해봅시다. 위, 유클리드 호제법을 기억하며 식을 따라갑니다.

gcd(240, 47)
240 = 47⋅5 + 5
5 = 240 + 47⋅(-5)
47 = 5⋅9 + 2
2 = 47 + 5⋅(-9)
= 47 + (240 + 47⋅(-5))⋅(-9)
= 47 + 240⋅(-9) + 47⋅45
= 240⋅(-9) + 47⋅46
5 = 2⋅2 + 1
1 = 5 + 2⋅(-2)
= (240 + 47⋅(-5))+(240⋅(-9) + 47⋅46)⋅(-2)
= 240 + 47⋅(-5) + 240⋅18 + 47⋅(-92)
= 240⋅19 + 47⋅(-97)
1 = 240⋅19 + 47⋅(-97)
x = 19 , y= -97

확장된 유클리드 호제법을 이용하여 (결국 유클리드 호제법에 의한 반복적인 장난질..?에 불과하지만) x, y를 구하는 방법을 알았으니 이걸 그대로 코드로 옮겨봅시다.

다만, 조금 순서가 복잡하여 앞에 왜 이러한 코드가 나왔는지 적은 식을 순서대로 주석에 달았습니다.

암호화와 복호화

고속 누승 알고리즘

암호화 방법은 다음과 같습니다

c = m^e mod n

복호화 방법은 다음과 같구요.

m = c^d mod n

여기서 c는 복호화된 값. m은 암호화 전의 값이라고 보면 됩니다.

그런데 여기서 문제되는건 m^e 혹은 c^d 입니다.

이 수들은 너무 커서 오버플로우가 일어나고 우선적으로 그냥 계산시 속도가 너무 느립니다.

이를 위해 두가지 방법을 병행합니다.

우선 계산횟수를 줄이는것. 알고리즘적 용어로는 O(n)을 최적화 하는건데.. 지금은 저도 잘 모르기에 그냥 계산횟수를 줄인다고 말하겠습니다~

예시로 3³⁰을 들겠습니다.

  • 3³⁰ 은 29번 곱
  • (3¹⁵)²은 15번 곱
  • ((3⁷)²*3)²은 9번 곱
  • (((3³)²*3)²*3)²은 7번 곱
  • (((3²*3)²*3)²*3)²은 7번 곱

이런 방법으로 점점 게산횟수를 줄입니다.

그럼 오버플로우를 해결하는 방법은?

모듈러의 성질을 이용합니다.

  • (a + b) mod n = (a mod n + b mod n)mod n
  • (a — b) mod n = (a mod n — b mod n)mod n
  • (a x b) mod n = (a mod n x b mod n)mod n
  • 10n mod n = (10 mod n)n mod n

여기서 3번째 곱셈성질을 이용합니다.

예시로 3³⁰mod5를 들겠습니다.

3³⁰mod5 ≡ (((3²*3)²*3)²*3)²mod5

≡ (((3²mod5 ⋅ 3mod5)² ⋅ 3mod5)² ⋅ 3mod5)²mod5

이렇게 계산된다면 오버플로우 걱정도 줄어들지요.

자 이제 소스입니다.

끝 ^^b