Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
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
Archives
Today
Total
관리 메뉴

아님말고

[AWS] KMS을 이용한 DB 암복호화 (with Mybatis) 본문

Java

[AWS] KMS을 이용한 DB 암복호화 (with Mybatis)

스타박씨 2023. 5. 24. 16:04

AWS의 KMS (key managerment service) 을 이용하여 DB 암복화을 구현해보자.

local에서는 암복화를 실행하지 않고, 서버에서만 암복화를 실행한다고 했을때 아래와 같이 interface를 두고 local용 LocalKMSManager 와 서버용 BaseKMSManager을 구현한다.

 

1. KMS 암복화 모듈 개발

profile 환경에 따라 KMSManager 이름으로 bean을 등록한다.

public interface KMSManager {
 
    public String encrypt(String plainText);
 
    public String decrypt(String encryptedText);
}

 

 
@Slf4j
@Component("KMSManager")
@Profile({"dev", "stg", "prd"})
public class BaseKMSManager implements KMSManager {
 
    @Value("${aws.kms.key-id}")
    private String keyId;
 
    private AWSKMS kmsClient = AWSKMSClientBuilder.standard()
            .withRegion(Regions.AP_NORTHEAST_2)
            .build();
 
    @Override
    public String encrypt(String plainText) {
        try {
            EncryptRequest request = new EncryptRequest();
            request.withKeyId(keyId);
            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;
        }
    }
 
    @Override
    public String decrypt(String encryptedText) {
        try {
            DecryptRequest request = new DecryptRequest();
            request.withCiphertextBlob(ByteBuffer.wrap(Base64.decodeBase64(encryptedText)));
            request.withKeyId(keyId);
            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;
        }
    }
 
}
@Component("KMSManager")
@Profile({"local"})
public class LocalKMSManager implements KMSManager {
 
    @Override
    public String encrypt(String plainText) {
        return plainText;
    }
 
    @Override
    public String decrypt(String encryptedText) {
        return encryptedText;
    }
 
}

 

2. Mybatis에서 사용

Mybatis 의 typeHandler을 이용하여 writer 컬럼에 암복화를 적용할건데

BaseTypeHandler을 상속해서 CryptTypeHandler을 우선 작성한다.

@Slf4j
public class CryptTypeHandler extends BaseTypeHandler<String> {
 
    private KMSManager kmsManager = null;
 
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        kmsManager = (KMSManager) BeanUtil.getBean("KMSManager");
 
        if(StringUtils.isNotEmpty(parameter)) {
            parameter = kmsManager.encrypt(parameter);
        }
        ps.setString(i, parameter);
    }
 
    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        kmsManager = (KMSManager) BeanUtil.getBean("KMSManager");
        String value = rs.getString(columnName);
 
        if(StringUtils.isEmpty(value)) {
            return null;
        }else {
            if(this.isDecryptTarget(value)) {
                return kmsManager.decrypt(value);
            }else {
                return value;
            }
        }
    }
 
    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        kmsManager = (KMSManager) BeanUtil.getBean("KMSManager");
        String value = rs.getString(columnIndex);
 
        if(StringUtils.isEmpty(value)) {
            return null;
        }else {
            if(this.isDecryptTarget(value)) {
                return kmsManager.decrypt(value);
            }else {
                return value;
            }
        }
    }
 
    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        kmsManager = (KMSManager) BeanUtil.getBean("KMSManager");
        String value = cs.getString(columnIndex);
 
        if(StringUtils.isEmpty(value)) {
            return null;
        }else {
            if(this.isDecryptTarget(value)) {
                return kmsManager.decrypt(value);
            }else {
                return value;
            }
        }
    }
 
    //복호화 대상인지 확인
    //local에서는 암호화 하지않기 때문에 같은 계좌번호 컬럼이어도 DB에 암호화 된것 안된것 혼재되어있다.
    //그래서, 사이즈로 대략 400자 이상되면 암호화 되어 있는 값으로 판단하였다.
    //참고로 KMS 암호화시 사이즈는 정해져 있지 않다.
    private boolean isDecryptTarget(String str) {
        return (str.length() > 400) ? true : false;
    }
}

 

board 테이블을 insert 할때 writer 컬럼 암호를 위해 위에서 작성한 typeHandler을 적용한다.

그리고, 조회시 writer 컬럼 복호화를 위해 resultMap 으로 typeHandler을 적용한다.

<resultMap id="boardMap" type="com.myproject.sample.vo.BoardVO">
    <result column="writer" property="writer" typeHandler="com.myproject.sample.typeHandler.CryptTypeHandler" />
</resultMap>
 
<select id="selectBoard" resultMap="boardMap">
<![CDATA[
    SELECT *
     FROM board
]]>
</select>
 
<insert id="createBoard" parameterType="com.myproject.sample.vo.BoardVO">
<![CDATA[
    INSERT INTO board (
        subject,
        contents,
        writer
    ) values (
        #{subject},
        #{contents},
        #{writer, jdbcType=VARCHAR, typeHandler=com.myproject.sample.typeHandler.CryptTypeHandler}
    )
]]>
</insert>

 

 

 

'Java' 카테고리의 다른 글

[AWS] AWS SDK For Java 버전1과 2의 차이  (0) 2023.08.02
[gradle] spring boot + XSS filter 설정  (0) 2023.05.31
헥사고날 아키텍처  (0) 2023.05.11
[JWT] token secret key 생성  (0) 2023.05.10
[gradle] Spring Cloud Gateway Header 변경  (0) 2023.05.08
Comments