본문 바로가기

Security&Identity

AWS Key Management Service(KMS)

KMS란? 암호화 키의 라이프사이클을 관리하는 전용 시스템으로 암호화 키의 생성, 저장, 백업, 복구, 파기 등의 기능을 제공하는 시스템으로 CMK 제어 및 관리, AES-256 / RSA(SHA_256)을 지원.

 

다만 CMK는 4KB까지 데이터만 암호화 가능. 

KMS 데이터 암호화 플로우

따라서 4KB 이하의 데이터 보다 큰 데이터들을 암호화할 때는 데이터키를 활용.

 

[암호화]

1) CMK의 generate를 이용하여 데이터키와 암호화 된 데이터키를 생성.

2) 원본 데이터키를 이용하여 데이터를 암호화.

3) 원본 데이터키를 삭제.

 

[복호화]

1) CMK를 이용하여 암호화 된 데이터키를 복호화.

2) 복호화 된 원본 데이터키를 이용하여 데이터를 복호화.

3) 복호화 된 원본 데이터키를 삭제.

 

암호화 방식은 아래 링크에 잘 설명되어 있음.

(https://medium.com/chequer/sqlgate-teams-%EA%B0%9C%EB%B0%9C%EA%B8%B0-1%ED%8E%B8-%EB%B6%80%EC%A0%9C-%EC%95%88%EC%A0%84%ED%95%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%95%94%ED%98%B8%ED%99%94-aws-kms-aes-d2583826ed90)

 

 

Python Boto3를 이용하여 CMK를 사용해보자 (대칭키 사용 / python v3 / fernet 이용)

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kms.html

 

KMS — Boto3 Docs 1.20.22 documentation

CustomKeyStoreId (string) -- [REQUIRED] Enter the key store ID of the custom key store that you want to connect. To find the ID of a custom key store, use the DescribeCustomKeyStores operation.

boto3.amazonaws.com

 

 

1) KMS에서 CMK를 사용할 User나 Role을 정하고 Policy를 생성.

https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html

 

Default key policy - AWS Key Management Service

Because key administrators have permission to change the key policy and create grants, they can give themselves AWS KMS permissions not specified in this policy. Principals who have permission to manage tags and aliases can also control access to a KMS key

docs.aws.amazon.com

 

 

[권한 관리 생성]

kms_policy.json

Root User, Master Role: KMS 관리

Dev User: 암복호화 용도

{
    "Id": "key-policy",
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Enable IAM User Permissions",
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                "arn:aws:iam::557xxxxx50:root",
                "arn:aws:iam::557xxxxx750:role/master"
                ]
            },
            "Action": "kms:*",
            "Resource": "*"
        },
        {
            "Sid": "Allow attachment of persistent resources",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::557xxx750:user/dev"
            },
            "Action": [
                "kms:CreateGrant",
                "kms:ListGrants",
                "kms:RevokeGrant"
            ],
            "Resource": "*",
            "Condition": {
                "Bool": {
                    "kms:GrantIsForAWSResource": "true"
                }
            }
        }
    ]
}

 

 

[CMK 생성]

주의: KeySpec과 keyUsage 파라미터를 정의하면 비대칭키로 생성

import boto3
import base64
import json
kms_client = boto3.client("kms")


# Key Policy
def get_policy():
    with open("./kms_policy.json", "r") as f:
        json_data = json.load(f)
    kms_policy = json.dumps(json_data)
    return kms_policy

# Create KMS
def create_kms(kms_policy):
    response = kms_client.create_key(
            Policy = kms_policy,
            Description = "Learning KMS",
            Origin = "AWS_KMS",
            BypassPolicyLockoutSafetyCheck = False,
            Tags = [
               {
                    "TagKey": "Name",
                    "TagValue": "KMS_TEST"
                }
            ]
    )
    return response["KeyMetadata"]["KeyId"], response["KeyMetadata"]["Arn"]


def create_alias(keyId):
    response = kms_client.create_alias(
        AliasName = "alias/demo_cmk",
        TargetKeyId = KeyId
    )

if __name__ == "__main__":
    kms_policy = get_policy()
    KeyId, Arn = create_kms(kms_policy)
    create_alias(KeyId)

 

 

AWS KMS Console에 아래와 같이 CMK가 생성되는 것을 확인

Key ID를 저장

 

 

 

별첨: 기본적으로 4K 이하의 데이터는 아래와 같이 데이터키 없이 사용 가능

import boto3
import base64
import json
kms_client = boto3.client("kms")

KEY_ID = "9dafea1b-9d8c-4e43-8fe3-4003747629b4"
PW = "qwer1234"

def encrypt(keyId, data):
    encrypt_result = kms_client.encrypt(KeyId=keyId, Plaintext=data)
    print(encrypt_result)
    return base64.b64encode(encrypt_result["CiphertextBlob"])

def decrypt(encrypted_data):
    decrypt_result = kms_client.decrypt(CiphertextBlob=base64.b64decode(encrypted_data))
    print(decrypt_result["Plaintext"].decode("utf8"))
    return decrypt_result

if __name__ == "__main__":
    encrypted_data = encrypt(KEY_ID, PW)
    decrypted_data = decrypt(encrypted_data)

 

 

2) CMK와 boto3의 generate_data_key를 사용하여 데이터키, 암호화 된 데이터키 생성

그리고 Fernet과 encrypt를 이용하여 암호화 된 Data 저장

encrypted_key.txt에는 암호화 된 데이터키 저장 / secret.txt에는 암호화 된 데이터 저장

import boto3
import base64
import json
from cryptography.fernet import Fernet

kms_client = boto3.client("kms")
KEY_ID = "alias/demo_cmk"
SECRET = "qwer1234"

def create_generate_data_key(KEY_ID):
    response = kms_client.generate_data_key(
            KeyId = KEY_ID,
            KeySpec = "AES_256"
    )
    return base64.b64encode(response["CiphertextBlob"]), base64.b64encode(response["Plaintext"])


def save_encrypted_key(encrypted_key):
    f = open("./encrypted_key.txt", "wb")
    f.write(encrypted_key)
    f.close()


def encrypt_data(SECRET, plain_key):
    cipher_suite = Fernet(plain_key)
    cipher_data = cipher_suite.encrypt(SECRET.encode('utf-8'))
    f = open("./secret.txt", "wb")
    f.write(cipher_data)
    f.close()
    return cipher_data


if __name__ == "__main__":
    encrypted_key, plain_key = create_generate_data_key(KEY_ID)
    save_encrypted_key(encrypted_key)

    cipher_data = encrypt_data(SECRET, plain_key)

 

 

3) 암호화 된 데이터키를 이용하여 암호화 된 데이터를 복호화

import boto3
import base64
import json
from cryptography.fernet import Fernet

kms_client = boto3.client("kms")
KEY_ID = "alias/demo_cmk"

def decrypt_data():
    f_s = open("./secret.txt", "rb")
    e_secret = f_s.readline()

    f_e = open("./encrypted_key.txt", "rb")
    encrypted_key = f_e.readline()
    response = kms_client.decrypt(CiphertextBlob=base64.b64decode(encrypted_key))
    plain_key = base64.b64encode(response["Plaintext"])

    cipher = Fernet(plain_key)
    secret = cipher.decrypt(bytes(e_secret))
    print(secret)

if __name__ == "__main__":
    chipher_data = decrypt_data()

 

데이터 복호화 결과

그럼 위 그림과 같이 암호화 된 데이터키를 통해 데이터를 복호화 할 수 있음.

 

추가로 AWS KMS의 key는 export가 불가능하기 때문에 삭제 후에는 데이터를 복호화 할 방법이 없음.

 

다만 AWS CloudHSM을 사용할 경우, key의 export가 가능.

 

어느 정도 규모가 있는 정보통신사업자(연 매출 100억 이상 등)라면 키 관리 시스템에 대한 감사가 있기 때문에 주로 법적인 문제로 KMS를 쓰는 경우가 많음. (ISMS-P 등)

 

그러나 법적인 문제 외에도 애플리케이션을 통해 암호화 할 경우 SHA-1, MD5 등 위험 요소가 있는 알고리즘 사용으로 보안 문제 및 유지보수의 어려움이 발생할 수 있기 때문에 중요한 데이터의 경우에도 KMS 사용을 권장.

 

KMS, HSM은 키 관리에 대한 보안, 성능, 관리 세 가지 목적을 위해 사용되는데, FIPS 등 인증을 받은 전용 장치이기 때문에 보다 안전하고, 애플리케이션이 암복호화에 처리해야 할 작업에 대한 부하를 줄여주기도 함.

 

HSM란? 암호화 키를 필요로 하는 다양한 애플리케이션이 있을 경우 생성, 저장, 백업, 복구 파기 등의 기능을 제공하는 물리 시스템.

'Security&Identity' 카테고리의 다른 글

AWS Config 5분만에 이해하기  (0) 2021.10.14