본문 바로가기
AWS/KMS

AWS KMS 개념과 응용 - 2

by 알파해커 2022. 10. 14.
반응형

지난 "AWS KMS 개념과 응용 - 1"에서 KMS를 사용하는 이유와 KMS가 제공하는 기본적인 기능들에 대해서 살펴보았다. 이번에는 실제로 KMS를 이용해서 암/복호화하는 것은 어떻게 하는지 먼저 알아보고, Slack에서 KMS을 이용하여, 서로 다른 계정에서 Key를 어떻게 제어하는지 알아보자.

 


1. 암/복호화 API 개발

API Gateway + Lambda를 활용하여 암/복호화 API를 개발할 수 있다. 

API Gateway를 통해 요청이 들어오면, Lambda 내에서 KMS API를 이용하여 Data key를 생성하여 암/복호화를 수행한다.

 

 

Lambda에 작성되는 코드를 세부적으로 살펴보면 다음과 같이 나눌 수 있다.

 

(1) 라이브러리와 변수 초기화

import base64
import boto3
from Crypto.Cipher import AES

self.client = boto3.client('kms')

 

(2) Key Id 발급 및 Data Key 생성

self.key_id = 'arn'
self.pad = lambda s: s + (32 - len(s) % 32) * ' '

# data key 생성
data_key = client.generate_data_key(KeyId=self.key_id, KeySpec='AES_256')
self.plaintext_key = data_key.get('Plaintext')
self.ciphertext_blob = data_key.get('CiphertextBlob')

 

(3) 암호화 메소드

평문 Data key를 이용하여 암호화 객체를 생성, 문자열을 암호화하고 base64로 인코딩 후 이미 생성된 암호화된 Data key(ciphertext_blob)와 함께 반환한다. 이때 반환되는 암호화된 Data key는 복호화에 필요한 평문 Data key를 얻기 위해 꼭 필요한 정보이므로 어플리케이션에서 관리되어야 한다.

 

def encrypt_data(self, plaintext_message):
  crypter = AES.new(self.plaintext_key)
  encrypted_data = base64.b64encode(crypter.encrypt(self.pad(plaintext_message)))
  return encrypted_data, self.ciphertext_blob

 

(4) 복호화 메소드

전달 받은 암호화된 문자열(encrypted_data)과 Data key(ciphertext_blob)로 평문 Data key를 반환 받아 암호화 역순으로 암호화된 문자열을 base64로 디코딩한 뒤 복호화해서 반환한다.

 

def decrypt_data(self, encrypted_data, ciphertext_blob):
  decrypted_key = self.client.decrypt(ciphertext_blob).get('Plaintext')
  crypter = AES.new(decrypted_key)
  return crypter.decrypt(base64.b64decode(encrypted_data)).rstrip()

 

(전체 코드 예시)

더보기
import boto3
import base64
from Crypto.Cipher import AES

BLOCK_SIZE = 32
PADDING = '|'

key_arn = '{{ KMS_ARN }}'
message = 'This is test for KMS. written by JHSong'

client = boto3.client('kms')
data_key = client.generate_data_key(
    KeyId=key_arn,
    KeySpec='AES_256'
)

pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING

plaintext_key = data_key.get('Plaintext')
encrypted_key = data_key.get('CiphertextBlob')

###### Encrypted data ######## 
encryptor = AES.new(plaintext_key)
encrypted_data = base64.b64encode(encryptor.encrypt(pad(message)))

print("########## Encrypted Data ##############")
print(encrypted_data)

###### Decrypted data ######## 
decrypted_key = client.decrypt(CiphertextBlob=encrypted_key).get('Plaintext')
decryptor = AES.new(decrypted_key)
decrypted_str = decryptor.decrypt(base64.b64decode(encrypted_data)).decode('utf-8')

print("########## Decrypted Data ##############")
print(decrypted_str.rstrip(PADDING))

 

 


2. Multi-Account 환경 Key 관리 시스템 

조직의 특성이나 서비스의 특성에 따라 다수의 계정에서 Key를 관리해야 하는 경우가 있다.

 

일반적으로 권장되는 방법은 하나의 중앙 계정에서 CMK Key를 생성하고, 권한을 설정한 후, 다른 계정에서 Data Key를 중앙 계정으로 부터 발급 받아서 사용하는 것이다. 이렇게 되면 중앙 계정에서 Key와 관련한 Action을 CloudTrail과 CloudWatch를 통해 모니터링 할 수 있을 뿐만 아니라, 실제 데이터를 암호화하는 Data Key에 대한 핸들링도 가능하게 된다.

 

2.1 Multi-Account 환경에서의 암/복호화

아래 이미지의 좌측(Customer AWS account. 이미지에 나타나 있는 계정의 이름은 신경쓰지 말자)이 CMK Key를 생성하는 Key 중앙 관리 계정이고, 우측(SaaS provider AWS account. 마찬가지로 계정의 이름은 신경쓰지말자)이 Data Key를 발급 받아서 사용하는 계정이라고 해보자.

 

편의상 중앙 관리 계정은 A 계정, Data Key를 발급 받아 사용하는 계정을 B 계정이라고 하자.

 

계정이 나누어져 있을 뿐, 같은 계정에서 CMK를 생성하고, Data Key를 발급 받아 데이터를 암/복호화 하는 원리와 동일하다. (이 말이 이해되지 않는다면, AWS KMS 개념과 응용 - 1를 다시 보고 오자)

 

(1) 우선 A 계정에서 CMK를 생성하고, Policy를 설정한다.

 

(2) B 계정에 암호화 하고자 하는 데이터가 인입되었다면

 

(3) B 계정에서 중앙 계정으로 Data Key 발급을 요청(GenerateDataKey)한다.

 

(4) A 계정에서는 평문 Data Key와 CMK로 암호화된 Data Key를 돌려준다.

 

(5) B 계정에서는 평문 Data Key를 이용해 인입된 데이터를 암호화한다.

 

(6) 암호화가 완료되면 평문 Data Key를 삭제하고, 암호화된 데이터와 암호화된 Data Key를 함께 보관한다.

 

(7) 복호화를 할 땐, 가지고 있던 암호화된 Data Key를 A 계정으로 보내, 평문 Data Key를 돌려 받는다.

 

(8) 받은 평문 Data Key를 이용해, 암호화된 데이터를 복호화 한다.

 

 

2.2 다른 계정의 사용자가 CMK를 사용하도록 허용하는 방법

다른 AWS 계정의 IAM 사용자 또는 역할이 계정에서 AWS KMS key(KMS 키)를 사용하도록 허용할 수 있다. 

Cross-account(교차 계정) 접근에는 KMS 키의 키 정책과 외부 사용자 계정의 IAM 정책에 대한 권한이 필요하다.

 

Cross-account 접근은 다음과 같은 Action에 대해서 허용이 된다.

 

Step 1. 로컬 계정에서 Key Policy 추가

KMS 키에 대한 Key Policy는 누가 KMS 키에 액세스할 수 있으며, 어떤 작업을 수행할 수 있는지에 대한 주요 결정 요인이다. Key Policy는 항상 KMS 키를 소유하는 계정에 있다. IAM Policy과 달리, Key Policy는 리소스를 지정하지 않는다. 리소스는 Key Policy와 연결된 KMS 키이다.

 

외부 계정에게 KMS 키를 사용할 수 있는 권한을 부여하려면 외부 계정을 지정하는 문을 Key Policy에 추가한다. 

Key Policy의 Principal 요소에 외부 계정의 Amazon 리소스 이름(ARN)을 입력하면 된다.

 

예를 들어, 444455556666라는 계정에서 111122223333 계정의 KMS Key를 사용할 수 있도록 허용하고자 한다면, 

111122223333 계정에 있는 Key에 대해서 다음과 같은 Key Policy를 적용할 수 있다.

 

{

    "Sid": "Allow an external account to use this KMS key",

    "Effect": "Allow",

    "Principal": {

        "AWS": [

            "arn:aws:iam::444455556666:root"

        ]

    },

    "Action": [

        "kms:Encrypt",

        "kms:Decrypt",

        "kms:ReEncrypt*",

        "kms:GenerateDataKey*",

        "kms:DescribeKey"

    ],

    "Resource": "*"

}

 

혹은 444455556666 계정의 특정 Role이나 User에게 Key 사용에 대한 권한을 부여하려면 다음과 같이 작성할 수 있다.

 

{

    "Sid": "Allow an external account to use this KMS key",

    "Effect": "Allow",

    "Principal": {

        "AWS": [

            "arn:aws:iam::444455556666:role/ExampleRole",

            "arn:aws:iam::444455556666:user/ExampleUser"

        ]

    },

    "Action": [

        "kms:Encrypt",

        "kms:Decrypt",

        "kms:ReEncrypt*",

        "kms:GenerateDataKey*",

        "kms:DescribeKey"

    ],

    "Resource": "*"

}

 

Step 2. 외부 계정에서 IAM 정책 추가

앞서 살펴본 Key Policy를 통해서 KMS Key를 사용할 수 있는 권한의 범위를 정했다면, 실제로 외부 계정의 Role이나 User가 해당 권한들을 사용하기 위해서는 IAM Policy를 통해 그 권한들을 위임받아야 한다. 이러한 IAM Policy 권한 위임은 외부 계정 내에서 이루어져야 한다.

 

아래의 IAM Policy는 111122223333 계정 내에 있는 Key를 사용하고자 하는 외부 계정의 Role, User에게 적용되어야 하는 것이며, 따라서 외부 계정(444455556666 계정) 내에서 해당 Policy에 대한 적용이 이루어져야 한다.

 

{

    "Sid": "AllowUseOfKeyInAccount111122223333",

    "Effect": "Allow",

    "Action": [

      "kms:Encrypt",

      "kms:Decrypt",

      "kms:ReEncrypt*",

      "kms:GenerateDataKey*",

      "kms:DescribeKey"

    ],

    "Resource": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"

}

 

참고로, 이 때 IAM Policy에 Principal은 명시하지 않는다. 해당 IAM Policy가 적용되는(attach되는) 주체 (User 혹은 Role)이 Principal이 되기 때문이다. Resource에는 Principal이 사용하고자 하는 Key의 ARN을 입력한다. 

 

2.3 Audit

AWS CloudTrail 로그에서 KMS 키에 대한 작업 내용을 볼 수 있다. 다른 계정의 KMS 키를 사용하는 작업은 호출자의 계정과 KMS 키 소유자 계정 모두에 기록된다.

 

2.4 Slack EKM

Slack에서 하고자 했던 것은 사용자가(Slack의 고객이) 자신들의 데이터를 암호화 하기 위한 Key를 직접 관리하고, 그래서 그 Key에 대한 통제권을 가지도록 하는 것이다. 그 통제권을 통해 데이터의 암호화의 범위도 고객이 직접 결정하여, 데이터의 visibility를 (Slack이 아닌) 고객이 결정할 수 있도록 한다.

 

원래 Slack의 구조를 단순하게 표현하면 다음과 같다. Slack 앱을 통해 사용자가 메세지를 입력하면, 그것을 데이터베이스에 저장하는 것이다. 데이터베이스가 암호화 되어 있긴 하지만, 고객이 암호화에 사용되는 Key에 대한 통제권을 가지고 있지 않다. (통제권을 가지고 있다면, 고객이 원하는 순간에 Revocation을 하거나 Rotation을 수행할수도 있을 것이다)

 

 

그래서 고객이 직접 암호화 Key와 그 통제권을 가지고, 데이터를 암호화할 수 있는 기능을 EKM이라는 모듈을 통해 제공한다. Slack 사용자의 입력된 메세지는 EKM을 통해서 암/복호화되고, 데이터베이스에는 암호화된 데이터가 저장된다.

 

 

EKM을 활용해서, 고객이 사용자들의 데이터 visibility를 결정하고, 암/복호화의 통제권을 가지기 위해서는 아래와 같은 구조로 만들 수 있다. 고객은 고객이 직접 운영하는 계정에서 KMS를 이용하여 CMK를 만들고, Slack 계정 내의 EKM이 해당 CMK로 부터 생성된 Data key를 받아서 데이터를 암/복호화 하는 것이다.

 

이렇게 되면 고객은 자신들의 계정에서 소유하고 있는 CMK를 통해, Slack 메세지에서 암/복호화 되는 Key에 대한 통제권을 가지고 있게 되며, Key와 관련한 작업에 대한 Log를 CloudTrail + CloudWatch을 통해서 확인할 수 있게 된다. 

(KMS 관련 작업은 CloudTrail에 남고, 그 외의 요청들은 CloudWatch에 남길 수 있다)

 

 

또, 암호 문맥(Encryption context)를 이용해 CMK로 부터 만들어지는 Data key의 범위(Scope)를 지정할 수 있다. 즉, 동일 Organization + Workspace + Channel + Hour을 만족해야만 복호화해서 데이터를 확인할 수 있도록 하는 것이다. (만약에 Hour을 넣게되면, 매 시간마다 Key를 Rotation 해줘야 한다)

 

 

뿐만 아니라 고객은 Key policy를 통해 접근 가능한 권한 범위에 대해서도 제어 가능하다. 

 

 

 


3. 레퍼런스

반응형

'AWS > KMS' 카테고리의 다른 글

AWS KMS 개념과 응용 - 1  (1) 2022.10.13

댓글