REST API를 통해 카드 결제 등의 사용자 정보를 요청/응답하는 경우가 빈번해지며

데이터를 암호화하는 과정이 많아져 이번 기회에 알고리즘에 대한 설명보다는

사용하는 방법에 초점을 맞춰 정리를 하고자 한다.

 

1. AES (Advanced Encryptrion Standard) 암호화

현재 널리 쓰이는 암호화 방식 중 미국 표준기구(NIST)에서 만든 AES 방식을 사용하기로 한다.

AES 방식은 타 알고리즘에 비해 보편적이며 효과적인 방식이다.

이에 한국에서도 AES와 비슷하게 만든 SEED 암호화를 사용하며, 방식 또한 매우 유사하다.

AES 암호화는 대칭키를 사용한다. 즉, 암/복호화를 하는 대상이 동일한 키를 가지고 있어야 한다.

이 키는 암/복호화에 핵심적인 요소이므로 반드시 노출되어서는 안되기에 비밀키로 불린다.

 

AES는 비밀키의 크기에 따라 다르게 분류된다.

  • AES - 128 :: 16Byte 비밀키 사용 (10라운드)
  • AES - 192 :: 24Byte 비밀키 사용 (12라운드)
  • AES - 256 :: 32Byte 비밀키 사용 (14라운드)

당연히 비밀키의 크기가 클 수록 더욱 안전한 보안이 가능하다.

현재 가장 보편적으로 사용하는 암호화답게 JAVA진영에서도 표준으로 채용하여

javax.crypto 에 정의되어있다. 

  • javax.crypto.spec.SecretKeySpec
  • javax.crypto.spec.IvParameterSpec
  • javax.crypto.Cipher

 

2. BASE64 인코딩

간단히 말하자면 Binary 데이터를 ASCII 형태의 Text로 변환해주는 인코딩 방식이다.

국제 표준과 같이 사용되는 ASCII로 변환하는 경우 아래와 같은 장점이 있다.

  • 데이터를 송/수신할 때 상대방의 Character Set을 신경쓸 필요가 없다.
  • HTML/CSS에서 Binary로 전달되는 미디어 정보를 깨짐없이 전달이 가능하다.

BASE64가 인코딩 하는 방식은 생각보다 단순하다.

Binary 데이터를 6자리씩 자른 후 64진법 (2^6 = 64) 으로 변환한다.

그 후 아래의 표를 참조하여 Text 데이터로 변환한다.

디코딩의 경우로 위의 표대로 Text -> Binary 로 변환할 수 있으므로 Charater Set에 대한 영향을 받지 않는다.

이러한 장점 덕분에 REST를 사용한 API개발에서도 BASE64 인코딩 방식을 널리 사용하고 있다.

 

BASE64 인코딩은 Apache 재단에서 제공해주며 별도 라이브러리를 추가해야 한다.

메이븐 레파지토리에서 쉽게 구할 수 있다.

 

https://mvnrepository.com/artifact/commons-codec/commons-codec

 

 

3. 사용 방법

암호화 하는 과정은 단순하다.

 

# 송신측

  • 데이터를 비밀키를 이용하여 암호화 
  • 암호화된 데이터를 BASE64 인코딩

#수신측

  • 수신한 데이터를 BASE64 디코딩
  • 데이터를 비밀키를 이용하여 복호화

암호화를 하는 과정은 구체적으로 AES/CBC/PKCS5Padding 방식을 사용한다.

  • CBC(Cipher-Block Chaining)
    - 블록 암호화의 대표적 방식
  • PKCS5Padding
    - 블록 암호화를 위해 데이터를 특정 크기로 맞추는 방법으로 8Byte 단위로 블록화하며 빈공간이 발생하는 경우
      의미없는 문자로 채움.
  • IV(Initialization Vector)
    - CBC 방식은 데이터를 분할한 블록끼리 XOR 연산을 하는데 첫 블록은 XOR연산을 수행할 블록이 없으므로
       IV를 사용하여 첫 블록을 연산한다.
    - IV값은 비밀키와 함께 송/수신자가 동일하게 맞춰야 한다. IV가 비밀키와 다른 경우 외부에 공개되어도 상관없다.
    - IV는 두 번째 키로도 사용되기에 비밀키와 동일한 크기로 맞춰야 한다.

 

AES에 대한 방식이 이해되었다면 아래의 코드를 분석하여 적용하는 것에 큰 무리가 없을 것이다.

// 암호화 
public static String encrypte(String msg, String enc_key) throws Exception {
		byte[] bKey = enc_key.getBytes();
		
        // IV 생성
        IvParameterSpec ivParameterSpec = new IvParameterSpec(bKey);
		
        // 비밀키 생성
		SecretKey secretKey = new SecretKeySpec(bKey, "AES");
		
        // 암호화방식 지정 : AES/CBC/PKCS5Padding
		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
		cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
		
		byte[] encrypted = cipher.doFinal(msg.getBytes("UTF-8"));
		
        // 암호화된 데이터를 BASE64 인코딩 
        String enc_data = new String(Base64.encodeBase64(encrypted));
		return enc_data;
	}

// 복호화
public static String decrypt(String msg, String enc_key) throws Exception{
		byte[] bKey = enc_key.getBytes();
		
        // IV 생성
		IvParameterSpec ivParameterSpec = new IvParameterSpec(bKey);
		
        // 비밀키 생성
		SecretKey secretKey = new SecretKeySpec(bKey, "AES");
		
        // 복호화방식 지정 : 암호화 방식과 동일
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
		cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
		
        // BASE64 디코딩
		byte[] decrypted = Base64.decodeBase64(msg.getBytes());
		
        // 디코딩된 데이터를 복호화
		return new String(cipher.doFinal(decrypted), "UTF-8");
    }

'JAVA' 카테고리의 다른 글

HttpURLConnection으로 POST / GET 연습  (0) 2022.10.11
JAVA / JSON 연습  (0) 2022.09.18
BufferedInputStream  (0) 2022.08.28
BigDecimal  (0) 2022.08.28
제너릭  (0) 2022.08.28

+ Recent posts