암호통제 지침 중 여러 조항이 있지만 개발과 관련있는 부분이다
아래 지침내용은 회사 내부 지침 일부 발췌
기밀성을 위한 암호 기술은 최소 128비트 이상의 키길이를 사용하는 암호화키를 사용하는 안정성이 입증된 대칭키 암호화 알고리즘을 이용한다
(암호화 대상 정보: 고유식별정보, 신용카드번호, 계좌번호, 바이오 정보 등)
보안강도 | 대칭키 암호 알고리즘 | 안정성 |
---|---|---|
80bit 미만 | DES | 권고하지 않음 |
80bit | 2TDEA | 권고하지 않음 |
112bit | 3TDEA | 권고하지 않음 |
128bit | SEED, HIGHT, ARIA-128, AES-128 | 권고 |
192bit | ARIA-192, AES-192 | 권고 |
256bit | ARIA-256, AES-256 | 권고 |
Oracle DBMS_CRYPTO
앞서 포스팅 했던 대로 오라클 패키지 함수 내 암호화 키가 평문으로 함수 매개변수에 삽입되어 있어 수정해야 했다. 기존 암호화된 데이터가 AES128 방식으로 암호화 되어 있어 자바 소스에서도 AES128 암호화를 선택했다
AS-IS:
1. 평문 암호키가 저장된 오라클 패키지 함수 호출
TO-BE:
1. 암호화키를 암/복호화 할 모듈 생성
2. 운영데이터 암/복호화 할 암호화키를 모듈로 암호화하여 txt 파일에 저장
3. txt파일 WAS 서버에 저장
4. WAS에서 AES128.jar 사용하여 txt파일의 암호키 복호화 후 오라클 패키지 함수 호출
package com.erp.common;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AES128 {
//IV
private String ips;
//키 클래스
private Key keySpec;
//생성자
public AES128() {
//암호화키를 암/복호화하는 암호화키 16byte
String key = "test1234ttest1234";
try {
//암호화키 저장할 byte[] 변수
byte[] keyBytes = new byte[16];
byte[] b = key.getBytes("UTF-8");
System.arraycopy(b, 0, keyBytes, 0, keyBytes.length);
//비밀키 클래스 선언
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
//운영중인 오라클 패키지 함수에서 IV를 사용하지 않기 때문에 null 16bytes
this.ips = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
//AES방식과 암호화 키 값을 Key 클래스에 저장
this.keySpec = keySpec;
} catch (Exception e) {
e.printStackTrace();
}
}
//생성자 오버로딩
public AES128(String key) {
try {
byte[] keyBytes = new byte[16];
byte[] b = key.getBytes("UTF-8");
System.arraycopy(b, 0, keyBytes, 0, keyBytes.length);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
this.ips = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; //null 16bytes
this.keySpec = keySpec;
} catch (Exception e) {
e.printStackTrace();
}
}
//암호화
public String encrypt(String plainText) {
//암/복호화 암호 기능 제공 클래스 선언
Cipher cipher;
try {
//암호방식, 체인방식, 패딩방식 선언 및 인스턴스 득
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//인스턴스 얻은 후 암호화키와 IV로 암호 초기화
cipher.init(Cipher.ENCRYPT_MODE, keySpec
, new IvParameterSpec(ips.getBytes()) );
//암호화 실행1
byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
//오라클 DBMS_CRYPTO 함수에서 HEX String을 반환하기 떄문에 byte[]->HEX 변환
String encryptStr = new String(byteArrayToHex(encrypted).toUpperCase());
//암호화된 문자열 반환
return encryptStr;
} catch (Exception e) {
return null;
}
}
//복호화
public String decrypt(String encryptStr) {
try {
//암호방식, 체인방식, 패딩방식 선언 및 인스턴스 득
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//인스턴스 얻은 후 암호화키와 IV로 암호 초기화
cipher.init(Cipher.DECRYPT_MODE, keySpec
, new IvParameterSpec(ips.getBytes("UTF-8")));
//HEX String->byte[] 로 변환
byte[] byteStr = hexToByteArray(encryptStr);
//복호화 실행
String decryptStr = new String(cipher.doFinal(byteStr), "UTF-8");
return decryptStr;
} catch (Exception e) {
return null;
}
}
//byte[]->hex String
private String byteArrayToHex(byte[] encrypted) {
if(encrypted == null || encrypted.length == 0){
return null;
}
StringBuffer sb = new StringBuffer();
for (byte b : encrypted) {
//앞 빈자리 0으로 채우는 16진수 (16진수 2자리가 1byte)
sb.append(String.format("%02x", b));
}
return sb.toString();
}
//hex String->byte[]
private byte[] hexToByteArray(String hex) {
if(hex == null || hex.length() == 0){
return null;
}
//16진수 2자리가 1byte
byte[] byteArray = new byte[hex.length() / 2 ];
for(int i = 0; i<byteArray.length; i++){
//2자리씩 16진수로 변환
byteArray[i] = (byte) Integer.parseInt(hex.substring(2*i, 2*i+2), 16);
}
return byteArray;
}
}
Util클래스에 static 함수로 구현
import java.io.File;
import java.util.Scanner;
public class Util{
public static String getEncKey(){
AES128 aes128 = new AES128();
String enc_key = null;
try {
//enc_key.txt 내용 :
//25570DDCCE2517C85728B94A051AF276A3D07E616EE120540EE4EFD7C88ED357
File f1 = new File("프로젝트 경로" + "/enc_key.txt");
Scanner sc = new Scanner(f1);
while (sc.hasNextLine()) {
String data = sc.nextLine();
enc_key = aes128.decrypt(data);
}
sc.close();
}
catch (Exception e) {
e.printStackTrace();
}
finally{
return enc_key;
}
}
}
//서비스 클래스 변수 선언
private String enc_key = Util.getEncKey();
//서비스 함수 내 쿼리실행 변수에 할당
public String getHpNo(DataMap paramData){
paramData.put("enc_key", enc_key);
}
SELECT CRYPTO.DECRYPT(HP_NO, ${enc_key}) FROM DUAL
ISMS 인증을 앞두고 수정한 건이라 결함으로 판단될 경우 보완하여 수정 예정