암호의 대표격이라 할 수 있는 카이사르 암호. 카이사르 암호는 단어의 각 알파벳을 특정한 숫자만큼 밀어서 표현하는 암호이다. 예를 들어 apple의 경우 3만큼 민다고 하였을 때 a는 d로, p는 s로, l은 o로, e는 h로 바뀌어 dssoh로 나타나게 된다. 이런 카이사르 암호를 한글 버전으로 구현해보았다.
이 코드를 실행하게 되면 가장 먼저 복호화(암호를 풀기)를 할 것인지, 암호화를 할 것인지 묻는다.
int issolve;
// 복호화, 암호화 여부 선택
public void IsSolve () {
System.out.println("복호화 1 / 암호화 0");
issolve = sc.nextInt();
if (!(issolve == 1 || issolve == 0)) {
System.out.println("잘못 입력하셨습니다. 다시 입력해주세요.");
IsSolve();
}
KeyKnowCheck ();
}

그런 다음, 글자를 미는 횟수인 규칙(key)을 알고 있는지 묻고 그 여부에 따라 알고 있으면 know(), 모르면 no_know() 함수를 호출한다.
// 키를 알고 있는지 체크, 알고 있으면 know(), 모르면 no_know() 호출
public void KeyKnowCheck () {
int key;
System.out.println("키를 알고 계십니까? (알고 있다면 1 / 모르면 0)");
key = sc.nextInt();
if (key == 1) {
know();
} else if (key == 0) {
no_know ();
} else {
System.out.println("다시 입력 해주세요.");
KeyKnowCheck();
}
}

know 함수에서는 규칙을 물은 후 dis 에 저장한 후 아까 저장한 복호화/암호화 여부에 따라 각 함수를 호출한다. no_know 함수에서는 원래 단어와 그 단어를 암호화한 단어를 입력 받아 규칙을 계산해 dis에 저장한다. 만약 두 단어의 길이가 다르거나 각 글자 마다의 규칙이 다를 경우 다시 입력받는다.
// 규칙 입력받아 dis 에 저장
public void know () {
System.out.println("키를 입력해주세요.");
dis = sc.nextInt ();
if (issolve == 0) {
encrypt();
} else {
decrypt();
}
}

// 원래 단어, 암호화된 단어를 입력받아 규칙 알아내서 dis에 저장, 길이가 맞지 않거나 규칙이 성립하지 않으면 재호출
public void no_know () {
String str;
ArrayList<Character> ori_word = new ArrayList<>();
ArrayList<Character> enc_word = new ArrayList<>();
System.out.println("원래 단어를 입력해주세요.");
str = sc.next();
Set (str);
for (int i=0;i<fir.size();i++) {
ori_word.add(fir.get(i));
}
original = Arrays.binarySearch(first, ori_word.get(0));
System.out.println("암호화된 단어를 입력해주세요.");
str = sc.next();
Set (str);
for (int i=0;i<fir.size();i++) {
enc_word.add(fir.get(i));
}
enc = Arrays.binarySearch(first, enc_word.get(0));
if (ori_word.size() != enc_word.size()) {
System.out.println("입력하신 두 단어의 길이가 같지 않습니다.");
no_know();
}
int check = 0;
if (enc >= original) {
dis = enc - original;
} else {
dis = 19 - (original - enc);
}
for (int i=0;i<ori_word.size()-1;i++) {
original = Arrays.binarySearch(first, ori_word.get(i+1));
enc = Arrays.binarySearch(first, enc_word.get(i+1));
if (enc >= original) {
check = enc - original;
} else {
check = 19 - (original - enc);
}
if (check != dis) {
System.out.println ("입력하신 두 단어는 부적절합니다.");
no_know();
}
}
if (issolve == 0) {
encrypt();
} else {
decrypt();
}
}

복호화를 선택했었다면 decrypt 함수가, 암호화를 선택했었다면 encrypt 함수가 실행된다.
// 복호화
public void decrypt () {
spell.clear();
String str;
System.out.println("해독할 단어를 입력해주세요.");
str = sc.next();
Set(str);
size = str.length();
for (int i=0; i<size; i++) {
int a;
int c;
if (Arrays.binarySearch(first, fir.get(i)) - dis < 0) {
a = Arrays.binarySearch(first, fir.get(i)) - dis + 19;
} else {
a = Arrays.binarySearch(first, fir.get(i)) - dis;
}
if (las.get(i) != ' ') {
if (Arrays.binarySearch(last, las.get(i)) - dis < 0) {
c = Arrays.binarySearch(last, las.get(i)) - dis + 28;
} else {
c = Arrays.binarySearch(last, las.get(i)) - dis;
}
} else {
c = Arrays.binarySearch(last, las.get(i));
}
int b = Arrays.binarySearch(middle, mid.get(i));
spell.add(new String( combine (new int[] {a, b, c})));
}
End = String.join("", spell);
System.out.println(End);
IsSolve();
}

// 암호화, 초성과 종성 배열에서 (원래 단어의 초종성의 위치 + 규칙) 위치의 글자 얻어와 조합 후 출력
public void encrypt () {
spell.clear();
String str;
System.out.println("암호화할 단어를 입력해주세요.");
str = sc.next();
Set(str);
size = str.length();
for (int i=0; i<size; i++) {
int a;
int c;
if (Arrays.binarySearch(first, fir.get(i)) + dis > 18) {
a = Arrays.binarySearch(first, fir.get(i)) + dis - 19;
} else {
a = Arrays.binarySearch(first, fir.get(i)) + dis;
}
if (las.get(i) != ' ') {
if (Arrays.binarySearch(last, las.get(i)) + dis > 27) {
c = Arrays.binarySearch(last, las.get(i)) + dis - 28;
} else {
c = Arrays.binarySearch(last, las.get(i)) + dis;
}
} else {
c = Arrays.binarySearch(last, las.get(i));
}
int b = Arrays.binarySearch(middle, mid.get(i));
spell.add(new String( combine (new int[] {a, b, c})));
}
End = String.join("", spell);
System.out.println(End);
IsSolve();
}

이 모든 과정이 끝나면 다시 IsSolve 함수를 호출한다. 아래는 이 코드에 사용된 초중종성 배열과 글자 결함 함수, 분해 함수이다.
final char[] first = {'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ',
'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'};
final char[] middle = {'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ',
'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ',
'ㅢ', 'ㅣ'};
final char[] last = {' ', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ',
'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ',
'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'};
// 초중종성 위치 설정
public int[] split(char c){
int sub[] = new int[3];
sub[0] = (c - 0xAC00) / (21*28); //초성의 위치
sub[1] = ((c - 0xAC00) % (21*28)) / 28; //중성의 위치
sub[2] = (c -0xAC00) % (28);//종성의 위치
return sub;
}
// 초중종성 결합
public char[] combine(int[] sub){
char[] ch = new char[1];
ch[0] = (char) (0xAC00 + (sub[0]*21*28) + (sub[1]*28) + sub[2]);
return ch;
}
전체 코드이다.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
import javax.print.DocFlavor.STRING;
public class CaesarCipher {
Scanner sc = new Scanner(System.in);
final char[] first = {'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ',
'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'};
final char[] middle = {'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ',
'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ',
'ㅢ', 'ㅣ'};
final char[] last = {' ', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ',
'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ',
'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'};
// 초중종성 위치 설정
public int[] split(char c){
int sub[] = new int[3];
sub[0] = (c - 0xAC00) / (21*28); //초성의 위치
sub[1] = ((c - 0xAC00) % (21*28)) / 28; //중성의 위치
sub[2] = (c -0xAC00) % (28);//종성의 위치
return sub;
}
// 초중종성 결합
public char[] combine(int[] sub){
char[] ch = new char[1];
ch[0] = (char) (0xAC00 + (sub[0]*21*28) + (sub[1]*28) + sub[2]);
return ch;
}
ArrayList<Character> fir = new ArrayList<>();
ArrayList<Character> mid = new ArrayList<>();
ArrayList<Character> las = new ArrayList<>();
ArrayList<String> spell = new ArrayList<>();
String End;
// 초중종성 분해 후 fir, mid, las 배열에 저장
public String Set (String str) {
fir.clear();
mid.clear();
las.clear();
int[] x = null;
int loop = str.length();
char c;
for( int i = 0; i < loop; i++ ){
c = str.charAt( i );
if( c >= 0xAC00 ){
x = split( c );
String arr[] = str.split(" ", 1);
char fir2 = first[x[0]];
char mid2 = middle [x[1]];
char las2 = last [x[2]];
fir.add(fir2);
mid.add(mid2);
las.add(las2);
}
}
return null;
}
int original;
int enc;
int dis;
int size;
ArrayList<Character> result = new ArrayList<>();
// 암호화, 초성과 종성 배열에서 (원래 단어의 초종성의 위치 + 규칙) 위치의 글자 얻어와 조합 후 출력
public void encrypt () {
spell.clear();
String str;
System.out.println("암호화할 단어를 입력해주세요.");
str = sc.next();
Set(str);
size = str.length();
for (int i=0; i<size; i++) {
int a;
int c;
if (Arrays.binarySearch(first, fir.get(i)) + dis > 18) {
a = Arrays.binarySearch(first, fir.get(i)) + dis - 19;
} else {
a = Arrays.binarySearch(first, fir.get(i)) + dis;
}
if (las.get(i) != ' ') {
if (Arrays.binarySearch(last, las.get(i)) + dis > 27) {
c = Arrays.binarySearch(last, las.get(i)) + dis - 28;
} else {
c = Arrays.binarySearch(last, las.get(i)) + dis;
}
} else {
c = Arrays.binarySearch(last, las.get(i));
}
int b = Arrays.binarySearch(middle, mid.get(i));
spell.add(new String( combine (new int[] {a, b, c})));
}
End = String.join("", spell);
System.out.println(End);
IsSolve();
}
// 규칙 입력받아 dis 에 저장
public void know () {
System.out.println("키를 입력해주세요.");
dis = sc.nextInt ();
if (issolve == 0) {
encrypt();
} else {
decrypt();
}
}
// 원래 단어, 암호화된 단어를 입력받아 규칙 알아내서 dis에 저장, 길이가 맞지 않거나 규칙이 성립하지 않으면 재호출
public void no_know () {
String str;
ArrayList<Character> ori_word = new ArrayList<>();
ArrayList<Character> enc_word = new ArrayList<>();
System.out.println("원래 단어를 입력해주세요.");
str = sc.next();
Set (str);
for (int i=0;i<fir.size();i++) {
ori_word.add(fir.get(i));
}
original = Arrays.binarySearch(first, ori_word.get(0));
System.out.println("암호화된 단어를 입력해주세요.");
str = sc.next();
Set (str);
for (int i=0;i<fir.size();i++) {
enc_word.add(fir.get(i));
}
enc = Arrays.binarySearch(first, enc_word.get(0));
if (ori_word.size() != enc_word.size()) {
System.out.println("입력하신 두 단어의 길이가 같지 않습니다.");
no_know();
}
int check = 0;
if (enc >= original) {
dis = enc - original;
} else {
dis = 19 - (original - enc);
}
for (int i=0;i<ori_word.size()-1;i++) {
original = Arrays.binarySearch(first, ori_word.get(i+1));
enc = Arrays.binarySearch(first, enc_word.get(i+1));
if (enc >= original) {
check = enc - original;
} else {
check = 19 - (original - enc);
}
if (check != dis) {
System.out.println ("입력하신 두 단어는 부적절합니다.");
no_know();
}
}
if (issolve == 0) {
encrypt();
} else {
decrypt();
}
}
// 키를 알고 있는지 체크, 알고 있으면 know(), 모르면 no_know() 호출
public void KeyKnowCheck () {
int key;
System.out.println("키를 알고 계십니까? (알고 있다면 1 / 모르면 0)");
key = sc.nextInt();
if (key == 1) {
know();
} else if (key == 0) {
no_know ();
} else {
System.out.println("다시 입력 해주세요.");
KeyKnowCheck();
}
}
int issolve;
// 복호화, 암호화 여부 선택
public void IsSolve () {
System.out.println("복호화 1 / 암호화 0");
issolve = sc.nextInt();
if (!(issolve == 1 || issolve == 0)) {
System.out.println("잘못 입력하셨습니다. 다시 입력해주세요.");
IsSolve();
}
KeyKnowCheck ();
}
// 복호화
public void decrypt () {
spell.clear();
String str;
System.out.println("해독할 단어를 입력해주세요.");
str = sc.next();
Set(str);
size = str.length();
for (int i=0; i<size; i++) {
int a;
int c;
if (Arrays.binarySearch(first, fir.get(i)) - dis < 0) {
a = Arrays.binarySearch(first, fir.get(i)) - dis + 19;
} else {
a = Arrays.binarySearch(first, fir.get(i)) - dis;
}
if (las.get(i) != ' ') {
if (Arrays.binarySearch(last, las.get(i)) - dis < 0) {
c = Arrays.binarySearch(last, las.get(i)) - dis + 28;
} else {
c = Arrays.binarySearch(last, las.get(i)) - dis;
}
} else {
c = Arrays.binarySearch(last, las.get(i));
}
int b = Arrays.binarySearch(middle, mid.get(i));
spell.add(new String( combine (new int[] {a, b, c})));
}
End = String.join("", spell);
System.out.println(End);
IsSolve();
}
// 메인
public static void main (String[] agrs) {
new CaesarCipher().IsSolve();
}
}
혹시 코드나 글에 문제가 있다면 댓글로 지적해주세요. 질문도 언제나 환영입니다.