Notice
Recent Posts
Recent Comments
Link
아님말고
[AWS] KMS을 이용한 DB 암복호화 (with Mybatis) 본문
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