[목차]

 

 

1. 윈도우 / 리눅스 암호화 실습

 

 

2. 파일 암복호화 프로그램 ccrypt.py 개발

   * argparese module 사용법

   * 암복호화 프로그램 실습1

   * 암복호화 프로그램 실습2

 

 

3. 과제

    * 조건

    * 참고 : 암호 입력 받는 부분 가리는 법

    * 과제 코드 전문

 

 

 

 

 


 

 

 

[윈도우 / 리눅스 암호화 실습]

 

 

(정리)

 

Windows

* 파일/디렉토리 단위의 암호(암호화/복호화) : EFS

* 파일시스템 단위의 암호 : BitLocker

* 특징 : 암호화시 데이터 있어도 유지, 용량이 크면 시간 오래 걸림

 

Linux

* 파일/디렉토리 단위의 암호 : ccrypt 층, gpg CMD, ...

* 파일시스템 단위의 암호 : LUKS

* 특징 : 해당 파티션을 암호화 먼저 하고 데이터를 추가해야한다.

기존 데이터가 존재하는 경우 암호화시 날아간다. 용량이 커도 시간이 오래걸 리지 않는다.

 

 

 

Firmware DISK     파티션 스키마

---------              ----------------

BIOS                  MBR 파티션 스키마 (2TB 미만의 작업, priamary/extended/logical partition)

UEFI                   GUID 파티션 스키마 (2TB 이상의 작업)

 

 

[참고] 파티션 작업

* fdisk CMD 2TB 미만의 파티션 작업

* gdisk CMD, parted CMD : Disk 2TB 이상의 작업

 

 

 


 

 

 

[파일 암복호화 프로그램 ccrypt.py 개발]

 

 

 

 

1. argparese module 사용법

 

■ argparse 모듈

 

[참고] argparse - 명령행 옵션, 인자와 부속 명령을 위한 파서

https://docs.python.org/ko/3.7/library/argparse.html

 

[참고] Argparse 자습서

https://docs.python.org/ko/3.7/howto/argparse.html#id1

 

 

 

 

[간단한 사용 예]

# vi argparse_test.py

#!/usr/bin/python3

import argparse

# 인자값을 받을 수 있는 인스턴스 생성
parser = argparse.ArgumentParser(description='사용법 테스트')

# 입력받을 인자값 등록
parser.add_argument('-t', '--target', required=True, help='타겟 호스트 지정')
parser.add_argument('-e', '--env', required=False, default='dev', help='환경변수 지정')

# 입력받은 인자값을 args에 저장(type: namespace)
args = parser.parse_args()

# 입력받은 인자값 출력
print(args.target)
print(args.env)#!/usr/bin/python3

import argparse

# 인자값을 받을 수 있는 인스턴스 생성
parser = argparse.ArgumentParser(description='사용법 테스트')

# 입력받을 인자값 등록
parser.add_argument('-t', '--target', required=True, help='타겟 호스트 지정')
parser.add_argument('-e', '--env', required=False, default='dev', help='환경변수 지정')

# 입력받은 인자값을 args에 저장(type: namespace)
args = parser.parse_args()

# 입력받은 인자값 출력
print(args.target)
print(args.env)

 

 

 

[실습] 암복호화 프로그램 샘플1

(예제) 프로그램 사용 형식

ccrypt.py

#!/usr/bin/python3

import argparse
Usage = '''ccrypt과 같은 동작을 할 수 있는 프로그램입니다. ccrypt.py 프로그램은 AES 알고리즘을 사용하여 암호화, 복호화를 진행합니다.'''
parser = argparse.ArgumentParser(description=Usage)

group = parser.add_mutually_exclusive_group()
group.add_argument("-e", action="store_true", help='암호화')
group.add_argument("-d", action='store_true', help='복호화')
parser.add_argument('filename')

args = parser.parse_args()

print(args.e)
print(args.d)
print(args.filename)

if args.e is True and args.d is False:
    print('Encryption Action')
elif args.e is False and args.d is True:
    print('Decryption Action')
elif args.e is False and args.d is False:
    print('Encryption Action')
else:
    print('Unkown Error')

 

 

 

 

 

[실습] 암복호화 프로그램 실습2

 

# ccrypt2.py

Enter encryption key: (암호입력)

(repeat) Enter encryption key: (암호입력)

#!/usr/bin/python3
# AES128-CBC
# 1) Cipher Algorithm : AES
#    * input 	: 16 bytes
#    * output 	: 16 bytes
#    * key   	: 16 bytes
# 2) Cipher Mode : CBC
#	* IV		: 16 bytes
#	* Padding	: pad, unpad
# 3) infile -> E(keypasswd(16) + IV(16) + E(infile)) -> outfile

import sys
import os
import time
import argparse
from getpass import getpass
from Crypto.Cipher import AES
from Crypto.Hash import SHA512 as SHA
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad


def main():
    # 명령어 옵션, 인자 처리 부분
    parser = argparse.ArgumentParser(
        description='ccrypt과 같은 동작을 할 수 있는 프로그램입니다. ccrypt.py 프로그램은 AES 알고리즘을 사용하여 암호화, 복호화를 진행합니다.')
    group = parser.add_mutually_exclusive_group()
    group.add_argument("-e", action="store_true", help='암호화')
    group.add_argument("-d", action='store_true', help='복호화')
    parser.add_argument('filename')

    args = parser.parse_args()

    inputfile = args.filename
    # inputfile = 'plain.txt'
    # inputfile = 'plain.txt.enc'

    hashpasswd = inputpass()
    if (args.e is True and args.d is False) or (args.e is False and args.d is False):
        aesencrypt(hashpasswd, inputfile)
    elif args.e is False and args.d is True:
        aesdecrypt(hashpasswd, inputfile)
    else:
        sys.exit('Error: Unkown Error')


def aesdecrypt(hashpasswd, filename):
    # input : filename(encrypted file(hashpasswd + iv + encrypted content))
    # output : file(decrypted file)
    # function : 암호화된 파일에서
    decfilename = filename + '.dec'
    if os.path.exists(filename):
        print('Decrypting...')
        with open(filename, 'rb') as fd:
            hashpasswd2, iv, ciphertext = [fd.read(x) for x in (16, 16, -1)]
            if hashpasswd == hashpasswd2:
                myhash = AES.new(hashpasswd2, AES.MODE_CBC, iv)
                plaintext = unpad(myhash.decrypt(ciphertext), 16)
                plaintext = plaintext.decode()
                with open(decfilename, 'w+') as fd:
                    fd.write(plaintext)
            else:
                sys.exit('Error: Incorrect password')
        time.sleep(1)
        print('Decrypted')
    else:
        sys.exit('Error: File(%s) not exist.' % filename)

    return


def aesencrypt(hashpasswd, filename):
    # input : key(hashpasswd), iv
    # output : file(encrypted_file(hashpasswd(16) + iv(16) + encrypted file))
    # function :
    #   * 키, 초기벡터를 입력으로 받아서 AES 암호화를 하고,
    #   * "hashpasswd + 파일 암호화된 부분"을 합쳐서 최종적인 암호화 파일을 생성한다.
    if os.path.exists(filename):
        print('Encrypting...')
        key = hashpasswd  # 16 bytes
        iv = get_random_bytes(16)
        encfile = filename + '.enc'

        aes = AES.new(key, AES.MODE_CBC, iv)
        with open(filename, 'rb') as fd:
            content = fd.read()
            encrypted = aes.encrypt(pad(content, 16))
            with open(encfile, 'wb+') as fd2:
                content2 = hashpasswd + iv + encrypted
                fd2.write(content2)
        time.sleep(1)
        print('Encrypted.')
    else:
        sys.exit('Error: File(%s) not exist.' % filename)

    return


def inputpass():
    # input : N/A - 키보드로 입력 받음
    # output : hash(hashpasswd)
    # function : 키보드로 입력을 암호를 2번 입력 받아서, 2번의 암호는 일치해야 하고,
    #   * 암호를 가지고 해시 함수를 통해 해시 값을 만들어 낸다.
    #   * 이 해시 값이 암호화할 때 사용하는 키이다.
    if sys.stdin.isatty():  # 터미널상에서 입력하는 경우(파이참 말고)
        passwd = getpass('Enter encryption key: ')
        passwd2 = getpass('(Repeat) Enter encryption key: ')
    else:
        sys.exit('Error: Using readline')

    if passwd == passwd2:
        myhash = SHA.new()
        myhash.update(passwd.encode('utf-8'))
        hashpasswd = myhash.digest()
        hashpasswd = hashpasswd[:16]
    else:
        sys.exit('Sorry, the keys you entered did not match.')

    return hashpasswd


if __name__ == '__main__':
    main()

 

 

 

 

 


 

 

[과제]

 

다음 조건에 맞는 프로그램을 작성하시오

 

 

 

 

■ 조건

암호화 알고리즘 AES256 사용 (32 bytes)

<Advanced Encryption Standard>를 줄인 말로

번역하면 '고급 암호화 표준'이라 하며

대칭형 블럭 암호화 알고리즘으로 가장 유명하다.

암호화 키는 128, 192, 256의 세 가지 중 하나가 될 수 있으며, 각각 AES-128, AES-192, AES-256으로 불린다. 암호화 키의 길이에 따라 실행하는 라운드의 수가 다른데, 각각 10, 12, 14 라운드를 실행한다.

AES는 DES(데이터 암호화 표준)의 뒤를 이을 AES(고급 암호화 표준)라는 이름을 걸고 NIST가 주최한 공모전에서 채택된 Rijndael(레인달) 알고리즘을 가리킨다. 엄밀하게는 Rijndael 알고리즘의 여러 가능성 중, 암호화 블럭의 크기가 128비트이며 암호화 키의 길이가 128, 192, 256비트인 세 가지 종류가 AES 표준으로 지정.

 

Cipher Mode : CTR

Hash Function : SHA256

 

 

기능 디자인 :

 

 

nonce 16 bytes / key 16 bytes

passphrase 입력 받아서 맞으면 파일을 열수 있고,

맞지 않으면 에러 메세지를 출력한다.

파일의 확장자는 .enc 사용한다.

enc로 되어있는지 점검하는 장치 추가.

# python3 ccrypt3.py -e file1.txt

-> file1.txt.enc

복호화시 다시 txt 확장자로 돌아오기

# python3 ccrypt3.py -d file1.txt.enc

-> file.txt

 

 

■ 암호화 과정

# ./ccrypt.py -e filename

암호를 물어본다.

암호를 SHA256 방식으로 암호화 한다.

파일을 AES256 방식으로 암호화 한다.

두개의 내용을 붙여서 암호화된 파일을 만들고,

이때 파일의 확장자(.enc)를 붙인다.

 

 

■ 복호화 과정

# ./ccrypt.py -d filename.enc

암호를 물어본다.

암호를 해시 함수로 돌리고, 파일의 64바이트를 읽어들여서 비교한다.

- 암호가 틀리면, 에러메세지를 출력하고 프로그램은 종료된다.

- 암호가 맞으면, 파일의 64바이트를 제외한 나머지 부분을 decryption 하고

* .enc 확장를 떼어버린다.

 

 

■ 선수지식

프로그램 옵션/인자 처리 방법

해시 함수(ex: SHA) 관련 사용법

대칭 암호(ex: AES) 관련 사용법

 

 

 

[참고 사항]

 

파이썬 input() 함수의 echo 기능 off 시키기

 

* 파이썬 프로그램 작성시 암호입력을 받는 부분이

보안상 눈에 보이지 않아야 하기 때문에

다음과 같은 2가지 방법을 통해 이 문제를 해결할 수 있다.

 

(출처) 
https://pymotw.com/2/getpass/
https://stackoverflow.com/questions/4616813/can-i-get-console-input-without-echo-in-python


(1) 첫번째 방법
from getpass import getpass
pw = getpass()
print(pw)


(2) 두번째 방법
import os
os.system('stty -echo')
pw = input('Passowrd: ')
os.system('stty echo')
print(pw)



(연습)
# (Linux) echo off
import os

os.system('stty -echo')
passwd = input("Password : ")
os.system('stty echo')



# (python) getpass
import getpass, sys

if sys.stdin.isatty():
p = getpass.getpass()
else:
print("Password: ")
p = sys.stdin.readline().rstrip()


import getpass, sys

if sys.stdin.isatty():  #터미널에서 명령어를 수행하는 경우 여길 수행
    p = getpass.getpass()
else:
    print('Password: ')
    p = sys.stdin.readline().rstrip()

 

 

 

 

[과제 코드 전문]

#!/usr/bin/python3
# AES256-CTR
# 1) Cipher Algorithm : AES256
#    * input 	: 32 (256 bits)
#    * output 	: 32 (256 bits)
#    * key   	: 32 (128 bits)
# 2) Cipher Mode : CTR
#   * counter value(nonce + count)
#	* nonce      : 16 (128 bytes)
# 3) inputfile -> E(AES256-CTR) + nonce(16) + E(infile)) -> outfile

import os
import sys
import time
import argparse
from getpass import getpass
from Crypto.Cipher import AES
from Crypto.Hash import SHA256 as SHA
from Crypto.Random import get_random_bytes

def main():
    # 명령어 옵션, 인자 처리 부분
    this_is = '''ccrypt.py 프로그램과 같 AES 알고리즘을 사용하여 암호화, 복호화를 진행하는 프로그램입니다.'''
    parser = argparse.ArgumentParser(description=this_is)

    group = parser.add_mutually_exclusive_group()
    group.add_argument("-e", action="store_true", help='암호화')
    group.add_argument("-d", action='store_true', help='복호화')
    parser.add_argument('filename')

    args = parser.parse_args()

    inputfile = args.filename
    # inputfile = 'plain.txt'
    # inputfile = 'plain.txt.enc'

    hashpasswd = inputpass()
    if (args.e is True and args.d is False) or (args.e is False and args.d is False):
        aes_encrypt(hashpasswd, inputfile)
    elif args.e is False and args.d is True:
        aes_decrypt(hashpasswd, inputfile)
    else:
        sys.exit('Error: Unkown Error')


def aes_decrypt(hashpasswd, filename):
    # input : filename(encrypted file(hashpasswd + encrypted content))
    # output : file(decrypted file)
    # function : 암호화된 파일에서
    decfile = '.'.join(filename.split('.')[0:2])
    if os.path.exists(filename):
        print('복호화 작업을 시작합니다...')
        with open(filename, 'rb') as fd:
            hashpasswd2, nonce, ciphertext = [fd.read(x) for x in (32, 8, -1)]
            if hashpasswd == hashpasswd2:
                myhash = AES.new(hashpasswd2, AES.MODE_CTR, nonce=nonce)
                plaintext = myhash.decrypt(ciphertext).decode()
                with open(decfile, 'w+') as fd:
                    fd.write(plaintext)
            else:
                sys.exit('Error: 암호가 일치하지 않습니다.')
        time.sleep(1)
        print('복호화가 완료 되었습니다. (파일명: %s)' % decfile)
    else:
        sys.exit('Error: File(%s)이 존재하지 않습니다.' % filename)

    return


def aes_encrypt(hashpasswd, filename):
    # input : key(hashpasswd)
    # output : file(encrypted_file(hashpasswd(16) + encrypted file))
    # function :
    #   * 키, nonce를 입력으로 받아서 AES 암호화를 하고,
    #   * "hashpasswd + 파일 암호화된 부분"을 합쳐서 최종적인 암호화 파일을 생성한다.
    if os.path.exists(filename):
        print('암호화 작업을 시작합니다...')

        key = hashpasswd  # 32 bytes
        encfile = filename + '.enc'
        aes = AES.new(key, AES.MODE_CTR)
        nonce = aes.nonce

        with open(filename, 'rb') as fd1:
            content = fd1.read()
            encrypted = aes.encrypt(content)
            with open(encfile, 'wb+') as fd2:
                content2 = hashpasswd + nonce + encrypted
                fd2.write(content2)
        time.sleep(1)
        print('암호화가 완료 되었습니다. (파일명: %s)' % encfile)
    else:
        sys.exit('Error: File(%s) not exist.' % filename)

    return


def inputpass():
    # input : N/A - 키보드로 입력 받음
    # output : hash(hashpasswd)
    # function : 키보드로 입력을 암호를 2번 입력 받아서, 2번의 암호는 일치해야 하고,
    #   * 암호를 가지고 해시 함수를 통해 해시 값을 만들어 낸다.
    #   * 이 해시 값이 암호화할 때 사용하는 키이다.
    if sys.stdin.isatty():  # 터미널상에서 입력하는 경우(파이참 말고)
        passwd = getpass('Enter encryption key: ')
        passwd2 = getpass('(Repeat) Enter encryption key: ')
    else:
        sys.exit('Error: Using readline')

    if passwd == passwd2:
        myhash = SHA.new()
        myhash.update(passwd.encode('utf-8'))
        hashpasswd = myhash.digest()
        hashpasswd = hashpasswd[:32]
    else:
        sys.exit('Sorry, the keys you entered did not match.')

    return hashpasswd


if __name__ == '__main__':
    main()

 

 

끗!

 

 

 

 

728x90

+ Recent posts