파란하늘의 지식창고
article thumbnail
Published 2019. 3. 1. 20:04
Jasypt 암복호화 하기 Study/Java
반응형

암호화는 잘 모르는 내용이라 테스트하면서 기록하여 두서없음

dependency 참조

<dependency>
	<groupId>org.jasypt</groupId>
	<artifactId>jasypt</artifactId>
	<version>1.9.2</version>
</dependency>

다른 참조 라이브러리로 사용한다면 jasypt-spring-boot-starter 2.1.1이 현재 있긴 한데 해당 pom은 현재 com.melloware.jasypt 1.9.4 라이브러리를 참조하고 있다.

com.melloware.jasypt의 경우 StringFixedSaltGenerator를 사용하면 오류가 발생하고 있어 org.jasypt.jasypt를 사용해야 한다

기본 사용

암복호화를 간단하게 해 보면 아래와 같다.

public void encryptSimpleTest() {
	StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
	encryptor.setPassword("somePassword");
	encryptor.setAlgorithm("PBEWithMD5AndDES");
	String str = "testString";
	String encStr = encryptor.encrypt(str);
	String decStr = encryptor.decrypt(encStr);
	log.debug("str : {}, encStr : {}, decStr : {}", str, encStr, decStr);
}

결과

str : testString, encStr : lu9b1bhuwUztHRvTw039zBsxVWOGOdMz, decStr : testString

여러 번 시도하면 매번 다른 암호화된 문자열 값을 출력한다.

str : testString, encStr : a5Yz2d9frCw8a4HCd2xEmIsMUX1xcP93, decStr : testString
str : testString, encStr : +NKU7ja0wF7VO4KKsZBlFTvGNzgHcp3y, decStr : testString
str : testString, encStr : 8A7f2u98kXVLoeH14nFaFP1zqmp1OFqY, decStr : testString
str : testString, encStr : L8g5oJgOdkWweJLtrYqgqTb6a4YdkU2E, decStr : testString
str : testString, encStr : xE42jiIL/9y5El3cEVHpEXGsxtVKgNY6, decStr : testString
str : testString, encStr : CqthyhwleCVLIX4Fp/DNVagQIBnRKF4G, decStr : testString

고정된 salt 사용

saltGenerator를 지정하지 않으면 RandomSaltGenerator를 default로 사용한다.

sandom salt를 사용하는 경우는 암호화된 결과 값이 매번 바뀐다.

StringFixedSaltGenerator를 사용해 고정된 salt값을 지정하면 암호화된 결과 값이 고정돼서 반환된다.

이경우 복호화 시에도 기존에 설정한 salt와 동일한 값을 지정해야 복호화가 된다.

@Test
public void encryptSimpleTest() {
	StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
	encryptor.setPassword("somePassword");
	encryptor.setAlgorithm("PBEWithMD5AndDES");
	encryptor.setSaltGenerator(new StringFixedSaltGenerator("someFixedSalt"));
	String str = "testString";
	String encStr = encryptor.encrypt(str);
	String decStr = encryptor.decrypt(encStr);
	log.debug("str : {}, encStr : {}, decStr : {}", str, encStr, decStr);
}

이 경우 여러 번 호출해도 고정된 암호화 값을 반환한다.

str : testString, encStr : rEMYziBW/rGyMTc3ppqmUw==, decStr : testString
str : testString, encStr : rEMYziBW/rGyMTc3ppqmUw==, decStr : testString
str : testString, encStr : rEMYziBW/rGyMTc3ppqmUw==, decStr : testString
str : testString, encStr : rEMYziBW/rGyMTc3ppqmUw==, decStr : testString
str : testString, encStr : rEMYziBW/rGyMTc3ppqmUw==, decStr : testString
str : testString, encStr : rEMYziBW/rGyMTc3ppqmUw==, decStr : testString

보안상 random salt가 좋지만 필요한 경우 고정값을 사용할 수 있다.

암호화 알고리즘 선택

위 예제의 경우 PBEWithMD5 AndDES를 사용하였지만 다양한 알고리즘을 사용할 수 있다.

알고리즘 목록은 다음과 같이 확인할 수 있다.

@Test
public void getAllAlgorithms() {
	log.debug("allDigestAlgorithms : {}", AlgorithmRegistry.getAllDigestAlgorithms());
	log.debug("allPBEAlgorithms : {}", AlgorithmRegistry.getAllPBEAlgorithms());
}

조회한 결과는 다음과 같다.

allDigestAlgorithms : [MD2, MD5, SHA, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256, SHA3-224, SHA3-256, SHA3-384, SHA3-512]
allPBEAlgorithms : [PBEWITHHMACSHA1ANDAES_128, PBEWITHHMACSHA1ANDAES_256, PBEWITHHMACSHA224ANDAES_128, PBEWITHHMACSHA224ANDAES_256, PBEWITHHMACSHA256ANDAES_128, PBEWITHHMACSHA256ANDAES_256, PBEWITHHMACSHA384ANDAES_128, PBEWITHHMACSHA384ANDAES_256, PBEWITHHMACSHA512ANDAES_128, PBEWITHHMACSHA512ANDAES_256, PBEWITHMD5ANDDES, PBEWITHMD5ANDTRIPLEDES, PBEWITHSHA1ANDDESEDE, PBEWITHSHA1ANDRC2_128, PBEWITHSHA1ANDRC2_40, PBEWITHSHA1ANDRC4_128, PBEWITHSHA1ANDRC4_40]

그런데 실제로 테스트해보니 안 되는 게 많다.

사용하기 위해서 뭔가 추가 작업을 해야 하는 거 같은데 우선 현재 사용 가능한 목록을 확인하려면 아래와 같이 확인한다.

@Test
public void checkSupportAlgorithm() {
	List<Object> supported = new ArrayList<>();
	List<Object> unsupported = new ArrayList<>();
	for (Object algorithm : AlgorithmRegistry.getAllPBEAlgorithms()) {
		try {
			StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
			encryptor.setPassword("somePassword");
			encryptor.setAlgorithm(String.valueOf(algorithm));

			String str = "test";
			String encStr = encryptor.encrypt(str);
			String decStr = encryptor.decrypt(encStr);
			assertThat(str.equals(decStr));
			supported.add(algorithm);
		} catch (EncryptionOperationNotPossibleException e) {
			unsupported.add(algorithm);
		}
	}
	log.debug("supported : {}", supported);
	log.debug("unsupported : {}", unsupported);
}

PooledPBEStringEncryptor 사용하기

PasswordBased의 암호화는 StringEncryptor 뿐만 아니라 BigDecimal, BigInteger, Byte Encryptor를 제공한다.

각 Encryptor를 보면 StandardPBE*Encryptor와 쌍으로 PooledPBE*Encryptor가 있다.

Pooled는 내부적으로 StandardPBE*Encryptor를 poolSize만큼 클론 하여 가지고 있다가 요청이 오면 순서대로 pool에서 encryptor를 사용하게 된다.

실제 사용할 때엔 PooledPBE*Encryptor를 사용하는 것이 좋다.

public void encryptSimpleTest() {
	PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
	encryptor.setPassword("somePassword");
	encryptor.setAlgorithm("PBEWithMD5AndDES");
	String str = "testString";
	String encStr = encryptor.encrypt(str);
	String decStr = encryptor.decrypt(encStr);
	log.debug("str : {}, encStr : {}, decStr : {}", str, encStr, decStr);
}

PBEConfig 사용하기

encryptor마다 설정을 따로 할 수 있고 config를 따로 불러와서 설정할 수도 있다.

PBEConfig는 가장 간단하게 사용할 수 있는 SimplePBEConfig가 있고 이를 확장한 EnvironmentPBEConfig, SimpleStringPBEConfig, WebPBEConfig, WEbStringPBEConfig가 있다.

public void encryptSimpleTest() {
	SimplePBEConfig config = new SimplePBEConfig();
	config.setAlgorithm("PBEWithMD5AndDES");
	config.setPassword("somePassword");
	config.setPoolSize(2);
	config.setSaltGenerator(new StringFixedSaltGenerator("someFixedSalt"));

	PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
	encryptor.setConfig(config);
	
	String str = "testString";
	String encStr = encryptor.encrypt(str);
	String decStr = encryptor.decrypt(encStr);
	log.debug("str : {}, encStr : {}, decStr : {}", str, encStr, decStr);
}

상세한 config 사용법은 생략한다.

AES 암호화 사용하기

http://www.jasypt.org/bouncy-castle.html

AES 암호화를 사용하려면 Bouncy Castle JCE Provider를 확장해서 사용해야 한다.

<dependency>
	<groupId>org.bouncycastle</groupId>
	<artifactId>bcprov-jdk15on</artifactId>
	<version>1.61</version>
</dependency>

해당 라이브러리를 추가 후 provider를 추가하여 사용할 수 있는데 아래 두 가지 방법 중 하나를 선택한다.

Security에 추가하기

Security.addProvider(new BouncyCastleProvider());
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
encryptor.setPassword("somePassword");
encryptor.setAlgorithm("PBEWITHSHA256AND128BITAES-CBC-BC");

encryptor에 추가하기

PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
encryptor.setProvider(new BouncyCastleProvider());
encryptor.setPassword("somePassword");
encryptor.setAlgorithm("PBEWITHSHA256AND128BITAES-CBC-BC");

추가하고 사용 가능한 알고리즘 목록을 확인해보면 *-BC로 끝나는 알고리즘이 추가된 것을 확인할 수 있다. BC가 접미사인 알고리즘이 Bouncy Castle JCE Provider에서 제공하는 알고리즘이다.

추가 사항

N개의 password를 가진 N개의 encryptor를 만들어 대량의 암 복호화를 테스트에서 StringFixedSaltGenerator를 사용하는 경우 첫 encryptor 이후 encryptor에서 복호화가 올바르게 되지 않았다.

Standard, Pooled encryptor 둘 다 해당 동일한 문제가 발생했고 단일 encryptor를 사용할 경우엔 문제없었다.

BouncyCastleProvider를 추가하면 N개 encryptor 복호화에 문제가 발생하지 않았다. (이 문제가 해결된 이유는 모르겠음)

대량의 encryptor를 생성하는 경우 encryptor 1개당 대략 0.8Mb의 memory를 점유하여 메모리 할당을 크게 해야 하는 문제가 있다. 170여 개 사용 시 기존 250mb의 메모리를 사용하던 웹서버가 순식간에 550mb의 메모리를 점유하여 out of memory 가 발생하였다.

되도록 encryptor를 많이 만들지 않고 사용하게 좋을 듯싶다.

반응형
profile

파란하늘의 지식창고

@Bluesky_

내용이 유익했다면 광고 배너를 클릭 해주세요