암호화와 복호화를 AWS Encryption SDK를 활용하여 Node.js와 Python 언어로 구현
사전지식
AWS 암호화 SDK는 누구나 업계 표준과 모범 사례를 사용하여 데이터를 쉽게 암호화하고 해독할 수 있도록 설계된 클라이언트 측 암호화 라이브러리입니다. 이 라이브러리를 사용하면 데이터를 가장 잘 암호화하고 해독하는 방법보다는 애플리케이션의 핵심 기능에 집중할 수 있습니다. AWS 암호화 SDK는 Apache 2.0 라이선스에 따라 무료로 제공됩니다.
특징으로 래핑 키로 데이터 키를 보호하는 프레임워크이며, AWS 암호화 SDK는 하나 이상의 래핑 키로 암호화하여 데이터를 암호화하는 데이터 키를 보호합니다. 하나 이상의 래핑 키로 데이터 키를 암호화하는 프레임워크를 제공함으로써, AWS 암호화 SDK는 암호화된 데이터를 이식할 수 있도록 도와줍니다. 예를 들어, AWS KMS의 AWS KMS 키와 온프레미스 HSM의 키로 데이터를 암호화할 수 있습니다. 래핑 키 중 하나를 사용할 수 없거나 호출자에게 두 키를 모두 사용할 수 있는 권한이 없는 경우 두 키 중 하나를 사용하여 데이터를 해독할 수 있습니다.
Node.js 구현
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import {
KmsKeyringNode,
buildClient,
CommitmentPolicy,
getClient,
KMS,
} from '@aws-crypto/client-node';
import 'dotenv/config';
const clientProvider = getClient(KMS, {
endpoint: 'https://kms.ap-northeast-2.amazonaws.com',
credentials: {
accessKeyId: process.env.AWS_ACCESSKEY,
secretAccessKey: process.env.AWS_SECRETKEY,
},
});
/* This builds the client with the REQUIRE_ENCRYPT_REQUIRE_DECRYPT commitment policy,
* which enforces that this client only encrypts using committing algorithm suites
* and enforces that this client
* will only decrypt encrypted messages
* that were created with a committing algorithm suite.
* This is the default commitment policy
* if you build the client with `buildClient()`.
*/
const { encrypt, decrypt } = buildClient(
CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT
);
async function kmsSimpleTest() {
/* A KMS CMK is required to generate the data key.
* You need kms:GenerateDataKey permission on the CMK in generatorKeyId.
*/
const generatorKeyId =
'arn:aws:kms:us-west-2:111122223333:alias/EncryptDecrypt';
/* Adding alternate KMS keys that can decrypt.
* Access to kms:Encrypt is required for every CMK in keyIds.
* You might list several keys in different AWS Regions.
* This allows you to decrypt the data in any of the represented Regions.
* In this example, I am using the same CMK.
* This is *only* to demonstrate how the CMK ARNs are configured.
*/
const keyIds = [
'arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab',
'arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ac',
];
/* The KMS keyring must be configured with the desired CMKs */
const keyring = new KmsKeyringNode({
clientProvider,
generatorKeyId,
keyIds,
});
/* Encryption context is a *very* powerful tool for controlling and managing access.
* It is ***not*** secret!
* Encrypted data is opaque.
* You can use an encryption context to assert things about the encrypted data.
* Just because you can decrypt something does not mean it is what you expect.
* For example, if you are are only expecting data from 'us-west-2',
* the origin can identify a malicious actor.
* See: https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html#encryption-context
*/
const context = {
stage: 'petaverse',
purpose: 'omoomo app',
origin: 'ap-northeast-2',
};
/* Find data to encrypt. A simple string. */
const cleartext = 'hello';
/* Encrypt the data. */
const { result } = await encrypt(keyring, cleartext, {
encryptionContext: context,
});
/* Decrypt the data. */
const { plaintext, messageHeader } = await decrypt(keyring, result);
/* Grab the encryption context so you can verify it. */
const { encryptionContext } = messageHeader;
/* Verify the encryption context.
* If you use an algorithm suite with signing,
* the Encryption SDK adds a name-value pair to the encryption context that contains the public key.
* Because the encryption context might contain additional key-value pairs,
* do not add a test that requires that all key-value pairs match.
* Instead, verify that the key-value pairs you expect match.
*/
Object.entries(context).forEach(([key, value]) => {
if (encryptionContext[key] !== value)
throw new Error('Encryption Context does not match expected values');
});
return { plaintext: plaintext.toString(), result: result.toString('base64') };
}
function main() {
kmsSimpleTest().then((result) => console.log(result));
}
main();
Python 구현
import aws_encryption_sdk
from aws_encryption_sdk import CommitmentPolicy
import boto3
import base64
def encrypt_decrypt(key_arns, source_plaintext, boto3_session=None):
encrypt_kwargs = dict(key_ids=key_arns)
if boto3_session is not None:
botocore_session = boto3_session._session
encrypt_kwargs["botocore_session"] = botocore_session
client = aws_encryption_sdk.EncryptionSDKClient(commitment_policy=CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT)
# 복합 키를 이용한 암호화
strict_encrypt_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(**encrypt_kwargs)
ciphertext, encrypted_message_header = client.encrypt(
source=source_plaintext, key_provider=strict_encrypt_key_provider
)
b64EncodeChpherTxt = base64.b64encode(ciphertext) # DBMS에 적재를 위하여 byte 타입을 base64를 통한 변경 처리
decodePlaintext = "";
# 두개의 키를 이용한 복호화 처리
for key_arn in key_arns:
decrypt_kwargs = dict(key_ids=[key_arn])
if boto3_session is not None:
botocore_session = boto3_session._session
decrypt_kwargs["botocore_session"] = botocore_session
strict_decrypt_key_provider = aws_encryption_sdk.StrictAwsKmsMasterKeyProvider(**decrypt_kwargs)
plaintext, decrypted_message_header = client.decrypt(
source=base64.b64decode(b64EncodeChpherTxt), key_provider=strict_decrypt_key_provider
)
decodePlaintext = plaintext
print(b64EncodeChpherTxt.decode('utf-8')) # byte 를 string 타입으로 전환
print(decodePlaintext)
# Example usage with a boto3 session
boto3_session = boto3.Session(
aws_access_key_id="AWS_ACCESSKEY",
aws_secret_access_key="AWS_SECRETKEY",
region_name="REGION_NAME"
)
key_arns = ['arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab', 'arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ac']
source_plaintext = "hello"
encrypt_decrypt(key_arns, source_plaintext, boto3_session)
마치며
Node.js에서 암호화 된 값을 python 코드에서 복호화가 가능합니다. 물론 그 반대도 가능하고요
node 코드는 잘 되어 있는데 python 코드는 예제가 잘 되어 있다고 하기 어려웠습니다. 다행히 aws-encryption-sdk-python 여기를 참조해서 잘 마무리 할 수 있었습니다