HD

Tomcat Server.xml 암호화 (feat.AES) 본문

WAS/Tomcat

Tomcat Server.xml 암호화 (feat.AES)

hunecenter 2022. 6. 23. 10:58
반응형

프로젝트 중에 Server.xml DB Resource 부분에 url, id, password 암호화를 할수없냐는 문의가 들어와서 찾아 보고 적용을 해보았다.

암호화하는 역할을 하는 DataSourceFactory를 가지고 참조해 소스를 수정 및 추가를 하였다.

 

작업한 소스는 총2개이다 Encryptor.java, EncryptedDataSourceFactory.java

 

※EncryptedDataSourceFactory의 full path 기억하기


[Encryptor.java]

package secured;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class Encryptor {

    private static final String ALGORITHM = "AES";
	
    private static final String defaultSecretKey = "SecretKeyTest";//AES secretKey

    private Key secretKeySpec;

    public Encryptor() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException,
            UnsupportedEncodingException {
        this(null);
    }

    public Encryptor(String secretKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
            UnsupportedEncodingException {
        this.secretKeySpec = generateKey(secretKey);
    }

    public String encrypt(String plainText) throws InvalidKeyException, NoSuchAlgorithmException,
            NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
        return asHexString(encrypted);
    }

    public String decrypt(String encryptedString) throws InvalidKeyException, IllegalBlockSizeException,
            BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
        byte[] original = cipher.doFinal(toByteArray(encryptedString));
        return new String(original);
    }

    private Key generateKey(String secretKey) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        if (secretKey == null) {
            secretKey = defaultSecretKey;
        }
        byte[] key = (secretKey).getBytes("UTF-8");
        MessageDigest sha = MessageDigest.getInstance("SHA-1");
        key = sha.digest(key);
        key = Arrays.copyOf(key, 16); // use only the first 128 bit

        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(128); // 192 and 256 bits may not be available

        return new SecretKeySpec(key, ALGORITHM);
    }

    private final String asHexString(byte buf[]) {
        StringBuffer strbuf = new StringBuffer(buf.length * 2);
        int i;
        for (i = 0; i < buf.length; i++) {
            if (((int) buf[i] & 0xff) < 0x10) {
                strbuf.append("0");
            }
            strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
        }
        return strbuf.toString();
    }

    private final byte[] toByteArray(String hexString) {
        int arrLength = hexString.length() >> 1;
        byte buf[] = new byte[arrLength];
        for (int ii = 0; ii < arrLength; ii++) {
            int index = ii << 1;
            String l_digit = hexString.substring(index, index + 2);
            buf[ii] = (byte) Integer.parseInt(l_digit, 16);
        }
        return buf;
    }

    public static void main(String[] args) throws Exception {
    	Encryptor aes = new Encryptor(defaultSecretKey);
    	String url ="jdbc:mysql://127.0.0.1:3306/testDb?characterEncoding=UTF-8";
    	System.out.println("url:" + aes.encrypt(url));
    	String testid = "test";
    	System.out.println("Id:" + aes.encrypt(testid));
    	String testpassword = "test";
    	System.out.println("Pw:" + aes.encrypt(testpassword));
    }

}

[EncryptedDataSourceFactory.java]

package secured;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.Properties;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.naming.Context;
import javax.sql.DataSource;

import org.apache.tomcat.jdbc.pool.DataSourceFactory;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.apache.tomcat.jdbc.pool.XADataSource;

public class EncryptedDataSourceFactory extends DataSourceFactory {


    private Encryptor encryptor = null;

    public EncryptedDataSourceFactory() {
        try {
            encryptor = new Encryptor(); // If you've used your own secret key, pass it in...
        } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public DataSource createDataSource(Properties properties, Context context, boolean XA) throws InvalidKeyException,
            IllegalBlockSizeException, BadPaddingException, SQLException, NoSuchAlgorithmException,
            NoSuchPaddingException {
        // Here we decrypt our password.
        PoolConfiguration poolProperties = EncryptedDataSourceFactory.parsePoolProperties(properties);
        poolProperties.setUrl(encryptor.decrypt(poolProperties.getUrl())); //dbConUrl 복호화
        poolProperties.setUsername(encryptor.decrypt(poolProperties.getUsername())); //username복호화
        poolProperties.setPassword(encryptor.decrypt(poolProperties.getPassword())); //password복호화
        /*
        setValidationQuery Option
        SELECT 1(mysql), SELECT 1 FROM DUAL(oracle), SELECT 1(mssql) 
        */
        poolProperties.setValidationQuery("SELECT 1");
        poolProperties.setTestWhileIdle(true);
        poolProperties.setTimeBetweenEvictionRunsMillis(720000);//2시간 단위로 SELECT 1 검증
        
        // The rest of the code is copied from Tomcat's DataSourceFactory.
        if (poolProperties.getDataSourceJNDI() != null && poolProperties.getDataSource() == null) {
            performJNDILookup(context, poolProperties);
        }
        org.apache.tomcat.jdbc.pool.DataSource dataSource = XA ? new XADataSource(poolProperties)
                : new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
        dataSource.createPool();

        return dataSource;
    }

}

[Encryptor.java bulid]

server.xml에 암호화한 값들을 넣어주기위해 Encryptor.java를 먼저 컴파일해서 값을 확인해본다.


[jar 추출]

(순서) - Export -> java에 JAR file -> JAR file에 경로와 jar파일명 설정후 Finish (하단 이미지 참조)

 

 

012


[Server.xml 설정]

Resource 부분에 factory="com.github.tomcatEncryptModule.EncryptedDataSourceFactory"를 추가해준다.

<Resource auth="Container" 
      		factory="com.github.tomcatEncryptModule.EncryptedDataSourceFactory" #여기가 중요
			driverClassName="com.mysql.jdbc.Driver"
			maxActive="50" 
			maxIdle="10" 
			maxWait="-1" 
			name="jdbc/testDB" 
			password="암호화값" 
			type="javax.sql.DataSource" 
			url="암호화값" 
			username="암호화값"/>

서버 재기동후 정상으로 작동되는지 확인!!


[적용후 개인생각]

만약 이걸 각 프로젝트마다 defaultSecretKey가 바뀐다 생각을 하면 소스 수정후 jar로 묶는 작업이 번거로울수 있다보니 해당 지정된 properties파일을 생성해서 properties에 defaultSecretKey관리 변수를 추가후 관리 해주는 방안도 있을거 같다. 

//예시
//AES secretKey
private static final String defaultSecretKey = properties.getProperty("SecretKey");

[jar 파일]

tomcatEncryptModule-0.0.1.jar
0.13MB

 

[git url]

https://github.com/hunecenter94/tomcat-jdbc-encrypt-module

 

GitHub - hunecenter94/tomcat-jdbc-encrypt-module: tomcatEncryptModule (server.xml) Aes Encrypt & Decrypt

tomcatEncryptModule (server.xml) Aes Encrypt & Decrypt - GitHub - hunecenter94/tomcat-jdbc-encrypt-module: tomcatEncryptModule (server.xml) Aes Encrypt & Decrypt

github.com


[참조]

http://www.jdev.it/encrypting-passwords-in-tomcat/

 

Encrypting passwords in Tomcat – JDev

Apache Tomcat is by far the most popular (open source) web server and Java servlet container. It has been around for a long time and – at the time of writing this post – has reached version 7.0.29. As Apache rightfully claims on its web site, Tomcat p

www.jdev.it

https://jar-download.com/artifacts/org.apache.tomcat/tomcat-jdbc/7.0.19/source-code/org/apache/tomcat/jdbc/pool/DataSourceFactory.java

 

org.apache.tomcat.jdbc.pool.DataSourceFactory Maven / Gradle / Ivy

org.apache.tomcat tomcat-jdbc 7.0.19 compile group: 'org.apache.tomcat', name: 'tomcat-jdbc', version: '7.0.19' //Thanks for using https://jar-download.com libraryDependencies += "org.apache.tomcat" % "tomcat-jdbc" % "7.0.19" //Thanks for using https://jar

jar-download.com

https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/tomcat/jdbc/pool/DataSourceFactory.html

 

DataSourceFactory (Apache Tomcat 8.0.53 API Documentation)

JNDI object factory that creates an instance of BasicDataSource that has been configured based on the RefAddr values of the specified Reference, which must match the names and data types of the BasicDataSource bean properties. Properties available for conf

tomcat.apache.org

 

반응형

'WAS > Tomcat' 카테고리의 다른 글

리눅스 톰켓 한글 깨짐 현상 해결  (0) 2022.03.08
session clustering 적용 (feat.Tomcat7 이상)  (0) 2020.10.05
TOMCAT 버전업 문제  (0) 2020.08.04
Tomcat JDK변경  (0) 2020.07.13
Tomcat ORACLE JNDI Datasource 설정  (0) 2019.01.23
Comments