[C언어] 추억의 암호학 개론(시저 암호)
[C언어] 추억의 암호학 개론(시저 암호)
어느정도 C언어에 대한 이해를 하게 되었을 때 암호학개론을 배우게 되었네요. 여기에서도 다양한 암호 알고리즘을 배웠었는데 이 학문도 꽤 재밌는 학문으로 기억하네요. 암호학 개론 수업에서 이론을 배울 때 시저 암호 알고리즘을 제일 먼저 배우게 됩니다.
이 시저 암호 알고리즘은 지금와서 보면 너무 단순하고 초등학교 수준의 놀이 같은 알고리즘인데 이 암호문이 나올 당시에는 정말 엄청나고 획기적인 암호문이였을 거에요. 아무튼 이 시저 암호 알고리즘은 너무나도 유명한 알고리즘이지만 과거의 C언어 입문기에 배웠던 추억을 떠올리면 post를 작성해 봅니다.
1. 시저 암호문
시저 암호는 단순합니다. 영어 알파벳을 기준으로 살펴 보면 영어는 26자로 구성되어 있습니다. 이 알파벳이 띠로 구성되어 있고 이곳에 A~Z까지 알파벳이 순차적으로 나열 되었다고 생각해 봅시다. 이 때 각 알파벳을 일정 위치(비밀키)로 이동시켜서 원문(평문)을 변형 시켜서 평문을 다른 사람들이 알아 볼 수 없게 암호문으로 만들게 됩니다.
다시 쉽게 설명하자면 다음 그림과 같습니다.

평문 알파벳 A가 3(비밀키)칸 이동시켜서 암호문 알파벳 D를 만들고 다시 D에서 반대 방향으로 3(비밀키)칸 이동시켜서 복호문 알파벳 A를 만들게 됩니다.
그러면 이 원리를 수학식으로 나타내면 다음과 같습니다.
암호문 = 평문 + 비밀키
복호문 = 암호문 - 비밀키
암호문 = A + 3 => D
복호문 = D - 3 => A
이제 이것을 코딩화 작업을 해 볼까요.
2. 시저 암호 코딩
그냥 단순하게 비밀키로 더하기, 빼기만 하면 될 듯 싶지만 코딩화에는 고려 할 부분이 있어서 코딩이 길어 집니다. 알파벳에는 대문자와 소문자로 구성되어 있습니다. 자신이 쓴 알파벳이 대문자인지 소문자 인지 구분해야 합니다.
대소문자 아스키코드값으로 살펴보기

아스키코드표를 살펴보면 되는데 간단히 위 코드값만 알면 됩니다. A~Z까지가 65~90까지이고 a~z까지가 97~122까지 입니다. 알파벳은 총 26자로 구성되어 있고요.
위 조건을 다 고려해서 암호문을 만들어야 합니다. 그러면 암호문은 식을 만들어 볼까요.
암호문 = (평문-'a'+3)%26 + 'a'; //소문자
암호문 = (평문-'A'+3)%26 + 'A'; //대문자
위와 같이 식을 세울 수 있습니다. 우선 알파벳 26글자 안에서 몇칸 이동하는지 구해야 합니다.
소문자의 경우를 살펴보면 다음과 같습니다.
평문-'a'+3 하면 어떻게 될까요.
평문 'c' 일 경우 아스키 코드값은 '99'이고 'a'은 아스키코드 값이 97입니다. 계산하면 'c' 알파벳의 위치는 2이 됩니다. 거기에 3을 더하니 5이 됩니다. 이 말은 'a'가 0일 때 순차적으로 'c'의 위치는 2가 되고 거기에 3을 더하면 5의 위치는 'f'가 됩니다.
여기서, 평문에 'a'을 빼준 이유는 이동칸을 구하기 위해서는 'a'의 알파벳의 아스키코드값이 97인데 여기서 97값을 기준으로 계산하면 26으로 나눌때 문제가 생깁니다. 그래서 'a'의 값을 0으로 맞추기 위해서 'a'을 빼주면 97으로 빼주는 효과로 알파벳 'a'가 0의 값으로 가정 할 수 있습니다. 이동 시키고 나서 알파벳을 출력 할 때는 다시 원상태 아스키코드값으로 'a'을 더하게 됩니다. 대문자의 경우는 'A'을 더하고 빼주면 되겠죠.
그리고, 위 식에서 26으로 나눈 나머지(%)라고 추가로 표현 되었는데 이말은 알파벳이 26글자로 원형 띠로 연결되어 있고 26으로 나눈 나머지라 했으니 26보다 큰면 26글자 한바퀴 도는 것은 의미가 없기 때문에 26보다 클 경우 나머지 값으로 최종 이동 위치의 알파벳을 구하게 됩니다.
그러면, 대소문자을 고려한 식으로 재구성하면 다음과 같습니다.
[암호문]
if(ch>='a' && ch<='z'){
enc = (ch-'a'+3)%26 + 'a';
}
else if(ch>='A' && ch<='Z'){
enc = (ch-'A'+3)%26 + 'A';
}
[복호문]
if(ch>='a' && ch<='z'){
dec = (enc-'a'-3)%26 + 'a';
}
else if(ch>='A' && ch<='Z'){
dec = (enc-'A'-3)%26 + 'A';
}
3. 코딩 실행
#include <stdio.h>
int main(void) {
char ch = 'c';
int k = 3;
char enc, dec;
printf("a = %d A = %d \n",'a','A');
printf("평문자 : %c %d \n",ch,ch);
//암호화
if(ch>='a' && ch<='z'){
enc = (ch-'a'+3)%26 + 'a';
}
else if(ch>='A' && ch<='Z'){
enc = (ch-'A'+3)%26 + 'A';
}
printf("암호문자 : %c %d\n",enc,enc);
//복호화
if(ch>='a' && ch<='z'){
dec = (enc-'a'-3)%26 + 'a';
}
else if(ch>='A' && ch<='Z'){
dec = (enc-'A'-3)%26 + 'A';
}
printf("복호문자 : %c %d\n",dec,dec);
}
[결과]

마무리
위 코딩은 알파벳 한글자만 했는데 scanf()문으로 해서 문장을 받아서 해당 문장을 암호화 할 수 있습니다. 더 나아가 파일을 읽어와 암호화 할 수 있는데 그 부분은 여러분들이 해보세요. 잘 모르면 구글링 검색을 통해서 찾아보시면 쉽게 찾을 수 있을 거에요.
원래 찾은 자료는 seed 암호 였는데 예전 암호학 개론을 배우면서 중간에 seed 암호화 코딩을 과제로 내줬는데 그렇게 어렵지 않게 코딩 한 것 같은데 지금와서 제가 만든 seed 암호소스가 복잡하고 머리가 아파 오네요. 어떻게 이걸 코딩했는지 이제는 감이 떨어졌는데 봐도 해독하는데 시간이 좀 걸릴 것 같아서 해독은 포기했네요. 아무튼 과제물 내준 하루만에 새벽까지 머리 싸매고 코딩했던 기억이 나네요. 학창시절에는 코딩이라면 분야 영역은 막론하고 스펀지 처럼 다양하게 흡수했는데 지금은 어떤 영역은 봐도 모르겠고 어렵고 그러네요.
짱짱맨 호출에 응답하였습니다.
짱짱맨 방문 환영해요
좋은날되셔요~~^^
marsswim님도 즐거운 하루 되세요.
럭키맨 호출에 응답하였습니다. 스팀엔진에서 5000스달 확인하세요 :)
감사해요