Emulasi KMS di Floci: GenerateKey, encrypt/decrypt, envelope pattern dengan data key, Sign/Verify dasar — dan implementasi envelope encryption app-side untuk melindungi field PII sebelum masuk DynamoDB

Ketika aplikasi menyimpan data pribadi (PII) — nomor KTP, kartu kredit, data kesehatan — enkripsi field-level lewat KMS adalah praktik standar kepatuhan. Masalahnya: menguji alur kriptografi ini biasanya butuh AWS sungguhan, padahal justru logika inilah yang paling sering salah implement.
Floci mengemulasi KMS cukup lengkap untuk melatih pola produksi: generate key, encrypt/decrypt langsung, envelope pattern dengan data key, sampai Sign/Verify dasar. Episode ini membahas semuanya lalu mempraktikkannya dalam kasus nyata: enkripsi field PII sebelum masuk DynamoDB.
KEY_ID=$(aws --endpoint-url=http://localhost:4566 kms create-key \
--description "pii-field-key" \
--query KeyMetadata.KeyId --output text)
CT=$(aws --endpoint-url=http://localhost:4566 kms encrypt \
--key-id "$KEY_ID" \
--plaintext fileb://<(echo -n "3271012345678901") \
--query CiphertextBlob --output text | base64 -d | base64 -w0)
aws --endpoint-url=http://localhost:4566 kms decrypt \
--ciphertext-blob fileb://<(echo "$CT" | base64 -d) \
--query Plaintext --output text | base64 -d
# 3271012345678901Ciphertext blob berformat binary base64 seperti aslinya — kode app yang menangani encoding tidak perlu cabang khusus lokal vs produksi. create-key mendukung alias (create-alias) agar referensi di kode tetap human-readable (alias/pii-key).
Pola yang dipakai hampir semua sistem enkripsi AWS:
Intuisinya: master key tidak pernah mengenkripsi data besar; ia hanya melindungi data encryption key (DEK). API terkait: GenerateDataKey (kembalikan plaintext + encrypted), GenerateDataKeyWithoutPlaintext, dan Decrypt untuk membuka DEK saat baca.
Untuk use case tanda tangan dokumen/token:
aws --endpoint-url=http://localhost:4566 kms create-key --key-spec RSA_2048 \
--key-usage SIGN_VERIFY --description signing-key >/dev/null
SIG=$(aws --endpoint-url=http://localhost:4566 kms sign \
--key-id "$KEY_ID" --message fileb://contract.pdf \
--signing-algorithm RSASSA_PKCS1_V1_5_SHA256 --query Signature --output text)
aws --endpoint-url=http://localhost:4566 kms verify --key-id "$KEY_ID" \
--message fileb://contract.pdf --signature "fileb://<(echo $SIG)" \
--signing-algorithm RSASSA_PKCS1_V1_5_SHA256Skenario patuh-regulasi yang umum: tabel users menyimpan nama publik, tapi field nik (nomor induk) wajib terenkripsi field-level. Alurnya:
{nik_ciphertext, encrypted_dek}.encrypted_dek ke KMS Decrypt → dekrip NIK di memory.Manfaat uji lokal: kalian bisa memverifikasi bahwa NIK tidak pernah muncul sebagai plaintext di DynamoDB — sesuatu yang bisa diperiksa langsung via scan karena database-nya ada di tangan sendiri.
Note
Di floci, kunci tersimpan di storage profile internal — bukan HSM sungguhan. Yang dilatih adalah LOGIKA aplikasi (kapan encrypt, bagaimana menyimpan blob), bukan keamanan fisik kunci.
Target outline: implementasi envelope encryption app-side terhadap KMS floci.
import boto3, json, os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
kms = boto3.client("kms", endpoint_url="http://localhost:4566")
ddb = boto3.resource("dynamodb", endpoint_url="http://localhost:4566").Table("users")
def put_user(user_id: str, name: str, nik: str):
dk = kms.generate_data_key(KeyId="alias/pii-key") # DEK baru
nonce = os.urandom(12)
ct = AESGCM(dk["Plaintext"]).encrypt(nonce, nik.encode(), None)
ddb.put_item(Item={
"pk": user_id,
"name": name,
"nik_ct": (nonce + ct).hex(), # PII tak pernah plaintext
"wrapped_dek": dk["CiphertextBlob"].base64 if hasattr(dk["CiphertextBlob"], "base64") else dk["CiphertextBlob"],
})
def get_nik(user_id: str) -> str:
item = ddb.get_item(Key={"pk": user_id})["Item"]
raw = bytes.fromhex(item["nik_ct"])
blob = item["wrapped_dek"]
if isinstance(blob, str):
import base64; blob = base64.b64decode(blob)
dek = kms.decrypt(CiphertextBlob=blob)["Plaintext"]
return AESGCM(dek).decrypt(raw[:12], raw[12:], None).decode()Rangkuman episode ini:
Episode 11 menutup layanan inti dengan identitas pengguna akhir: emulasi Cognito — user pool, sign-up/sign-in, JWT, group, dan identity pool role mapping. Sampai jumpa!