ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring] 전자정부프레임워크 crypto를 활용한 암호화
    Spring 2024. 6. 17. 21:07

     

    서버의 프로퍼티 파일(Spring의 properties 파일)에 데이터베이스 연결 정보를 작성하거나 데이터베이스에 사용자의 정보 등 민감한 정보를 저장할 때에는 유출을 생각해서 적절한 암호화가 필요합니다. 이때, 데이터를 암호화한 후, 다시 복호화 할 수 없도록 하는 단방향 암호화를 활용할 수 있습니다. 대표적인 단방향 알고리즘으로는 해시 함수를 이용하여 원본 데이터를 고정된 길이의 해시값으로 변환하는 MD5, SHA-1, SHA-256 등이 있습니다. 이번 예시에서 작성할 전자정부 프레임워크의 crypto 라이브러리(패키지 까보면 jasypt 라이브러리 기반)도 위의 알고리즘들이 가용하지만, MD5와 SHA-1은 취약성 문제 때문에 SHA-256을 사용하는 것이 일반적입니다.

     

    아래 예시는 전자정부프레임워크 4.2.0 버전으로, 3.x 버전과는 groupId, artifactId가 다르므로 이전 버전에서는 그대로 복붙시 오류가 발생할 수도 있습니다. messageSource 빈의 기본 프로퍼티 설정 파일의 경로값 등을 버전에 맞게 수정하셔야 합니다.

     

    pom.xml

    <!-- Maven -->
    <dependency>
        <groupId>org.egovframe.rte</groupId>
        <artifactId>org.egovframe.rte.fdl.crypto</artifactId>
        <version>${org.egovframe.rte.version}</version> <!-- 4.2.0 -->
    </dependency>

     

     

    context-crypto.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:egov-crypto="http://maven.egovframe.go.kr/schema/egov-crypto"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://maven.egovframe.go.kr/schema/egov-crypto http://maven.egovframe.go.kr/schema/egov-crypto/egov-crypto-4.2.0.xsd">
    
        <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
            <property name="basenames">
                <list>
                    <value>classpath:/myproject/message/message-common</value>
                    <value>classpath:/org/egovframe/rte/fdl/property/messages/properties</value>
                </list>
            </property>
            <property name="cacheSeconds">
                <value>60</value>
            </property>
        </bean>
        
        <egov-crypto:config id="egovCryptoConfig"
            initial="false"
            crypto="true"
            algorithm="SHA-256"
            algorithmKey=""
            algorithmKeyHash=""
            cryptoBlockSize="1024"
            cryptoPropertyLocation="classpath:/globals.properties"
        />
    
    </beans>

     

     

     

    테스트 클래스

    package myproject;
    
    import org.egovframe.rte.fdl.cryptography.EgovEnvCryptoService;
    import org.egovframe.rte.fdl.cryptography.EgovPasswordEncoder;
    import org.egovframe.rte.fdl.cryptography.impl.EgovEnvCryptoServiceImpl;
    import org.junit.jupiter.api.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    @Slf4j
    class EgovEnvCryptoAlgorithmCreateTest {
    	
    	public String algorithmKey = "testkey";
    	public String algorithm = "SHA-256";
    	public int algorithmBlockSize = 1024;
    	
    	@Test
    	void createHash() {
    		EgovPasswordEncoder egovPasswordEncoder = new EgovPasswordEncoder();
    		egovPasswordEncoder.setAlgorithm(algorithm);
    		
    		log.info("-------------------------");
    		log.info("알고리즘: " + algorithm);
    		log.info("알고리즘 키: " + algorithmKey);
    		log.info("알고리즘 키 Hash: " + egovPasswordEncoder.encryptPassword(algorithmKey));
    		log.info("알고리즘 블럭사이즈: " + algorithmBlockSize);
    	}
    
    	@Test
    	void createEncryptedProperties() {
    		String[] arrCryptoString = { 
    			"root", //데이터베이스 접속 계정 설정
    			"0000", //데이터베이스 접속 패드워드 설정
    			"jdbc:log4jdbc:mysql://127.0.0.1:3306/mydb", //데이터베이스 접속 주소 설정
    			"net.sf.log4jdbc.DriverSpy" //데이터베이스 드라이버
    		};
    	 
    		log.info("------------------------------------------------------");		
    		ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:/publicdata/spring/context-crypto.xml"});
    		EgovEnvCryptoService cryptoService = context.getBean(EgovEnvCryptoServiceImpl.class);
    		log.info("------------------------------------------------------");
    	 
    		String label = "";
    		try {
    			for(int i=0; i < arrCryptoString.length; i++) {		
    				if(i==0)label = "사용자 아이디";
    				if(i==1)label = "사용자 비밀번호";
    				if(i==2)label = "접속 주소";
    				if(i==3)label = "데이터 베이스 드라이버";
    				log.info(label+" 원본(orignal):" + arrCryptoString[i]);
    				log.info(label+" 인코딩(encrypted):" + cryptoService.encrypt(arrCryptoString[i]));
    				log.info("------------------------------------------------------");
    			}
    		} catch (IllegalArgumentException e) {
    			log.error("["+e.getClass()+"] IllegalArgumentException : " + e.getMessage());
    		} catch (Exception e) {
    			log.error("["+e.getClass()+"] Exception : " + e.getMessage());
    		}
    	}
    }

     

    src/test/java/myproject 패키지에 테스트 클래스를 생성했습니다. 키, 알고리즘, 블록 사이즈, DB 연결 정보는 자신의 프로젝트에 해당하는 값을 넣고 다음과 같이 테스트를 실행하면 됩니다.

     

    콘솔 창에 출력된 암호화된 값을 아래와 같이 프로퍼티 파일에 작성해줍니다.

     

    globals.properties

    # DB Connection Information
    Globals.DB.DriverClassName = QhNVFN5s4e8lZyDXMM4%2FAcfh84bSuNjyyNKHVsnpXPs%3D
    Globals.DB.Url = jzW9rBvX9%2B6LqhqaNvOiP5zpWTA437oixLYM0KRbPnPjQ%2BbONWL80Fj1xe34ZhGO
    Globals.DB.UserName = NlkO72EKounpY2QVrjIyoA%3D%3D
    Globals.DB.Password = agdeOaZixq8da4%2FDwlYm1g%3D%3D

     

    context-datasource.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" 
    	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    			http://www.springframework.org/schema/jdbc  http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd"
    >
    	
    	<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:/globals.properties</value>
                </list>
            </property>
        </bean>
        
        <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
        	<property name="driverClassName" value="#{egovEnvCryptoService.decrypt('${Globals.DB.DriverClassName}')}" />
        	<property name="url" value="#{egovEnvCryptoService.decrypt('${Globals.DB.Url}')}" />
        	<property name="username" value="#{egovEnvCryptoService.decrypt('${Globals.DB.UserName}')}" />
        	<property name="password" value="#{egovEnvCryptoService.decrypt('${Globals.DB.Password}')}" />
        </bean>
    </beans>

     

     

    서비스 클래스

    @Service("userService")
    @RequiredArgsConstructor
    public class UserServiceImpl implements UserService {
    	
        private final UserMapper userMapper;
    	
        private final EgovEnvCryptoService cryptoService;
    	
    
        // 조회 - 인풋 패스워드를 암호화해서 DB의 암호화된 패스워드와 비교
        public UserVO findUser(UserVO userVO) throws Exception {
            userVO.setUserPassword(cryptoService.encryptNone(userVO.getUserPassword()));
            return userMapper.selectUser(userVO);
        }
        
        // 사용자 전체 조회
        public List<UserVO> findAll(UserVO userVO) throws Exception {
            List<UserVO> users = userMapper.selectUsers(userVO);
    		
            users.stream().forEach(user -> {
                user.setUserName(cryptoService.decryptNone(user.getUserName()));
                user.setUserPhoneNumber(cryptoService.decryptNone(user.getUserPhoneNumber()));
            });
    		
            return users;
        }
    	
        // 등록
        public int addUser(UserVO userVO) throws Exception {
            userVO.setUserName(cryptoService.encryptNone(userVO.getUserName()));
            userVO.setUserPassword(cryptoService.encryptNone(userVO.getUserPassword()));
            userVO.setUserPhoneNumber(cryptoService.encryptNone(userVO.getUserPhoneNumber()));
    		
            return userMapper.insertUser(userVO);
        }
    	
        // 갱신 - Overwrite
        public int modifyUserPassword(UserVO userVO) throws Exception {
            userVO.setUserPassword(cryptoService.encryptNone(userVO.getUserPassword()));
            return userMapper.updateUserPassword(userVO);
        }
    }

    xml 설정 파일에서 작성한 값으로 bean 초기화를 하기 때문에, EgovEnvCryptoService를 주입받아서 encrypt(), decrypt() 메서드를 사용해 주기만 하면 됩니다.

     

     

    조회 결과

     


    참조

    전자정부프레임워크 문서

    (https://www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte2:fdl:crypto_simplify_v3_8)

    댓글

Designed by Tistory.