더북(TheBook)

암호화 실습

지금까지 살펴 본 DBMS_CRYPTO 패키지를 사용해 문자열 데이터를 암호화하고 이를 다시 복호화 하는 익명 블록을 만들어 보자.

입력

    DECLARE
      input_string  VARCHAR2 (200) := 'The Oracle';  -- 암호화할 VARCHAR2 데이터
      output_string VARCHAR2 (200); -- 복호화된 VARCHAR2 데이터

      encrypted_raw RAW (2000); -- 암호화된 데이터
      decrypted_raw RAW (2000); -- 복호화할 데이터

      num_key_bytes NUMBER := 256/8; -- 암호화 키를 만들 길이 (256 비트, 32 바이트)
      key_bytes_raw RAW (32);        -- 암호화 키

      -- 암호화 슈트
      encryption_type PLS_INTEGER;

    BEGIN
     -- 암호화 슈트 설정
     encryption_type := DBMS_CRYPTO.ENCRYPT_AES256 + -- 256비트 키를 사용한 AES 암호화
                        DBMS_CRYPTO.CHAIN_CBC +      -- CBC 모드
                        DBMS_CRYPTO.PAD_PKCS5;       -- PKCS5로 이루어진 패딩

       DBMS_OUTPUT.PUT_LINE ('원본 문자열: ' || input_string);

       -- RANDOMBYTES 함수를 사용해 암호화 키 생성
       key_bytes_raw := DBMS_CRYPTO.RANDOMBYTES (num_key_bytes);

       -- ENCRYPT 함수로 암호화를 한다. 원본 문자열을 UTL_I18N.STRING_TO_RAW를 사용해 RAW 타입으로 변환한다.
       encrypted_raw := DBMS_CRYPTO.ENCRYPT ( src => UTL_I18N.STRING_TO_RAW (input_string, 'AL32UTF8'),
                                              typ => encryption_type,
                                              key => key_bytes_raw
                                            );

       -- 암호화된 RAW 데이터를 한번 출력해보자
       DBMS_OUTPUT.PUT_LINE('암호화된 RAW 데이터: ' || encrypted_raw);
       -- 암호화 한 데이터를 다시 복호화 ( 암호화했던 키와 암호화 슈트는 동일하게 사용해야 한다. )
       decrypted_raw := DBMS_CRYPTO.DECRYPT ( src => encrypted_raw,
                                              typ => encryption_type,
                                              key => key_bytes_raw
                                            );

       -- 복호화된 RAW 타입 데이터를 UTL_I18N.RAW_TO_CHAR를 사용해 다시 VARCHAR2로 변환
       output_string := UTL_I18N.RAW_TO_CHAR (decrypted_raw, 'AL32UTF8');

       -- 복호화된 문자열 출력
       DBMS_OUTPUT.PUT_LINE ('복호화된 문자열: ' || output_string);
    END;

결과

    원본 문자열: The Oracle
    암호화된 RAW 데이터: 139CA77B810FDCF9AC0B2113D8FCCF67
    복호화된 문자열: The Oracle

결과를 보면 ‘The Oracle’이란 문자열을 ENCRYPT 함수를 사용해 RAW 타입(139CA77B810FDCF9AC0B2113D8FCCF67)으로 암호화 한 후, 이를 다시 DECRYPT 함수를 사용해 복호화했다. 암호화하려면 키가 필요한데 키 값은 RANDOMBYTES 함수를 사용해 생성했고 암호화 알고리즘은 AES를 사용했다. 다시 한 번 말하지만 암호화된 데이터를 복호화하려면 암호화 할 때 사용했던 키와 암호화 슈트를 사용해서 복호화해야 한다.

ENCRYPT나 DECRYPT 프로시저는 암호화 대상 데이터, 암호화 및 복호화된 데이터를 BLOB로 변환해야 하는데 보통 암호화 대상 데이터는 문자나 숫자형이 대부분이며 이를 BLOB 타입으로 변환하는 것은 매우 번거롭다. 따라서 보통은 프로시저 대신 ENCRYPTDECRYPT 함수를 사용한다.

이번에는 단방향 암호화 해시 함수인 HASH와 MAC 함수를 사용해 보자. 이 함수는 단방향이므로 복호화가 매우 어렵고 통상 입력 값에 따라 암호화된 데이터를 비교함으로써 입력 값을 검증하는 데 사용된다.

입력

    DECLARE
      input_string  VARCHAR2 (200) := 'The Oracle';  -- 입력 VARCHAR2 데이터
      input_raw     RAW(128);                        -- 입력 RAW 데이터

      encrypted_raw RAW (2000); -- 암호화 데이터

      key_string VARCHAR2(8) := 'secret';  -- MAC 함수에서 사용할 비밀 키
      raw_key RAW(128) := UTL_RAW.CAST_TO_RAW(CONVERT(key_string,'AL32UTF8','US7ASCII')); -- 비밀키를 RAW 타입으로 변환

    BEGIN
      -- VARCHAR2를 RAW 타입으로 변환
      input_raw := UTL_I18N.STRING_TO_RAW (input_string, 'AL32UTF8');

      DBMS_OUTPUT.PUT_LINE('----------- HASH 함수 -------------');
    encrypted_raw := DBMS_CRYPTO.HASH( src => input_raw,
                                         typ => DBMS_CRYPTO.HASH_SH1);

      DBMS_OUTPUT.PUT_LINE('입력 문자열의 해시값 : ' || RAWTOHEX(encrypted_raw));

      DBMS_OUTPUT.PUT_LINE('----------- MAC 함수 -------------');
      encrypted_raw := DBMS_CRYPTO.MAC( src => input_raw,
                                        typ => DBMS_CRYPTO.HMAC_MD5,
                                        key => raw_key);

      DBMS_OUTPUT.PUT_LINE('MAC 값 : ' || RAWTOHEX(encrypted_raw));
    END;

결과

    ----------- HASH 함수 -------------
    입력 문자열의 해시값 : B095432571F215D1B60DDFCAEA9E483F5E24A084
    ----------- MAC 함수 -------------
    MAC 값 : 10722EA8E9CB57CE953E576D30D87260

HASH나 MAC 함수는 보통 비밀번호를 체크하는 데 많이 사용된다. 예를 들어 사용자가 아이디와 비밀번호를 입력하면 이를 체크해 로그인하는 프로그램이 있다고 하자. 대부분은 사용자 테이블에 아이디와 비밀번호를 저장해 둘 텐데 비밀번호 컬럼에 사용자가 입력한 비밀번호를 그대로 저장해 놓는 것이 아니라, 비밀번호로 사용자가 입력한 값이나 이 값과 또 다른 임의의 다른 값을 결합해 이를 HASH나 MAC 함수의 입력 값으로 받아 반환된 결과 값을 비밀번호 컬럼에 저장한다. 이렇게 처리하면 시스템 관리자도 해당 테이블을 조회해 본들 암호화된 값이 들어가 있으므로 사용자의 진짜 비밀번호를 알 수 없고 오직 해당 사용자만이 알 수 있다.

그렇다면 만약 사용자가 비밀번호를 분실했다면 어떻게 해야 할까? 먼저 무작위로 신규 비밀번호를 생성하고 이 번호를 이용해 HASHMAC 함수를 태워 변환된 값을 비밀번호 컬럼에 저장해 놓는다. 그리고 나서 사용자에게 신규 비밀번호를 알려준 뒤, 새로운 비밀번호로 변경하게끔 유도해야 한다. 사용자가 비밀번호를 변경하면 변경한 신규 비밀번호를 다시 HASHMAC 함수의 매개변수로 받아 그 반환 값을 비밀번호 컬럼에 최종적으로 저장된다. 좀 복잡하긴 하지만 이렇게 처리하면 시스템 관리자조차 사용자의 비밀번호를 알 수 없다.

지금까지 DBMS_CRYPTO 패키지를 사용해 데이터를 암호화하고 복호화 기법을 살펴봤다. 다시 한 번 강조하지만 데이터 암호화만으로는 시스템의 안전성을 충분히 보장할 수는 없고 다만 몇 가지 추가적인 안전장치를 부여한다는 차원이라고 생각하는 것이 맞다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.