encrypt-and-decrypt-data-with-oracle-dbms_crypto.md
devcondadatabaseencrypt-and-decrypt-data-with-oracle-dbms_crypto.md

Encrypt and Decrypt Data with Oracle DBMS_CRYPTO

Written by

in

DBMS_CRYPTO is a library that provides an interface to encrypt and decrypt stored data. It supports several encryption algorithms. This covers reversible encryption with the AES algorithm.

Unlike one-way hashing, PII handling usually needs reversible encryption: store data encrypted, then decrypt it when showing it to users. Here we use AES-128 with a 128-bit (16-byte) symmetric key.

Install DBMS_CRYPTO

1. Log in to the Oracle account with SYSDBA privileges:

sqlplus / as sysdba

2. Add the CRYPTO package:

@$ORACLE_HOME/rdbms/admin/dbmsobtk.sql
@$ORACLE_HOME/rdbms/admin/prvtobtk.plb

3. Grant package execute privileges:

grant execute on dbms_crypto to public;
grant execute on dbms_obfuscation_toolkit to public;

Encryption algorithms

AlgorithmDescription
DESA symmetric-key algorithm that splits data into 64-bit blocks and uses a 56-bit key for each. Security is no longer considered sufficient, so usage is declining.
3DESApplies the DES algorithm two or three times. Uses 112-bit and 168-bit keys. Because it repeats DES, encryption and decryption take longer than other symmetric algorithms.
AES 128, 192, 256Splits data into 128-bit blocks and uses 128-, 192-, or 256-bit keys. Designed to be stronger than DES and established as an encryption standard.

Chaining modes

ModeDescription
ECBEncrypts each original data block independently.
CBC (Cipher Block Chaining)XORs the current block with the previously encrypted block, then encrypts it. Prevents identical plaintext from producing identical ciphertext, which can happen with ECB.

Padding modes

ModeDescription
PKCS5PKCS #5 (Password-Based Cryptography Standard) padding.
NONENo padding. Encryption fails unless the data size is a multiple of the block size (128 bits), so you must check the data size.
ZEROFills remaining bytes in the last block with zeros. Should be used only for data that marks the end with a zero value, such as some strings.

Function signatures

DBMS_CRYPTO.ENCRYPT (
  src          IN RAW,          -- data to encrypt
  cipher_type  IN PLS_INTEGER,  -- algorithm, chain, and padding
  key          IN RAW,          -- encryption key
  init_vector  IN RAW DEFAULT NULL -- IW: NULL or 0
) RETURN RAW;

DBMS_CRYPTO.DECRYPT(
  src IN RAW,          -- data to decrypt
  typ IN PLS_INTEGER,
  key IN RAW,
  iv  IN RAW DEFAULT NULL
) RETURN RAW;

Create a package and use the functions

test1234test1234 is the default encryption key and is used when no key parameter is passed.

Package specification

CREATE OR REPLACE PACKAGE TEST.CRYPTO IS
  FUNCTION ENCRYPT (
    input_string IN VARCHAR2,
    key_data IN VARCHAR2 := 'test1234test1234'
  ) RETURN RAW;

  FUNCTION DECRYPT (
    input_string IN VARCHAR2,
    key_data IN VARCHAR2 := 'test1234test1234'
  ) RETURN VARCHAR2;
END CRYPTO;

Package body

Uses the AES128 + CBC + PKCS5 combination.

CREATE OR REPLACE PACKAGE BODY TEST.CRYPTO IS
  SQLERRMSG VARCHAR2(255);
  SQLERRCDE NUMBER;

  -- Encrypt function
  FUNCTION encrypt (
    input_string IN VARCHAR2,
    key_data IN VARCHAR2 := 'test1234test1234'
  ) RETURN RAW IS
    input_raw RAW(1024);
    -- Convert plaintext key to RAW
    key_raw RAW(16) := UTL_RAW.CAST_TO_RAW(key_data);
    v_out_raw RAW(1024);
    -- Use AES algorithm, CBC chaining, PKCS5 padding
    AES_CBC_PKCS5 CONSTANT PLS_INTEGER :=
      DBMS_CRYPTO.ENCRYPT_AES128
      + DBMS_CRYPTO.CHAIN_CBC
      + DBMS_CRYPTO.PAD_PKCS5;
  BEGIN
    IF input_string IS NULL THEN
      RETURN NULL;
    END IF;

    -- Convert the string to encrypt into RAW
    input_raw := UTL_I18N.STRING_TO_RAW(input_string, 'AL32UTF8');
    v_out_raw := DBMS_CRYPTO.ENCRYPT(
      src => input_raw,
      typ => AES_CBC_PKCS5,
      key => key_raw
    );
    -- Return encrypted RAW data
    RETURN v_out_raw;
  END encrypt;

  -- Decrypt function
  FUNCTION decrypt (
    input_string IN VARCHAR2,
    key_data IN VARCHAR2 := 'test1234test1234'
  ) RETURN VARCHAR2 IS
    -- Convert plaintext key to RAW
    key_raw RAW(16) := UTL_RAW.CAST_TO_RAW(key_data);
    output_raw RAW(1024);
    v_out_string VARCHAR2(1024);
    -- Use AES algorithm, CBC chaining, PKCS5 padding
    AES_CBC_PKCS5 CONSTANT PLS_INTEGER :=
      DBMS_CRYPTO.ENCRYPT_AES128
      + DBMS_CRYPTO.CHAIN_CBC
      + DBMS_CRYPTO.PAD_PKCS5;
  BEGIN
    IF input_string IS NULL THEN
      RETURN NULL;
    END IF;

    output_raw := DBMS_CRYPTO.DECRYPT(
      src => input_string,
      typ => AES_CBC_PKCS5,
      key => key_raw
    );
    -- Convert decrypted RAW data to a UTF-8 string
    v_out_string := UTL_I18N.RAW_TO_CHAR(output_raw, 'AL32UTF8');
    -- Return decrypted string data
    RETURN v_out_string;
  END decrypt;
END CRYPTO;

Packages, functions, and variables used

TypeNameDescription
Data typeRAWStores binary data or a hexadecimal byte string (maximum length: 2000 bytes).
Data typePLS_INTEGERUnlike INTEGER or NUMBER, which use library-based numeric operations, PLS_INTEGER uses machine arithmetic and is faster.
PackageUTL_RAWProvides functions related to RAW data.
FunctionUTL_RAW.CAST_TO_RAWConverts a STRING to RAW.
PackageUTL_I18NProvides compatibility features across countries and languages.
FunctionUTL_I18N.STRING_TO_RAWConverts a STRING to RAW.

UTF8 vs AL32UTF8

Character setUnicode versionUnicode encodingVariable lengthSupplementary charactersOracle version
AL32UTF83.0~3.1UTF-84 bytesSupports U+10000~U+10FFFF (emoji and more CJK characters)9i and later
UTF82.1~3.0UTF-83 bytesNo8~9i

Because the default database character set in this Oracle environment is AL32UTF8, the two functions return the same result.

-- Current database character set
-- Result: NLS_CHARACTERSET AL32UTF8
SELECT * FROM NLS_DATABASE_PARAMETERS
WHERE PARAMETER = 'NLS_CHARACTERSET';

-- Result: 5445535431323334
SELECT UTL_RAW.CAST_TO_RAW('test1234') FROM DUAL;

-- Result: 5445535431323334
SELECT UTL_I18N.STRING_TO_RAW('test1234', 'AL32UTF8') FROM DUAL;

Use the created functions

-- Encrypt
SELECT CRYPTO.ENCRYPT('01012345678') FROM DUAL;

-- Decrypt
SELECT CRYPTO.DECRYPT('357788CC5A3C8551550D62EA308CCF30') FROM DUAL;

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *