카테고리 없음

한글 카이사르(시저) 암호 암호화/복호화 프로그램 (java)

이륙 2021. 7. 30. 12:12

 암호의 대표격이라 할 수 있는 카이사르 암호. 카이사르 암호는 단어의 각 알파벳을 특정한 숫자만큼 밀어서 표현하는 암호이다. 예를 들어 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();
		}
	}

콘솔 출력 (know 함수)

// 원래 단어, 암호화된 단어를 입력받아 규칙 알아내서 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();
		}
	}

콘솔 출력 (no_know 함수)

 복호화를 선택했었다면 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();
	}

콘솔 출력 (decrypt 함수, 슮보느가 블로그로 해독되었다)

// 암호화, 초성과 종성 배열에서 (원래 단어의 초종성의 위치 + 규칙) 위치의 글자 얻어와 조합 후 출력
	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();
	}

콘솔 출력 (encrypt 함수, 대한민국이 래깒삖눇으로 암호화되었다.)

 이 모든 과정이 끝나면 다시 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();
    }
}

 

 혹시 코드나 글에 문제가 있다면 댓글로 지적해주세요. 질문도 언제나 환영입니다.