티스토리 뷰

AWS

[AWS KMS] KMS를 통한 암복호화

DevBee 2022. 4. 26. 08:42

1. AWS IAM 생성하기

KMS는 AWS IAM user나 role을 통해서 권한을 가질 수 있으니 우선 local에서 사용할 IAM user 또는 role을 만들어 줍니다. 저는 user를 만들어서 진행을 해보겠습니다.

AWS IAM에서 사용자 추가 선택
사용자 이름 지정 및 액세스 유형 선택
Admin 권한 부여 (원래는 적절한 권한만 체크하는 것이 중요!!)
user 생성 후 보안 자격 증명에서 액세스 키 만들기 선택! 엑셀 파일 다운 받고 저장해두기!!!

 

2. KMS Key 생성하기

KMS Key를 생성합니다. 이때 키 관리 권한과 키 사용 권한은 위에서 만들어둔 user를 지정합니다.

키 구성 옵션 선택
레이블 추가
위에서 생성한 user 선택
위에서 생성한 user 선택
신뢰할 수 있는 엔티티 선택
키 권한 선택
키 생성 완료!!! 키 ID 복사하기

 

3. credential 설정하기

먼저 aws cli를 사용하여 암복호화가 되는지 확인하기 위해 설정을 진행합니다.

 

(1) aws cli 설치

https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/getting-started-install.html

 

(2) aws configure로 credential 설정하기

 

4. AWS CLI로 암복호화 해보기

아래 명령을 통해 "Hello World"를 암호화해보겠습니다. 알고리즘은 RSAES_OAEP_SHA_256를 사용하였습니다.

aws kms encrypt --key-id YOUR_KEY_ID --plaintext fileb://<(echo -n 'Hello World') --encryption-algorithm RSAES_OAEP_SHA_256

위 명령을 통해 암호화된 문자열을 얻을 수 있습니다. 해당 문자열을 복사해 뒀다가 아래 명령을 통해 복호화할 수 있습니다.

aws kms decrypt --key-id YOUR_KEY_ID --ciphertext-blob fileb://<(echo -n '암호화된_문자열' | base64 --decode) --output text --encryption-algorithm RSAES_OAEP_SHA_256 --query Plaintext | base64 --decode

 

5. 프로젝트 생성하기

Spring Boot 프로젝트를 생성하고 아래와 같이 dependency를 추가합니다.

implementation("org.zalando:spring-cloud-config-aws-kms:5.1.2")
implementation("com.amazonaws:aws-java-sdk-core:1.11.1019")
implementation("com.amazonaws:aws-java-sdk-kms:1.11.1019")
implementation("com.amazonaws:jmespath-java:1.11.1019")

 

application.yml 파일에 kms key 와 알고리즘 방식 등의 설정을 해줍니다.

spring:
  profiles:
    active: default

server:
  port: 9090
  tomcat:
    threads:
      max: 512
  servlet:
    context-path: /v1/kms
    encoding:
      charset: UTF-8
      enabled: true
      force: true

aws:
  kms:
    keyId: 키 아이디
    encryptionAlgorithm: "RSAES_OAEP_SHA_256"

 

이후 service와 controller를 차례로 만들어 줍니다.

package com.example.kms_test.service;

import com.amazonaws.regions.Regions;
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClientBuilder;
import com.amazonaws.services.kms.model.DecryptRequest;
import com.amazonaws.services.kms.model.EncryptRequest;
import com.amazonaws.services.kms.model.EncryptResult;
import com.amazonaws.services.kms.model.EncryptionAlgorithmSpec;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

@Slf4j
@Service
public class KmsService {

    @Value("${aws.kms.keyId}")
    private String KEY_ID;

    public String encrypt(String plainText) {
        try {
            AWSKMS kmsClient = AWSKMSClientBuilder.standard()
                    .withRegion(Regions.AP_NORTHEAST_2)
                    .build();

            EncryptRequest request = new EncryptRequest();
            request.withKeyId(KEY_ID);
            request.withPlaintext(ByteBuffer.wrap(plainText.getBytes(StandardCharsets.UTF_8)));
            request.withEncryptionAlgorithm(EncryptionAlgorithmSpec.RSAES_OAEP_SHA_256);

            EncryptResult result = kmsClient.encrypt(request);
            ByteBuffer ciphertextBlob = result.getCiphertextBlob();

            return new String(Base64.encodeBase64(ciphertextBlob.array()));
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        }
    }

    public String decrypt(String encryptedText) {
        try {
            AWSKMS kmsClient = AWSKMSClientBuilder.standard()
                    .withRegion(Regions.AP_NORTHEAST_2)
                    .build();

            DecryptRequest request = new DecryptRequest();
            request.withCiphertextBlob(ByteBuffer.wrap(Base64.decodeBase64(encryptedText)));
            request.withKeyId(KEY_ID);
            request.withEncryptionAlgorithm(EncryptionAlgorithmSpec.RSAES_OAEP_SHA_256);
            ByteBuffer plainText = kmsClient.decrypt(request).getPlaintext();

            return new String(plainText.array());
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        }
    }
}

 

package com.example.kms_test.controller;

import com.example.kms_test.service.KmsService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
@RestController
public class KmsController {

    private final KmsService kmsService;

    @GetMapping("/encrypt")
    public String encrypt(@RequestParam String plainText) {
        return kmsService.encrypt(plainText);
    }

    @GetMapping("/decrypt")
    public String decrypt(@RequestParam String encryptedText) {
        return kmsService.decrypt(encryptedText);
    }
}

 

6. 포스트맨으로 확인

이제 프로젝트를 실행하고 아래 사진과 같이 HTTP Request를 날려 결과를 확인합니다.

암호화하기
복호화하기

 

❗️에러

 

(1) ClassNotFoundException: org.springframework.boot.context.properties.ConfigurationBeanFactoryMetadata

해당 에러는 Spring Boot 버전과 Cloud 버전이 맞지 않아 발생하는 에러로 아래와 같이 설정을 해주면 해결됩니다!

plugins {
    id 'org.springframework.boot' version '2.3.8.RELEASE'  // 요기!!!
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

ext {
    set('springCloudVersion', "Hoxton.SR9")  // 요기!!!
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'

    implementation("org.zalando:spring-cloud-config-aws-kms:5.1.2")
    implementation("com.amazonaws:aws-java-sdk-core:1.11.1019")
    implementation("com.amazonaws:aws-java-sdk-kms:1.11.1019")
    implementation("com.amazonaws:jmespath-java:1.11.1019")

    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

 

(2) InvalidCiphertextException

복호화를 진행하려고 할 때 다음과 같은 에러가 발생합니다...

null (Service: AWSKMS; Status Code: 400; Error Code: InvalidCiphertextException; Request ID: b42394cd-709b-40e0-b071-1ab763f7dab8; Proxy: null)

이 부분은 아직 해결을 하지 못했습니다...😥 혹시 해결 방법을 알고 계시다면 공유해주시면 감사하겠습니당!

 

대신 테스트 코드를 작성하여 암복호화를 확인해 보았습니다! 테스트 코드는 잘 작동합니다! (왜일까요...?)

package com.example.kms_test.service;

import com.amazonaws.regions.Regions;
import com.amazonaws.services.kms.AWSKMS;
import com.amazonaws.services.kms.AWSKMSClientBuilder;
import com.amazonaws.services.kms.model.DecryptRequest;
import com.amazonaws.services.kms.model.EncryptRequest;
import com.amazonaws.services.kms.model.EncryptResult;
import com.amazonaws.services.kms.model.EncryptionAlgorithmSpec;
import org.apache.commons.codec.binary.Base64;
import org.junit.jupiter.api.Test;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

import static org.junit.jupiter.api.Assertions.*;

class KmsServiceTest {

    private static final String KEY_ID = "KEY_ID";

    @Test
    void encrypt() {
        final String plaintext = "Hello, KMS";

        try {
            AWSKMS kmsClient = AWSKMSClientBuilder.standard()
                    .withRegion(Regions.AP_NORTHEAST_2)
                    .build();

            EncryptRequest request = new EncryptRequest();
            request.withKeyId(KEY_ID);
            request.withPlaintext(ByteBuffer.wrap(plaintext.getBytes(StandardCharsets.UTF_8)));
            request.withEncryptionAlgorithm(EncryptionAlgorithmSpec.RSAES_OAEP_SHA_256);

            EncryptResult result = kmsClient.encrypt(request);
            ByteBuffer ciphertextBlob = result.getCiphertextBlob();

            System.out.println("ciphertextBlob: " + new String(Base64.encodeBase64(ciphertextBlob.array())));
        } catch (Exception e) {
            System.out.println("encrypt fail: " + e.getMessage());
        }
    }

    @Test
    void decrypt() {
        final String encriptedText = "XkJcKtmn60fT...nmTEJnw48yjjQy8+A=";

        try {
            AWSKMS kmsClient = AWSKMSClientBuilder.standard()
                    .withRegion(Regions.AP_NORTHEAST_2)
                    .build();

            DecryptRequest request = new DecryptRequest();
            request.withCiphertextBlob(ByteBuffer.wrap(Base64.decodeBase64(encriptedText)));
            request.withKeyId(KEY_ID);
            request.withEncryptionAlgorithm(EncryptionAlgorithmSpec.RSAES_OAEP_SHA_256);
            ByteBuffer plainText = kmsClient.decrypt(request).getPlaintext();

            System.out.println("plainText: " + new String(plainText.array()));
        } catch (Exception e) {
            System.out.println("decrypt fail: " + e.getMessage());
        }
    }
}

 

[참고]

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함