0. 서론
고전 암호는 송신자와 수신자간의 공통된 키를 이용하여 통신을 진행하였습니다. 이런 암호를 대칭키 암호 혹은 관용 암호라고 부르며, 암호화 복호화 과정을 거치기 전에 키를 공유하는 통신 과정이 필수적이었습니다. 이번 글에서는 이런 고전 암호들에 대해서 배워보도록 하겠습니다.
1. 대치 암호
1-1. Caesar cipher - 카이사르 암호
현재 발견된 암호중 가장 오래된 암호이며, 쥴리어스 시저라는 사람의 이름에서 딴 암호입니다. B.C. 10044에 전쟁에서 사용된 것으로 알려진 이 암호는 영어 문자에 번호를 025까지 부여하여 이 문자를 비밀 키와 조합하여 법 연산을 하는 것으로 암호화 복호화를 진행합니다.
| a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v | w | x | y | z |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
#include <stdio.h>
#include <string.h>
void CRYPTO(char* str, int str_size, int key) {
if ('A' <= str[i] && str[i] <='Z') {
for(int i = 0; i < str_size; i++)
str[i] = 'A' + (str[i] - 'A' + key) % 26;
} else if('a' <= str[i] && str[i] <= 'z') {
for(int i = 0; i < str_size; i++)
str[i] = 'a' + (str[i] - 'a' + key) % 26;
}
}
void DECRYPTO(char* str, int str_size, int key) {
if (str[i] >= 'A' && str[i] <='Z') {
for(int i = 0; i < str_size; i++)
str[i] = 'Z' - ((str[i] ) - key) % 26;
} else if(str[i] >= 'a' && str[i] <= 'z') {
for(int i = 0; i < str_size; i++)
str[i] = 'z' - ((str[i] ) - key) % 26;
}
}
int main(void) {
key = 0, str_size = 0;
char str[50] = {0,};
printf("평문 또는 암호문 을 입력하시오 : ");
gets(str);
printf("키 값을 입력하시오(0~25) : ");
scanf("%d", &key);
str_size = strlen(str);
printf("암호화된 문자열 : %s\n", CRYPTO(str, str_size, key));
pritnf("복호화된 문자열 : %s\n", DECRYPTO(str, str_size, key));
}Quize 1
int main() {
int key = 1;
char plain_text = 'A';
printf("%c", 'A' + ((plain_text - 'A' + key) % 26));
return 0;
}
int main() {
int key = 1;
char plain_text = 'A';
printf("%c", 'A' + ((plain_text - 'A' + key) % 25));
return 0;
}문제 1. 다음 두 코드를 보고 위 코드와 아래 코드중 정상적으로 실행되는 코드를 찾고 정상적으로 실행되지 않는 코드가 성립하지 않는 이유를 찾으시오.
- 생각 :
'A' + ((plain_text - 'A' + key) % 25)에서((plain_text - 'A' + key) % 25)의 코드는 최종적으로 ‘A’ 문자에서 몇을 더 이동해야 하는지를 나타내는 코드임. 즉 평문이 ‘B’ 이고 키 값이 2일때 우리는 Z에서 2번 더 이동한 ‘B’라고 생각한다면 해당 코드에서는 결국 A에서 1번 더 이동한 값 이렇게 계산 한다는 의미. 이때 ‘A’에서 몇번을 더 이동해야하는 가를 나타내는 코드는 025의 숫자를 반환해줘야함. 왜냐하면 A는 우리의 숫자로는 0이라고 규명했지만 실제로는 A또한 하나의 문자이므로 1개로 쳐지며 알바펫은 총 26자 이므로 우리는 025까지의 값들이 뒤 소괄호 코드에서 반환되어야함. 이 떄문에 % 25는 될수 없음.
Notify
> 해당 문제는 코드 해석의 다양성을 보여주기 위해서 만들었습니다.
> 위 문제의 난이도를 위해서 하나는 틀리고 하나는 맞는 코드로 만들었지만 두 코드의 의도를 파악한다면 둘가 가능한 코드라는 것을 알수 있습니다.
> - 첫번째 코드는 'A'라는 문자에서 최종적으로 몇번을 더 이동하면 되는가를 계산하는 코드입니다. 즉 "Z에서 3번 더 간다" 이렇게 생각하는 것이 아닌 "결국 Z에서 3번 더 가는 건 'A에서 몇번을 더 가면 되는데?"를 말하는 코드였습니다.
> - 두번째 코드 숫자와 알파벳의 매칭으로 암호화하는 코드였습니다. 최종적으로 Z에서 2번 X에서 3번 이렇게가 아닌 결국 숫자 0인가 1인가를 한번에 구하고 싶었던 코드였습니다. 하지만 두번째 코드는 24 숫자 밖에 못 나오게 만들었기 떄문에 해당 의도는 파악하기 힘드셨을거라 생각합니다.
문제 2. 틀린 코드에 대해서 정답 코드와 다른 방식으로 틀린코드가 정상적으로 실행될수 있는 코드를 작성하시오.
int main() {
int key = 1;
char plain_text = 'A';
printf("%c", 'A' + (plain_text - 'A' + key) % 25);
}1-2. monoalphabetic substitution cipher - 단일 대칭 암호
단일 대칭 암호는 하나의 문자에 대한 특정 문자를 1대1로 대칭 시킨 암호문 입니다. 해당 말이 이해가 잘 안가신다면 표를 보십시오.
| A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| D | K | V | Q | F | I | B | J | W | P | E | S | C | X | H | T | M | Y | A | U | O | L | R | G | Z | N |
#include <stdio.h>
#include <string.h>
int main() {
char str[50], key_table[26];
printf("키 테이블을 입력해 주세요.(1개씩 입력 가능하며, A를 기준으로 합니다. 소문자만 가능)");
for (int i = 0; i < 26; i++) {
printf("%c 매칭 알파벳 : ", 'a' + i);
scanf("%c", &key_table[i]);
}
printf("평문을 입력해주세요.");
gets(str);
for(int i = 0; i<strlen(str); i++){
str[i] =
}
}해당 암호는 brute force attack 공격을 통해서 쉽게 기밀성을 깰수 있습니다.
다음과 같은 경우의 수 안에서 복호화가 가능합니다. 그 이유는 각 표의 1칸당 1개의 문자가 매칭 됩니다. 따라서 이를 경우의 수로 계산한다면
#include <stdio.h>
#include <string.h>
int main() {
}1-3. Vigenere - 비즈네르 암호
단일 문자 대치 암호는 하나의 평문 문자랑 하나의 다른 문자를 1대1 매칭하는 형식이였습니다. 다중 문자 대치 암호에서는 하나의 평문 문자가 여러 개의 암호 문자로 대치될 수 있습니다. 이 원리는 기존의대칭 암호에서 각 평문의 문자 위치에 따라서 대칭되는 암호가 달라지는 시스템입니다. 이런 다중 대칭 암호의 대표적인 알고리즘이 Vigenere 암호입니다.
암호 방식은 주기 d를 갖고 다중 대치 암호에서 비밀 키는 (k1,k2…,kd)라 합니다. 또한 평문 M = m1,m2,m..md,md+1,md+2에 대하여 암호화 과정을 반복 적용합니다.
암호화할 때는 아래와 같은 비즈네르 표(Vigenère square)를 사용합니다. 맨 위 행이 평문 문자, 맨 왼쪽 열이 비밀 키 문자이며, 두 값이 만나는 칸이 암호문 문자가 됩니다.
| A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| A | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
| B | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | A |
| C | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | A | B |
| D | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | A | B | C |
| E | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | A | B | C | D |
| F | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | A | B | C | D | E |
| G | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | A | B | C | D | E | F |
| H | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | A | B | C | D | E | F | G |
| I | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | A | B | C | D | E | F | G | H |
| J | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | A | B | C | D | E | F | G | H | I |
| K | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | A | B | C | D | E | F | G | H | I | J |
| L | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | A | B | C | D | E | F | G | H | I | J | K |
| M | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | A | B | C | D | E | F | G | H | I | J | K | L |
| N | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | A | B | C | D | E | F | G | H | I | J | K | L | M |
| O | O | P | Q | R | S | T | U | V | W | X | Y | Z | A | B | C | D | E | F | G | H | I | J | K | L | M | N |
| P | P | Q | R | S | T | U | V | W | X | Y | Z | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O |
| Q | Q | R | S | T | U | V | W | X | Y | Z | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P |
| R | R | S | T | U | V | W | X | Y | Z | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q |
| S | S | T | U | V | W | X | Y | Z | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R |
| T | T | U | V | W | X | Y | Z | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S |
| U | U | V | W | X | Y | Z | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T |
| V | V | W | X | Y | Z | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U |
| W | W | X | Y | Z | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V |
| X | X | Y | Z | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W |
| Y | Y | Z | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X |
| Z | Z | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y |
예를 들어 평문 문자가 t(T)이고 비밀 키 문자가 s(S)라면, S행과 T열이 만나는 칸을 찾으면 암호문 문자 L이 나옵니다. 아래 예시의 첫 글자(t → l)와 일치하는 것을 확인할 수 있습니다.
| 평문 | t | h | i | s | c | r | y | p | t | o | s | y | s | t | e | m | i | s | n | o | t | s | e | c | u |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 비밀 키 수열 | s | e | c | u | r | i | t | y | s | e | c | u | r | i | t | y | s | e | c | u | r | i | t | y | s |
| 암호문 | l | l | k | m | t | z | r | n | l | s | u | s | j | b | x | k | a | w | p | i | k | a | x | a | m |
#include <stdio.h>
#include <string.h>
void main() {
int j=0, key_size = 0, str_size = 0, select = 1;
char str[50] = {0,}, key[16] = {0,};
printf("암호문 또는 평문을 입력 : ");
gets(str);
printf("암호는 1번 복호는 2번 선택 : ");
scanf("%d", &select);
while(getchar() != '\n') ;
printf("키 값 입력(소문자만 : ");
gets(key);
str_size = strlen(str);
key_size = strlen(key);
for(int i = 0; i<str_size; i++){
j = i % key_size;
if(select == 1) {
if((str[i] > 'a') && (str[i] <= 'z')) {
str[i] -= 'a'; key[j] -= 'a';
if((str[i] + key[j]) < 0)
str[i] += 26;
str[i] = (str[i] + key[i]) % 26;
str[i] += 'a'; key[j] -= 'a';
}
if((str[i] >= 'A') && (str[i] <= 'Z')) {
str[i] -= 'A'; key[j] -= 'a';
if(str[i] + key[j] < 0)
str[i] += 26;
str[i] = (str[i] + key[j]) % 26
str[i] += 'A'; key[j] += 'a';
}
}
if(select == 2) {
if((str[i] >= 'a') && (str[i] <= 'z')) {
str[i] -= 'a'; key[j] -= 'a';
if(str[i] - key[j] < 0 )
str[i] += 26;
str[i] = (str[i] - key[j]) % 26;
str[i] += 'a'; key[j] += 'a';
}
if((str[i] >= 'A') && (str[j] <= 'Z')) {
str[i] -= 'A'; key[j] -= 'a';
if((str[i] - key[j]) < 0)
str[i] += 26;
str[i] = (str[i] - key[j]) % 26;
str[i] += 'A'; key[j] += 'a';
}
}
}
printf("\n 암호화 또는 복호화된 결과 출력 : ");
puts(str);
}2. 전치 암호
전치 암호는 평문 문자의 순서를 어떤 특정한 절차에 따라서 재배치하는 암호 방식입니다.
| 암호문 위치 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 평문 위치 | 3 | 5 | 1 | 6 | 4 | 2 |
해당 표를 보면 쉽게 이해할수 있습니다. 이렇게 문자의 위치를 바꿔 암호화 하는 방식을 전치 암호라고 합니다.
| 평문 | i | n | f | o | m | a | t | i | o | n | s |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 암호문 | f | r | i | m | o | n | n | a | s | o | t |
#include <stdio.h>
#include <string.h>
void main() {
int i, j, size, block_num;
int key[64] = {3, 5, 1, 6, 4, 2};
char p_text[64], c_text[64], d_text[64];
pritnf(" 평문 입력 : ");
scanf("%s", p_text);
size = strlen(p_text);
if(size % BLOCK_SIZE > 0) {
block_num = strlen(p_text) / BLOCK_SIZE + 1;
for(i = strlen(p_text); i < block_num*BLOCK_SIZE; i++)
p_text[i] = 'x';
}
else block_num = strlen(p_text)/ BLOCK_SIZE;
for(i = 0; i < block_num;i++)
for(j=0;j<BLOCK_SIZE;j++)
c_text[i*BLOCK_SIZE+j] = p_text[(key[i] - 1) + i*BLOCK_SIZE];
printf(" 암호문 : ");
for(i = 0; i<block_num*BLOCK_SIZE;i++)
printf("%c ", c_text[i]);
printf("\n");
for(i=0;i<block_num;i++) {
for(j=0;j<BLOCK_SIZE;j++)
d_text[(key[j]-1)+i*BLOCK_SIZE] = c_text[i*BLOCK_SIZE+j];
}
printf(" 복호화 : ");
for(i=0;i<size;i++)
printf("%c ",d_text[i]);
printf("\n");
}