oracle에 펑션을 생성하여 AES, SHA 암호화를 진행해보겠습니다.
첫번째로 oracle이 제공하는 DBMS_CRYPTO 패키지를 이용하여 진행하겠습니다.
토드에서 코딩~~, SHA는 일단 없습니다. oracle 12이상 버전에서만 SHA256을 지원하고 아래 버전에서는 128만 가능합니다. ㅠㅠ
package spec
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | CREATE OR REPLACE PACKAGE pkg_crypto AS FUNCTION fn_encrypt_aes128 (p_string IN VARCHAR2) RETURN VARCHAR2; FUNCTION fn_decrypt_aes128 (p_string IN VARCHAR2) RETURN VARCHAR2; FUNCTION fn_encrypt_aes256 (p_string IN VARCHAR2) RETURN VARCHAR2; FUNCTION fn_decrypt_aes256 (p_string IN VARCHAR2) RETURN VARCHAR2; END pkg_crypto; / | cs |
package body
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | CREATE OR REPLACE PACKAGE BODY pkg_crypto AS --key v_key VARCHAR2 (32) := '01234567890123456789012345678901'; FUNCTION fn_encrypt_aes128 (p_string IN VARCHAR2) RETURN VARCHAR2 IS --결과 담을 변수 v_encrypted RAW (2000); --암호화 키, 128.. 16바이트 v_key_bytes RAW (16); --암호화 초기화벡터값.. 키와 같게 v_iv_bytes RAW (16); --암호화 알고리즘 aes128, 운용모드, 패딩 v_encryption_type PLS_INTEGER := DBMS_CRYPTO.encrypt_aes128 + DBMS_CRYPTO.chain_cbc + DBMS_CRYPTO.pad_pkcs5; BEGIN v_key_bytes := UTL_I18N.string_to_raw (SUBSTR (v_key, 0, 16), 'AL32UTF8'); v_iv_bytes := UTL_I18N.string_to_raw (SUBSTR (v_key, 0, 16), 'AL32UTF8'); v_encrypted := DBMS_CRYPTO.encrypt (src => UTL_I18N.string_to_raw (p_string, 'AL32UTF8'), typ => v_encryption_type, key => v_key_bytes, iv => v_iv_bytes); --base64 RETURN UTL_RAW.cast_to_varchar2 (UTL_ENCODE.base64_encode (v_encrypted)); EXCEPTION WHEN OTHERS THEN RETURN NULL; END fn_encrypt_aes128; FUNCTION fn_decrypt_aes128 (p_string IN VARCHAR2) RETURN VARCHAR2 IS --결과 담을 변수 v_decrypted RAW (2000); --복호화 키, 128.. 16바이트 v_key_bytes RAW (16); --복호화 초기화벡터값.. 키와 같게 v_iv_bytes RAW (16); --복호화 알고리즘 aes256, 운용모드, 패딩 v_encryption_type PLS_INTEGER := DBMS_CRYPTO.encrypt_aes128 + DBMS_CRYPTO.chain_cbc + DBMS_CRYPTO.pad_pkcs5; BEGIN v_key_bytes := UTL_I18N.string_to_raw (SUBSTR (v_key, 0, 16), 'AL32UTF8'); v_iv_bytes := UTL_I18N.string_to_raw (SUBSTR (v_key, 0, 16), 'AL32UTF8'); v_decrypted := DBMS_CRYPTO.decrypt (src => UTL_ENCODE.base64_decode (UTL_RAW.cast_to_raw (p_string)), typ => v_encryption_type, key => v_key_bytes, iv => v_iv_bytes); RETURN UTL_I18N.raw_to_char (v_decrypted, 'AL32UTF8'); EXCEPTION WHEN OTHERS THEN RETURN NULL; END fn_decrypt_aes128; FUNCTION fn_encrypt_aes256 (p_string IN VARCHAR2) RETURN VARCHAR2 IS --결과 담을 변수 v_encrypted RAW (2000); --암호화 키 v_key_bytes RAW (32); --암호화 초기화벡터값.. 키와 같게 v_iv_bytes RAW (16); --암호화 알고리즘 aes256, 운용모드, 패딩 v_encryption_type PLS_INTEGER := DBMS_CRYPTO.encrypt_aes256 + DBMS_CRYPTO.chain_cbc + DBMS_CRYPTO.pad_pkcs5; BEGIN v_key_bytes := UTL_I18N.string_to_raw (SUBSTR (v_key, 0, 32), 'AL32UTF8'); v_iv_bytes := UTL_I18N.string_to_raw (SUBSTR (v_key, 0, 16), 'AL32UTF8'); v_encrypted := DBMS_CRYPTO.encrypt (src => UTL_I18N.string_to_raw (p_string, 'AL32UTF8'), typ => v_encryption_type, key => v_key_bytes, iv => v_iv_bytes); --base64 RETURN UTL_RAW.cast_to_varchar2 (UTL_ENCODE.base64_encode (v_encrypted)); EXCEPTION WHEN OTHERS THEN RETURN NULL; END fn_encrypt_aes256; FUNCTION fn_decrypt_aes256 (p_string IN VARCHAR2) RETURN VARCHAR2 IS --결과 담을 변수 v_decrypted RAW (2000); --복호화 키 v_key_bytes RAW (32); --복호화 초기화벡터값.. 키와 같게 v_iv_bytes RAW (16); --복호화 알고리즘 aes256, 운용모드, 패딩 v_encryption_type PLS_INTEGER := DBMS_CRYPTO.encrypt_aes256 + DBMS_CRYPTO.chain_cbc + DBMS_CRYPTO.pad_pkcs5; BEGIN v_key_bytes := UTL_I18N.string_to_raw (SUBSTR (v_key, 0, 32), 'AL32UTF8'); v_iv_bytes := UTL_I18N.string_to_raw (SUBSTR (v_key, 0, 16), 'AL32UTF8'); v_decrypted := DBMS_CRYPTO.decrypt (src => UTL_ENCODE.base64_decode (UTL_RAW.cast_to_raw (p_string)), typ => v_encryption_type, key => v_key_bytes, iv => v_iv_bytes); RETURN UTL_I18N.raw_to_char (v_decrypted, 'AL32UTF8'); EXCEPTION WHEN OTHERS THEN RETURN NULL; END fn_decrypt_aes256; END pkg_crypto; / | cs |
실행스크립트
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | DECLARE v_str1 VARCHAR2 (1000) := '암호화되지 않은 문자'; v_str2 VARCHAR2 (1000); v_str3 VARCHAR2 (1000); v_str4 VARCHAR2 (1000); v_str5 VARCHAR2 (1000); BEGIN DBMS_OUTPUT.put_line ('plain : ' || v_str1); v_str2 := pkg_crypto.fn_encrypt_aes128 (v_str1); DBMS_OUTPUT.put_line ('AES128 encrypted : ' || v_str2); v_str3 := pkg_crypto.fn_decrypt_aes128 (v_str2); DBMS_OUTPUT.put_line ('AES128 decrypted : ' || v_str3); v_str4 := pkg_crypto.fn_encrypt_aes256 (v_str1); DBMS_OUTPUT.put_line ('AES256 encrypted : ' || v_str4); v_str5 := pkg_crypto.fn_decrypt_aes256 (v_str4); DBMS_OUTPUT.put_line ('AES256 decrypted : ' || v_str5); END; | cs |
실행결과
두번째로 java code를 이용한 암호화를 해보겠습니다.
이전 포스팅의 java 소스를 활용하겠습니다. 이전소스에서 common-codec 라이브러리를 이용했기 때문에 이 라이브러리를 oralce에 다음 명령어를 이용하여 로드합니다. 물른 JDK 기본클래스만 사용한다면 안해도 됩니다. commons-codec의 버전이 1.10이었는데 oralce에 포함되어 있는 jdk가 1.5라서 commons-codec 1.6버전을 로드시키겠습니다.
이렇게 명령어를 실행하면 마지막부분에 아래와 같은 메세지를 보이면서 클래스가 로드됩니다.
Classes Loaded: 76
Resources Loaded: 129
Sources Loaded: 0
Published Interfaces: 0
Classes generated: 0
Classes skipped: 0
Synonyms Created: 0
Errors: 0
현재 oralce이 가지고 있는 jdk의 버전은 펑션을 만들어 다음 쿼리를 실행하면 확인 가능합니다.
1 | SELECT fn_get_java_property ('java.version') FROM DUAL; | cs |
1 2 3 4 5 6 | CREATE OR REPLACE FUNCTION fn_get_java_property (p_prop IN VARCHAR2) RETURN VARCHAR2 IS LANGUAGE JAVA NAME 'java.lang.System.getProperty(java.lang.String) return java.lang.String'; / | cs |
이제 자바소스를 생성합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | DROP JAVA SOURCE CRYPTO; CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED CRYPTO as package com.tistory.aircook; import java.security.MessageDigest; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.CharEncoding; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; public class Crypto { // 키 private final static String KEY = "01234567890123456789012345678901"; // 128bit (16자리) private final static String KEY_128 = KEY.substring(0, 128 / 8); // 256bit (32자리) private final static String KEY_256 = KEY.substring(0, 256 / 8); // AES 128 암호화 public static String encryptAES128(String string) { try { byte[] keyData = KEY_128.getBytes(CharEncoding.UTF_8); // 운용모드 CBC, 패딩은 PKCS5Padding Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // key 와 iv 같게.. // 블록 암호의 운용 모드(Block engine modes of operation)가 CBC/OFB/CFB를 사용할 경우에는 // Initialization Vector(IV), IvParameterSpec를 설정해줘야한다. 아니면 InvalidAlgorithmParameterException 발생 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyData, "AES"), new IvParameterSpec(keyData)); // AES 암호화 byte[] encrypted = cipher.doFinal(string.getBytes(CharEncoding.UTF_8)); // base64 인코딩 byte[] base64Encoded = Base64.encodeBase64(encrypted); // 결과 String result = new String(base64Encoded, CharEncoding.UTF_8); return result; } catch (Exception e) { return null; } } // AES 128복호화 public static String decryptAES128(String string) { try { byte[] keyData = KEY_128.getBytes(CharEncoding.UTF_8); // 운용모드 CBC, 패딩은 PKCS5Padding Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyData, "AES"), new IvParameterSpec(keyData)); // base64 디코딩 byte[] base64Decoded = Base64.decodeBase64(string.getBytes(CharEncoding.UTF_8)); // AES 복화화 byte[] decrypted = cipher.doFinal(base64Decoded); // 결과 String result = new String(decrypted, CharEncoding.UTF_8); return result; } catch (Exception e) { return null; } } // AES 256 암호화 public static String encryptAES256(String string) { try { byte[] key256Data = KEY_256.getBytes(CharEncoding.UTF_8); byte[] key128Data = KEY_128.getBytes(CharEncoding.UTF_8); // 운용모드 CBC, 패딩은 PKCS5Padding Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // key 와 iv 같게.. // 블록 암호의 운용 모드(Block engine modes of operation)가 CBC/OFB/CFB를 사용할 경우에는 // Initialization Vector(IV), IvParameterSpec를 설정해줘야한다. 아니면 InvalidAlgorithmParameterException 발생 // AES 256은 미국만 되는거라. JDK/JRE 패치를 해야된다. // http://www.oracle.com/technetwork/java/javase/downloads/index.html 에서 // Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files for JDK/JRE 8 이런 링크 찾아서 다운 // $JAVA_HOME\jre\lib\security 아래에 local_policy.jar, US_export_policy.jar 파일 overwrite! // iv값이 16자리가 아니면.. // java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long 발생 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key256Data, "AES"), new IvParameterSpec(key128Data)); // AES 암호화 byte[] encrypted = cipher.doFinal(string.getBytes(CharEncoding.UTF_8)); // base64 인코딩 byte[] base64Encoded = Base64.encodeBase64(encrypted); // 결과 String result = new String(base64Encoded, CharEncoding.UTF_8); return result; } catch (Exception e) { return null; } } // AES 256복호화 public static String decryptAES256(String string) { try { byte[] key256Data = KEY_256.getBytes(CharEncoding.UTF_8); byte[] key128Data = KEY_128.getBytes(CharEncoding.UTF_8); // 운용모드 CBC, 패딩은 PKCS5Padding Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key256Data, "AES"), new IvParameterSpec(key128Data)); // base64 디코딩 byte[] base64Decoded = Base64.decodeBase64(string.getBytes(CharEncoding.UTF_8)); // AES 복화화 byte[] decrypted = cipher.doFinal(base64Decoded); // 결과 String result = new String(decrypted, CharEncoding.UTF_8); return result; } catch (Exception e) { return null; } } public static String encryptSHA256(String string) { try { MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); byte[] stringBytes = string.getBytes(); int stringBytesLength = stringBytes.length; byte[] dataBytes = new byte[1024]; for (int i = 0; i < stringBytesLength; i++) { dataBytes[i] = stringBytes[i]; } messageDigest.update(dataBytes, 0, stringBytesLength); byte[] encrypted = messageDigest.digest(); // hex, 16진수 // StringBuffer sb = new StringBuffer(); // for (int i = 0; i < encrypted.length; i++) { // sb.append(Integer.toString((encrypted[i] & 0xff) + 0x100, 16).substring(1)); // } // 결과 // String result = sb.toString(); // commons codec lib 사용하면 아래처럼 간단하게.. // String result = Hex.encodeHexString(encrypted); // base64 인코딩 byte[] base64Encoded = Base64.encodeBase64(encrypted); // 결과 String result = new String(base64Encoded, CharEncoding.UTF_8); return result; } catch (Exception e) { return null; } } } / | cs |
생성된 소스를 이용하여 펑션을 정의합니다.
package spec
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | CREATE OR REPLACE PACKAGE pkg_crypto AS FUNCTION fn_encrypt_aes128 (p_string IN VARCHAR2) RETURN VARCHAR2; FUNCTION fn_decrypt_aes128 (p_string IN VARCHAR2) RETURN VARCHAR2; FUNCTION fn_encrypt_aes256 (p_string IN VARCHAR2) RETURN VARCHAR2; FUNCTION fn_decrypt_aes256 (p_string IN VARCHAR2) RETURN VARCHAR2; FUNCTION fn_encrypt_sha256 (p_string IN VARCHAR2) RETURN VARCHAR2; END pkg_crypto; / | cs |
package body
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | CREATE OR REPLACE PACKAGE BODY pkg_crypto AS FUNCTION fn_encrypt_aes128 (p_string IN VARCHAR2) RETURN VARCHAR2 IS LANGUAGE JAVA NAME 'com.tistory.aircook.Crypto.encryptAES128(java.lang.String) return String'; FUNCTION fn_decrypt_aes128 (p_string IN VARCHAR2) RETURN VARCHAR2 IS LANGUAGE JAVA NAME 'com.tistory.aircook.Crypto.decryptAES128(java.lang.String) return String'; FUNCTION fn_encrypt_aes256 (p_string IN VARCHAR2) RETURN VARCHAR2 IS LANGUAGE JAVA NAME 'com.tistory.aircook.Crypto.encryptAES256(java.lang.String) return String'; FUNCTION fn_decrypt_aes256 (p_string IN VARCHAR2) RETURN VARCHAR2 IS LANGUAGE JAVA NAME 'com.tistory.aircook.Crypto.decryptAES256(java.lang.String) return String'; FUNCTION fn_encrypt_sha256 (p_string IN VARCHAR2) RETURN VARCHAR2 IS LANGUAGE JAVA NAME 'com.tistory.aircook.Crypto.encryptSHA256(java.lang.String) return String'; END pkg_crypto; / | cs |
이렇게 생성된 펑션을 실행해보면 AES128, SHA256 잘 동작하나 AES256의 경우는 동작하지 않습니다. JCE패치때문인거 같은데 oracle이 설치되어 있는 디렉토리에서 oracle/product/11.2.0/db_1/jdk/jre/lib/security/ 안에 있는 jdk1.5 버전용 local_policy.jar, US_export_policy.jar를 덮어쓰기 해도 동작을 안합니다. oracle을 내렸다 올려야 되는지.. 내릴수는 없는 상황이라 이부분은 해결을 하지 못했습니다.
'others' 카테고리의 다른 글
AES, SHA 암호화 4, T-SQL (0) | 2015.08.26 |
---|---|
AES, SHA 암호화 3, C# (6) | 2015.08.26 |
AES, SHA 암호화 1, JAVA (1) | 2015.08.26 |
facebook note에 있던 잡다한것들.. (0) | 2013.06.03 |
JSON Object 정렬하기 (0) | 2012.01.30 |