고전암호

고전암호(Classical cipher)는 과거에 사용되었으나 지금은 사용하지 않는 암호의 형태이다. 지금은 컴퓨터로 비트를 다루는 암호를 사용하지만 과거에는 사람이 암호화와 복호화를 해야했기 때문에 고전암호는 오늘날의 암호보다 훨씬 단순하다. 

고전암호에서 가장 대표적인 스키테일 암호, 시저 암호, 비즈네르 암호에 대해서 알아보자.

스키테일 암호(Scytale cipher)

고대 그리스의 역사학자 플루타르크에 따르면 약 2,500년 전 그리스 지역의 옛 나라인 스파르타에서는 전쟁터에 나가있는 군대에 비밀메시지를 전할 때 암호를 사용했다. 이들의 암호는 세월이 흐른 후 오늘날의 시각에서 보면 민간인도 금세 알아차릴수 있을 정도로 매우 간단하다. 하지만 그때 당시로서는 아무나 쉽게 암호문을 열어 볼수 없는, 아주 교묘하고도 획기적인 방법이었다. 이때 사용한 암호화방법은 스키테일(Scytale)암호라 하고 당시의 전쟁터에선 다음과 같은 방법으로 암호화하였다.

스키테일, 그리스어로 바톤이라는 뜻이다.

  1. 전쟁터에 나갈 군대와 본국에 남아있는 정부는 각자, 스키테일(Scytale)이라고 하는 굵기의 원통형 막대기를 나누어 갖는다.
  2. 비밀리에 보내야 할 메시지가 생기면, 본국 정부의 암호 담당자는 스키테일에 가느다란 양피지 리본을 위에서 아래로 감은 다음 옆으로 메시지를 적는다.
  3. 리본을 풀어내어 펼치면 메시지의 내용은 아무나 읽을 수 없게 된다.
  4. 전쟁터에 나가있는 오로지 같은 굵기의 원통막대기를 가진 사람만이 메시지를 읽을 수 있다.

 

시저 암호(Caesar cipher)

로마의 황제 율리우스 카이사르의 이름을 따서 만든 암호이다. 그렇기 때문에 카이사르 암호 또는 시저 암호라고 한다. 카이사르가 써서 유명해진 것이지, 그보다 앞서서 사용한 사람들이 있다고 한다. 시저 암호는 암호화하고자 하는 내용을 일정한 거리만큼 밀어서 다른 알파벳으로 치환하는 방식이다.  예를 들어 3글자씩 밀어내는 카이사르 암호로 ‘COME TO ROME’을 암호화하면 ‘FRPH WR URPH’가 된다. 여기서 밀어내는 글자 수는 암호를 보내는 사람과 함께 정해 더 어려운 암호를 만들 수 있다.

코틀린으로 작성한 시저 알고리즘은 다음과 같다.

class CaesarCipher(private val shiftCount: Int = 3) : Cipher {
    override fun encrypt(plainText: String): String {
        var result = StringBuffer()
        for (i in plainText.indices) {
            var char: Char = plainText.elementAt(i)
            if (Character.isLowerCase(char)) {
                char = 'a' + (char - 'a' + shiftCount) % 26
            } else if (Character.isUpperCase(char)) {
                char = 'A' + (char - 'A' + shiftCount) % 26
            }
            result.append(char)
        }
        return result.toString()
    }

    override fun decrypt(cipherText: String): String {
        var result = StringBuffer()
        for (i in cipherText.indices) {
            var char: Char = cipherText.elementAt(i)
            if (Character.isLowerCase(char)) {
                char = 'z' + (char - 'z' - shiftCount) % 26
            } else if (Character.isUpperCase(char)) {
                char = 'Z' + (char - 'Z' - shiftCount) % 26
            }
            result.append(char)
        }
        return result.toString()
    }
}

하지만 시저암호는 빈도 분석법을 통해 쉽게 풀 수 있다는 단점이 있다.

여담이지만, 실제로 카이사르는 회의 참석전에 RUSQHUVKBVEHQIIQIYDQJEH이라는 암호를 받았고, 해독하면 암살자를 조심해라(BECAREFULFORASSASINATOR)라는 뜻이 된다. 하지만 암살자가 누군지는 알 수 없었고 회의에 참석하는 것을 강행했다. 결국 믿었던 브루투스에게 칼을 맞게 되고 이때 그 유명한 “브루투스 너마저..” 라는 말을 남기고 죽습니다. 실제로 한 말이 아니라는게 정설이지만 워낙 유명한 세익스피어가 쓴 희곡 “줄리어스 시저”의 대사다.

비즈네르 암호(Vigenere cipher)

시저암호를 개선한 암호가 비즈네르 암호다. 16세기에 지오반 바티스타 벨라소라는 이태리 사람이 만들었다. 비즈네르 암호는 시저 암호랑 비슷하지만 글자를 무조건 3칸 이동 하는 것이 아니라 문자열을 키로 갖는다. 예를들어 CAN이라는 키가 있다면 각 알파벳의 순서는 C(2) , A(0), N(13) 이므로 평문의 글자들을 순서대로 순환하며 2,0,13 자리 이동하게 된다. 

만약 Hello라는 문자열에 CAN이라는 Key를 적용한 비즈네르 암호화를 적용하면 Jeyno라는 문자열이 도출된다.

암호화와 복호화를 위해 보통 다음과 같은 비즈네르표를 사용한다.

비즈네르 암호화 알고리즘을 코틀린으로 작성하면 다음과 같다.

class VigenereCipher(private val key: String) : Cipher {
    override fun encrypt(plainText: String): String {
        val result = StringBuffer()
        var keyCharIndex = 0
        for (i in plainText.indices) {
            var char = plainText[i]
            if (Character.isLetter(char)) {
                char = if (Character.isUpperCase(char)) {
                    val shiftCount = key.elementAt(keyCharIndex).toUpperCase().toInt()
                    ((char.toInt() + shiftCount - (2 * 'A'.toInt())) % 26 + 'A'.toInt()).toChar()
                } else {
                    val shiftCount = key.elementAt(keyCharIndex).toLowerCase().toInt()
                    ((char.toInt() + shiftCount - (2 * 'a'.toInt())) % 26 + 'a'.toInt()).toChar()
                }
                keyCharIndex = ++keyCharIndex % key.length
            }
            result.append(char)
        }
        return result.toString()
    }

    override fun decrypt(cipherText: String): String {
        val result = StringBuffer()
        var keyCharIndex = 0
        for (i in cipherText.indices) {
            var char: Char = cipherText[i]
            if (Character.isLetter(char)) {
                char = if (Character.isUpperCase(char)) {
                    val shiftCount = key.elementAt(keyCharIndex).toUpperCase().toInt()
                    'A' + ((char - shiftCount + 26).toInt() % 26)
                } else {
                    val shiftCount = key.elementAt(keyCharIndex).toLowerCase().toInt()
                    'a' + ((char - shiftCount + 26).toInt() % 26)
                }
                keyCharIndex = ++keyCharIndex % key.length
            }
            result.append(char)
        }
        return result.toString()
    }

}

비즈네르 암호가 시저 암호보다 훨씬 안전하지만 그래도 암호를 깨끼는 쉽다. 암호화된 문자열에서 등장하는 반복되는 문자열을 통해 키의 개수를 추론하고, 그 이후 빈도분석을 통해 실제 키를 확정할 수 있다. 암호로서는 비교적 약한 편이지만 그 시절에 사용하기에는 보안성이 충분했다.

 

카테고리: 미분류

0개의 댓글

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.