[java] Querydsl을 사용하여 데이터베이스 테이블 암호화하기

이 기술 블로그에서는 Querydsl을 사용하여 데이터베이스 테이블을 암호화하는 방법을 알아보겠습니다. Querydsl은 자바 퍼시스턴스 프레임워크로, SQL문을 자바 코드로 작성할 수 있게 해줍니다. 이를 활용하여 데이터베이스 테이블에 저장되는 데이터를 암호화할 수 있습니다.

데이터베이스 설정

먼저, 데이터베이스에 암호화할 테이블을 생성해야 합니다. 예를 들어, 사용자 정보를 저장하는 테이블을 생성해보겠습니다.

CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(255),
    password VARCHAR(255)
);

위의 예시에서 password 열은 암호화되어 저장될 필요가 있습니다. 암호화하기 위해 Querydsl을 사용하겠습니다.

Querydsl 추가

먼저, Querydsl을 프로젝트에 추가해야 합니다. Maven을 사용하는 경우, pom.xml 파일에 다음 의존성을 추가합니다.

<dependencies>
    <!-- Querydsl JPA -->
    <dependency>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-jpa</artifactId>
        <version>4.4.0</version>
    </dependency>

    <!-- Querydsl SQL -->
    <dependency>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-sql</artifactId>
        <version>4.4.0</version>
    </dependency>

    <!-- ... -->
</dependencies>

Gradle을 사용하는 경우, build.gradle 파일에 다음 의존성을 추가합니다.

dependencies {
    // Querydsl JPA
    implementation 'com.querydsl:querydsl-jpa:4.4.0'

    // Querydsl SQL
    implementation 'com.querydsl:querydsl-sql:4.4.0'

    // ...
}

의존성을 추가한 후, 프로젝트를 다시 빌드합니다.

암호화 로직 작성

이제 Querydsl을 사용하여 암호화 로직을 작성해보겠습니다. 먼저, 암호화에 사용할 키를 생성합니다. 이 키는 암호화 및 복호화에 사용될 것입니다.

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

public class EncryptionUtil {
    public static SecretKey generateKey() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(256);
            return keyGenerator.generateKey();
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate encryption key", e);
        }
    }
}

위의 코드에서는 AES 알고리즘을 사용하여 256비트 키를 생성하도록 설정하였습니다.

이제 Querydsl을 사용하여 저장될 데이터를 암호화하는 코드를 작성해보겠습니다.

import com.querydsl.sql.Configuration;
import com.querydsl.sql.SQLQueryFactory;
import com.querydsl.sql.SQLTemplates;
import com.querydsl.sql.dml.SQLInsertClause;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.persistence.EntityManager;
import java.security.GeneralSecurityException;
import java.sql.Connection;

public class UserDAO {
    private final EntityManager entityManager;
    private final SQLQueryFactory queryFactory;

    public UserDAO(EntityManager entityManager, Connection connection) {
        this.entityManager = entityManager;

        SQLTemplates templates = MySQLTemplates.builder().build();
        Configuration config = new Configuration(templates);
        queryFactory = new SQLQueryFactory(config, () -> connection);
    }

    public void save(User user) {
        try {
            Cipher cipher = Cipher.getInstance("AES");
            SecretKey key = EncryptionUtil.generateKey();
            cipher.init(Cipher.ENCRYPT_MODE, key);

            byte[] encryptedPassword = cipher.doFinal(user.getPassword().getBytes());
            user.setPassword(new String(encryptedPassword));

            QUser qUser = QUser.user;
            SQLInsertClause insertClause = queryFactory.insert(qUser);
            insertClause.set(qUser.username, user.getUsername());
            insertClause.set(qUser.password, user.getPassword());
            insertClause.execute();
        } catch (GeneralSecurityException e) {
            throw new RuntimeException("Failed to encrypt password", e);
        }
    }
}

위의 코드에서는 AES 알고리즘을 사용하여 비밀번호를 암호화합니다. 암호화된 비밀번호는 users 테이블에 저장됩니다.

암호화된 데이터 조회

마지막으로, 저장된 암호화된 데이터를 조회하는 코드를 작성해보겠습니다.

import com.querydsl.sql.Configuration;
import com.querydsl.sql.SQLQueryFactory;
import com.querydsl.sql.SQLTemplates;
import com.querydsl.sql.dml.SQLInsertClause;
import com.querydsl.sql.*;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.persistence.EntityManager;
import java.security.GeneralSecurityException;
import java.sql.Connection;
import java.util.List;

public class UserDAO {
    private final EntityManager entityManager;
    private final SQLQueryFactory queryFactory;

    public UserDAO(EntityManager entityManager, Connection connection) {
        this.entityManager = entityManager;

        SQLTemplates templates = MySQLTemplates.builder().build();
        Configuration config = new Configuration(templates);
        queryFactory = new SQLQueryFactory(config, () -> connection);
    }

    public List<User> getUsers() {
        QUser qUser = QUser.user;

        SQLQuery<User> query =
                queryFactory.selectFrom(qUser)
                        .fetch();

        List<User> users = query.fetch();
        try {
            Cipher cipher = Cipher.getInstance("AES");
            SecretKey key = EncryptionUtil.generateKey();
            cipher.init(Cipher.DECRYPT_MODE, key);

            for (User user : users) {
                byte[] encryptedPassword = user.getPassword().getBytes();
                byte[] decryptedPassword = cipher.doFinal(encryptedPassword);
                user.setPassword(new String(decryptedPassword));
            }
        } catch (GeneralSecurityException e) {
            throw new RuntimeException("Failed to decrypt password", e);
        }

        return users;
    }
}

위의 코드에서는 저장된 암호화된 비밀번호를 복호화하여 사용합니다. 이를 통해 정상적인 비밀번호를 확인할 수 있습니다.

결론

이 튜토리얼에서는 Querydsl을 사용하여 데이터베이스 테이블을 암호화하는 방법을 알아보았습니다. Querydsl을 활용하여 데이터를 암호화하고, 암호화된 데이터를 복호화하여 사용할 수 있습니다. 복잡한 암호화 로직을 작성하지 않고도 Querydsl의 간편한 문법을 활용하여 데이터의 보안을 강화할 수 있습니다.