DES 코드와 많이 달라 진 것은 없다. 다만 makeEnabled()함수를 잘 이해하고 넘어가야한다. 인자로 입력되는 plaintext의 크기가 16바이트 배수가 아닐 경우 문자 '0'을 plaintext 끝에 추가하여 16바이트 배수로 만들고, 이에 대한 정보를 헤더로 구성한 후 결과를 리턴한다.
헤더 크기는 16바이트로 한다. 예로 plaintext를 16바이트 배수로 만들기 위해 추가된 '0'을 개수가 12일 경우 헤더는 다음과 같이 구성된다. header = '12##############'로 총 '#'문자가 총 14개로 구성된다. makeEnabled()로 plaintext를 "헤어+원래 정보+'0'문자열"로 변환한다.
그리고 split 부분 맨 뒤 [0] 인덱스가 무엇을 의미하는 지 몰라서 파이썬 idle에 실행 해보았는데 파이썬 split()함수를 사용하면 다음과 같이 자료형이 list로 반환된다. 애석하기도 이거 하다가 처음 알았음;;;;
split으로 '#'을 구분자로 분리하고, 분리된 값 중 첫 번째를 정수로 변환하여 변수 fillersize에 할당한다.
fillersize는 원래 정보 뒤에 추가한 문자 '0'의 개수이므로 decmsg를 16에서 -fillersize까지 슬라이싱하면 원래 정보가 추출된다.
CBC모드란 블록을 암호화하기 전에 이전 블록의 암호화된 블록과 XOR연산을 한 결과를 새로운 암호키로 해서 블록을 암호화하는 방식이다. 맨 첫 블록은 이전 블록이 없기 때문에 이전 블록 역할을 하는 초기화 벡터가 필요하다.
from Crypto.Cipher import DES3
from Crypto.Hash import SHA256 as SHA
class myDES():
def __init__(self, keytext, ivtext): #keytext는 암호키 생성, ivtext는 초기화 벡터를 위한 문자열
hash = SHA.new() #SHA256객체 만들고 hash에 할당
hash.update(keytext.encode('utf-8')) #SHA256해시 갱신
key = hash.digest() #hash값 추출한후 key에 할당
self.key = key[:24] #24바이트만큼 슬라이싱
hash.update(ivtext.encode('utf-8'))
iv = hash.digest()
self.iv = iv[:8]
def enc(self, plaintext): #암호화
plaintext = make8String(plaintext)
des3 = DES3.new(self.key, DES3.MODE_CBC, self.iv)
encmsg = des3.encrypt(plaintext.encode())
return encmsg
def dec(self, ciphertext): #복호화
des3 = DES3.new(self.key, DES3.MODE_CBC, self.iv)
decmsg = des3.decrypt(ciphertext)
return decmsg
#DES3.new()의 인자는 순서대로 '암호키', '운영모드', '초기화 벡터'순
#운영 모드에 따라서 초기화 벡터가 있을 수도 있고 없을 수도 있다.
def make8String(msg): #암호화하려는 메시지 길이가 8바이트 배수가 아닌 경우 8바이트 배수로
msglen = len(msg)
filler = ''
if msglen%8 != 0:
filler = '0'*(8-msglen%8)
msg += filler
return msg
def main():
keytext = 'keyisimportant'
ivtext = '1234'
msg = 'python3xab'
myCipher = myDES(keytext, ivtext)
ciphered = myCipher.enc(msg)
deciphered = myCipher.dec(ciphered)
print('ORIGINAL:\t%s' %msg)
print('CIPHERED:\t%s' %ciphered)
print('DECIPHERED:\t%s' %deciphered)
main()
myDES클래스에서 keytext인자와 ivtext인자를 utf-8로 인코딩하는 것은 파이썬3에서 UTF-8로 인코딩된 문자열은 문자열이 아니라 이진데이터가 연속적으로 있는 바이트 스트림으로 취급하기 때문이다.
Pycryptodome에서 제공하는 3DES의 크기는 16바이트 또는 24바이트 크기, 초기화 벡터의 크기는 8바이트이므로 암호화하려는 문자열의 배수가 8바이트의 배수여야 한다. 그렇지 않으면 암호화가 되지 않기 때문에 make8String 함수를 만들어 8바이트의 배수가 아닌경우에도 암호화가 되게 한다.
주상 전치 암호는 특정 개수 열로 평문 문장을 배열한 후, 열 방향으로 문장을 읽어 암호문을 구성하는 암호화 방법이다.
여기서 중요한 것은 읽어 들이는 열의 순서
만약 암호키가 'BRAIN'인 경우 암호크 크기는 5이므로 평문 배열은 5열로 구성하고 'B','R','A','I','N'의 상대적인 순서는 2,5,1,3,4이다.
B R A I N
2 5 1 3 4
A B C D E
F G H I J
K L M N O
P Q R S T
각 열에 배열된 문자 개수는 4개이다. 이 숫자를 앞으로 암호 블록 크기라고 부른다.
배열된 문장의 각 열을 읽는 순서는 1열부터 5열까지 각각 2,5,1,3,4이므로, 이 순서로 각 열을 읽어 암호문을 만들면 'CHMR AFKP DINS EJOT BGLQ'로 암호화가 된다. (당연히 생성된 암호문에서 공백을 제거한 후가 최종 암호문)
ENC = 0
DEC = 1
def parseKey(key):
key = key.upper()
tmp = []
for i, k in enumerate(key):
tmp.append((i, k))
tmp = sorted(tmp, key = lambda x: x[1])
enc_table = {}
dec_table = {}
for i, r in enumerate(tmp):
enc_table[r[0]] = i
dec_table[i] = r[0]
return enc_table, dec_table
def transposition(msg, key, mode):
msgsize = len(msg)
keysize = len(key)
ret = ''
filler = ''
if msgsize%keysize != 0: #보다 쉬운 프로그래밍을 위해 평문이 키크기의 배수가 아닐시 배수가 되도록 평문의 끝에 문자 '0'으로 채우기
filler = '0'*(keysize - msgsize%keysize)
msg = msg.upper()
msg += filler
enc_table, dec_table = parseKey(key)
if mode == ENC:
table = enc_table
else:
table = dec_table
if mode == ENC:
buf = ['']*keysize
for i, c in enumerate(msg):
col = i%keysize
index = table[col]
buf[index] += c
for text in buf:
ret += text
else:
blocksize = int(msgsize/keysize)
buf = ['']*keysize
pos = 0
for i in range(keysize):
text = msg[pos:pos+blocksize] #암호문 블록크기만 큼 자름 [0:4], [4:8],..
index = table[i]
buf[index] += text
pos += blocksize
for i in range(blocksize):
for j in range(keysize):
if buf[j][i] != '0':
ret += buf[j][i]
return ret
def main():
key = 'BRAIN'
msg = 'TREASUREBOXISBURRIEDATTWOHUNDREDFEETTONORTHEASTAWAYFROMYOURHOME'
print('Original:\t%s' %msg.upper())
ciphertext = transposition(msg, key, ENC)
print('Ciphered:\t%s' %ciphertext)
deciphertext = transposition(ciphertext, key, DEC)
print('Deciphered:\t%s' %deciphertext)
if __name__ == '__main__':
main()
mod는 (i+k)를 26으로 나눈 나머지이다. 위의 식을 카이사르 문자변환식이라고 한다.
만약 암호키가 C이면 암호키 인덱스는 k=2이다.
그러면 Enc(i) = (i+2)mod26 이 된다.
문자 A의 인덱스 i=0일 때, Enc(i)는 2mod26이므로 값은 2, 2의 인덱스는 C이다. 고로 A는 C로 암호화 되는 것이다.
카이사르 암호 도구 구현하기
ENC = 0
DEC = 1
def makeDisk(key):
keytable = map(lambda x: (chr(x+65), x), range(26))
key2index = {}
for t in keytable:
alphabet, index = t[0], t[1]
key2index[alphabet] = index
if key in key2index: #k는 입력한 키에 해당하는 인덱스
k = key2index[key]
else:
return None, None
enc_disk = {}
dec_disk = {}
for i in range(26):
enc_i = (i+k)%26 #enc_i
enc_ascii = enc_i + 65 #0~25부터 enc_i에 해당하는 아스키 코드
enc_disk[chr(i+65)] = chr(enc_ascii)
dec_disk[chr(enc_ascii)] = chr(i+65)
return enc_disk, dec_disk
def caesar(msg, key, mode):
ret = ''
key = key.upper()
msg = msg.upper()
enc_disk, dec_disk = makeDisk(key)
if enc_disk is None:
return ret
if mode is ENC:
disk = enc_disk
if mode is DEC:
disk = dec_disk
for c in msg:
if c in disk:
ret += disk[c]
else:
ret += c
return ret
def main():
plaintext = 'ilovepython'
key = 'i'
print('Original:\t%s' %plaintext.upper())
ciphertext = caesar(plaintext, key, ENC)
print('Caesar Cipher:\t%s' %ciphertext)
deciphertext = caesar(ciphertext, key, DEC)
print('Deciphered:\t%s' %deciphertext)
if __name__ == '__main__':
main()
def makeCodebook(): #르그랑 코드북 참조
decbook ={'5':'a', '2':'b', '#':'d', '8':'e', '1':'f', '3':'g', '4':'h', '6':'i', '0':'l', '9':'m',\
'*':'n', '%':'o', '=':'p', '(':'r', ')':'s', ';':'t', '?':'u','@':'v',':':'y', '7':' '} #복호화 할때 쓰일 코드북
encbook = {} #암호화 할때 쓰일 코드북
for k in decbook: #debcook의 key와 value 값을 바꾸어서 encbook을 만듦
tmp = decbook[k]
encbook[tmp] = k
return encbook, decbook #반환값 순서대로 encbook, decbook
def encrypt(msg, encbook): #암호화 로직
for c in msg:
if c in encbook:
msg = msg.replace(c, encbook[c])
return msg
def decrypt(msg, decbook): #복화화 로직
for c in msg:
if c in decbook:
msg = msg.replace(c, decbook[c])
return msg
def main():
h = open('plain.txt', 'rt') #plain.txt라는 파일을 텍스트 읽기모드로 염
content = h.read()
h.close()
encbook, decbook = makeCodebook()
content = encrypt(content, encbook)
h = open('encryption.txt', 'wt+') #암호화 한것을 encryption.txt 파일에 텍스트 쓰기모드로 염
h.write(content)
h.close()
if __name__ == '__main__':
main()
암호화 전의 평문 텍스트 파일 plain.txt
For seven days and seven nights
Man will watch this awesome sight.
The tides will rise beyond their ken
To bite away the shores and then
A fiery dragon will cross the sky
Six times before this earth shall die
Mankind will tremble and frightened be
for the sixth heralds in this prophecy.
The great star will burn for seven days,
The cloud will cause two suns to appear
The big mastiff will howl all night
When the great pontiff will change country.
컴포넌트는 Angular의 핵심구성요소로서 Angular 애플리케이션은 컴포넌트를 중심 CBD, Component Vased Development으로 구성된다. 컴포넌트의 역할은 애플리케이션의 화면을 구성하는 뷰View를 생성하고 관리하는 것이다.
컴포넌트는 독립적이고 완결된 뷰를 생성하기 위하여 "HTML, CSS, 자바스크립트를 하나의 단위로 묶는 것"
컴포넌트의 기본 구조
생성된 루트 컴포넌트 src/app/app.component.ts
app.component.ts
컴포넌트는 1)임포트 영역과 2)@Component 데코레이터 영역 3)컴포넌트 클래스 영역으로 구분할 수 있다.
임포트 영역
컴포넌트에 필요한 의존 모듈을 임포트한다.
@Component 데코레이터 영역
@Component 데코레이터에는 메타데이터 객체를 인자로 전달한다. 메타데이터 객체는 컴포넌트 생성에 필요한 정보(셀렉터, 템플릿, 스타일 정의 등)를 담고 있다.
컴포넌트 클래스 영역
컴포넌트 뷰를 관리하기 위한 로직을 담은 클래스를 정의한다. 컴포넌트 클래스는 컴포넌트의 내부 관심사인 뷰의 관리에 집중해야 하며 애플리케이션 공통 관심사는 서비스로 분리하여야 한다.
주의해야 할 것은 @Componenet 데코레이터 바로 아래에는 반드시 컴포넌트 클래스를 위치시켜야 한다는 점이다. @Component 데코레이터는 자신의 바로 아래에 위치한 클래스를 컴포넌트 클래스로 인식한다. 따라서 데코레이터와 데코레이터가 장식하는 클래스 사이에는 아무것도 존재해서는 안 된다.