Category Archives: TDE

Oracle 12.2 full database encryption (TDE)

Starting with Oracle 12.2 it is possible to encrypt all Tablespaces including SYSTEM, SYSAUX, TEMP, and UNDO. Off the top of my head I can think of a couple of reasons why encrypting SYSTEM and SYSAUX might be desired:

  • Histograms in SYSAUX might contain sensitive data
  • Application tables/indexes in SYSTEM or SYSAUX Tablespace
  • Hard-coded secrets in PL/SQL objects (yes, I’ve seen it all!)

Strangely enough, the 12.2 TDE FAQ discourages encryption of “internal objects”:

I believe this to be a leftover from previous releases which hasn’t been cleaned up, yet. Because it is supported and the Advanced Security Guide provides plenty of information about it.

Splitting hairs, “Fully Encrypt Databases” is not entirely correct. There is currently no supported way to encrypt PDB$SEED Tablespaces…and I’ve tried for the fun of it:-)
Forcing the PDB$SEED open read-write with “alter pluggable database PDB$SEED open force” and then open the keystore works. I could even create a new encryption key, but it won’t let me activate it:

SQL> show con_name
CON_NAME 
------------------------------
PDB$SEED

SQL> administer key management create encryption key using tag 'PDBSEED_MK_1' force keystore identified by "Strng-4-Scrty" with backup using 'bck1';
keystore altered.

SQL> column key_id new_value new_key_id
SQL> select key_id from v$encryption_keys where con_id = 0 and tag = 'PDBSEED_MK_1';
KEY_ID                                                                        
------------------------------------------------------------------------------
AVVfIyuTLk9sv2EyAw9dJ18AAAAAAAAAAAAAAAAAAAAAAAAAAAAA

SQL> administer key management use encryption key '&new_key_id' identified by "Strng-4-Scrty" with backup using 'bck2';
*
ERROR at line 1:
ORA-46656: operation not valid for container database

I know it doesn’t make a lot of sense as for instance, cloning from a encrypted PDB$SEED would be kind of a Catch-22.

Anyway, back to encrypting Oracle’s “internal” Tablespaces. It’s pretty straight forward. It’s the same as you would setup the database for any TDE encryption.
Add ENCRYPITON_WALLET_LOCATION to your sqlnet.ora

ENCRYPTION_WALLET_LOCATION =
  (SOURCE =
    (METHOD = FILE)
    (METHOD_DATA =
      (DIRECTORY = /u01/app/oracle/admin/$ORACLE_SID/wallet/tde)
    )
  )

Setup a keystore in the CDB$ROOT and generate a new key to use:

alter session set container=cdb$root;

-- create PKCS#12 keystore (ewallet.p12)
administer key management
  create keystore '/u01/app/oracle/admin/DEV1/wallet/tde'
  identified by "Strng-4-Scrty"
;

-- open the keystore
administer key management
  set keystore open
  identified by "Strng-4-Scrty"
  container = current
;

-- create a new encryption key
administer key management
  create encryption key using tag 'DEV1_ROOT_MK_1'
  identified by "Strng-4-Scrty"
  with backup using 'Before-Create-DEV1_ROOT_MK_1'
  container = current
;

-- activate the key for use in this container
--   note: newly created keys have "con_id = 0", as soon as they are activated they get assigned to the respective PDB
column key_id new_value new_key_id
select key_id from v$encryption_keys where con_id = 0 and tag = 'DEV1_ROOT_MK_1';
administer key management
  use encryption key '&new_key_id'
  using tag 'DEV1_ROOT_MK_1'
  identified by "Strng-4-Scrty"
  with backup using 'Before-Activate-DEV1_ROOT_MK_1'
;

-- we also want a local auto-login keystore (cwallet.sso)
--   otherwise manual keystore password input is required during database startup
administer key management
  create local auto_login keystore from keystore '/u01/app/oracle/admin/DEV1/wallet/tde'
  identified by "Strng-4-Scrty"
;

Now, that we have the key setup it’s as simple as an ALTER TABLESPACE statement. We used the default algorithm AES128 when generating the encryption key so we specify the same here.

alter tablespace system
  encryption online using 'AES128' encrypt
  file_name_convert = ('system', 'system-enc')
;
alter tablespace sysaux
  encryption online using 'AES128' encrypt
  file_name_convert = ('sysaux', 'sysaux-enc')
;

Be careful, if the specified algorithm is different from the one of the key the statement will still succeed. You’ll see following warning in the alert.log:

An algorithm different from the database key is specified for SYSTEM tablespace. The algorithm has been overridden to AES128 per V$DATABASE_KEY_INFO view.

Let’s move to my PDB.

alter session set container=pdbdev1;

-- assuming auto-login keystore is now open
-- otherwise open password keystore

administer key management
  create encryption key using tag 'PDBDEV1_MK_1'
  force keystore
  identified by "Strng-4-Scrty"
  with backup using 'Before-Create-PDBDEV1_MK_1'
;

column key_id new_value new_key_id
select key_id from v$encryption_keys where con_id = 0 and tag = 'PDBDEV1_MK_1';
administer key management
  use encryption key '&new_key_id'
  using tag 'PDBDEV1_MK_1'
  force keystore
  identified by "Strng-4-Scrty"
  with backup using 'Before-Activate-PDBDEV1_MK_1'
;

To encrypt the SYSTEM/SYSAUX Tablespace on our PDB you can use the same statements from above:

alter tablespace system
  encryption online using 'AES128' encrypt
  file_name_convert = ('system', 'system-enc')
;
alter tablespace sysaux
  encryption online using 'AES128' encrypt
  file_name_convert = ('sysaux', 'sysaux-enc')
;

According to the documentation, undo, temp, and redo data originating from objects in encrypted tablespaces will also be encrypted.

There’s still reason to encrypt temporary Tablespaces in full:

  • Temporary LOBs
  • Global temporary tables

Encrypting existing TEMP Tablespaces is not supported, we just create a new one and drop the old.

create temporary tablespace tempts_enc
  tempfile '/u02/oradata/DEV1/tempts_enc-001.dbf' size 512M autoextend off
  encryption using 'AES128' encrypt
;
alter database default temporary tablespace tempts_enc;
drop tablespace tempts including contents and datafiles;

Once I have encrypted SYSTEM, SYSAUX, TEMP, and all application Tablespaces there should be no need to encrypt the UNDO Tablespace, right? All undo data is then based on objects from encrypted Tablespaces and should therefore also be encrypted in the UNDO stream. This is to be further investigated. If you have any input, please leave a comment.

One final note: if you want to understand the performance impact of TDE I highly recommend to use SLOB.

TDE safety net

You run your Oracle database with Transparent Data Encryption (TDE) and you store your Master Encryption Keys in a software keystore (wallet)?
Sure, you backup the keystore to a safe location and you are confident that in a desaster scenario you will be able to recover and open the keystore from backup. Maybe you are just a bit overconfident… or I’m paranoid. Anyway, here’s the safety net to TDE if you can’t open the keystore or any of its backups.

Whenever you create (or rekey) a Master Encryption Key you can extract all the information from the software keystore. Make sure you have a safe terminal session and nobody is watching over your shoulder (lock yourself in a room with your security officer). Write the key information on a piece of paper, envelope it and securely put it in a safe.

As you probably know from secure external password stores Oracle stores the information in a keystore as key-value pairs. This is also true for all the key information. Unitl 12c we used the “orapki” utility for keystore management. Starting with 12c you should not use that utility anymore as all operations should be performed using “ADMINISTER KEY MANAGEMENT” SQL commands.
“orapki” would not be useful in this context anyway as it won’t allow you do display the value for a given key. But, there’s still the good old “mkstore” utility which is mainly used for managing secure external password stores. This little tool allows you to display the value for a given key.
So, we (ab)use “mkstore” to extract all the Master Encryption Key information:

# read the keystore password from the command prompt
read -s -p "Keystore password: " keypass

# 1) get all TDE related key entries from the keystore
# 2) print the key-value pairs for each key entry
for secret in $(echo ${keypass} \
  | mkstore -wrl ${ORACLE_BASE}/admin/${ORACLE_SID}/wallet/tde -list \
  | grep '^ORACLE.SECURITY');
do
  echo ${keypass} \
     | mkstore -wrl ${ORACLE_BASE}/admin/${ORACLE_SID}/wallet/tde -viewEntry ${secret} \
     | grep '^ORACLE.SECURITY'
done

Note: even though Oracle probably didn’t think of this as a use case for “mkstore” I don’t see anything why this operation should not be supported.

Here’s a sample output of above script anotated with some comments:

# this is the actual Master Encryption Key
ORACLE.SECURITY.DB.ENCRYPTION.AfUlOfRdpE/VvwpTbMGlxlIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA = AEMAASAAPoVGwpf57OTqOnRzOLr5ElAOOO7JDfZm7Gf1GtEIBwQDEADaxjuiRvu9qTEz0HBp6vp7BQcAeHQBHw0iPA==

# This matches column KEY_ID in V$ENCRYPTION_KEYS
# It holds the ID of the currently active Master Encryption KEY
ORACLE.SECURITY.DB.ENCRYPTION.MASTERKEY = AfUlOfRdpE/VvwpTbMGlxlIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

# I'm not quite sure, I think this matches column WALLET_TYPE in V$ENCRYPTION_WALLET
ORACLE.SECURITY.ID.ENCRYPTION. = PASSWORD

# This matches column FULLY_BACKED_UP in V$ENCRYPTION_WALLET
# "KB" probably stands for "Key Backup"
ORACLE.SECURITY.KB.ENCRYPTION. = ORACLE.KEY.STORE.NOT.BACKED.UP

# This is the rest of the information in V$ENCRYPTION_KEYS encoded in some format
# "KM" probably stands for "Key Metadata" (or something like that)
ORACLE.SECURITY.KM.ENCRYPTION.AfUlOfRdpE/VvwpTbMGlxlIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA = 019C0001000D7874011F0C223C2CE54D28153C000D7874011F0C232837D0ECD8153C0000000000000003001E53595300000000000000000000000000000000000000000000000000000000330090D128C43600000004001E44455631000000000000000000000000000000000000000000000000000000000001FFFFFFFF00000004001E444556310000000000000000000000000000000000000000000000000000000100000008001E43444224524F4F5400000000000000000000000000000000000000000000000000010010286F66C41A66075DE0531F38A8C0351F0000000000000003001E5359530000000000000000000000000000000000000000000000000000000090D128C43600000004001E44455631000000000000000000000000000000000000000000000000000000000001FFFFFFFF00000004001E444556310000000000000000000000000000000000000000000000000000000100000008001E43444224524F4F5400000000000000000000000000000000000000000000000000010010286F66C41A66075DE0531F38A8C0351F00000001

# This matches column TAG in V$ENCRYPTION_KEYS (the tag specified in the ADMINISTER KEY MANAGEMENT command)
# "KT" probably stands for "Key Tag"
ORACLE.SECURITY.KT.ENCRYPTION.AfUlOfRdpE/VvwpTbMGlxlIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA = DEV1_MK_ACT

 
Recover (re-create) the keystore
Now, in case we lose the keystore and all its backups we can re-create the keystore with all its necessary content. Just get the notes from the safe.

Create a new, empty keystore

administer key management
  create keystore '<ORACLE_BASE>/admin/<ORACLE_SID>/wallet/tde'
  identified by "<KEYSTORE-PASSWORD>"
;

Insert the key information using “mkstore”

# Example with actual values from above sample output
read -s -p "Keystore password: " keypass
echo ${keypass} | mkstore -wrl ${ORACLE_BASE}/admin/${ORACLE_SID}/wallet/tde \
  -createEntry ORACLE.SECURITY.DB.ENCRYPTION.AfUlOfRdpE/VvwpTbMGlxlIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AEMAASAAPoVGwpf57OTqOnRzOLr5ElAOOO7JDfZm7Gf1GtEIBwQDEADaxjuiRvu9qTEz0HBp6vp7BQcAeHQBHw0iPA==

echo ${keypass} | mkstore -wrl ${ORACLE_BASE}/admin/${ORACLE_SID}/wallet/tde \
  -createEntry ORACLE.SECURITY.DB.ENCRYPTION.MASTERKEY AfUlOfRdpE/VvwpTbMGlxlIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

echo ${keypass} | mkstore -wrl ${ORACLE_BASE}/admin/${ORACLE_SID}/wallet/tde \
  -createEntry ORACLE.SECURITY.ID.ENCRYPTION. PASSWORD

echo ${keypass} | mkstore -wrl ${ORACLE_BASE}/admin/${ORACLE_SID}/wallet/tde \
  -createEntry ORACLE.SECURITY.KB.ENCRYPTION. ORACLE.KEY.STORE.NOT.BACKED.UP

echo ${keypass} | mkstore -wrl ${ORACLE_BASE}/admin/${ORACLE_SID}/wallet/tde \
  -createEntry ORACLE.SECURITY.KM.ENCRYPTION.AfUlOfRdpE/VvwpTbMGlxlIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 019C0001000D7874011F0C223C2CE54D28153C000D7874011F0C232837D0ECD8153C0000000000000003001E53595300000000000000000000000000000000000000000000000000000000330090D128C43600000004001E44455631000000000000000000000000000000000000000000000000000000000001FFFFFFFF00000004001E444556310000000000000000000000000000000000000000000000000000000100000008001E43444224524F4F5400000000000000000000000000000000000000000000000000010010286F66C41A66075DE0531F38A8C0351F0000000000000003001E5359530000000000000000000000000000000000000000000000000000000090D128C43600000004001E44455631000000000000000000000000000000000000000000000000000000000001FFFFFFFF00000004001E444556310000000000000000000000000000000000000000000000000000000100000008001E43444224524F4F5400000000000000000000000000000000000000000000000000010010286F66C41A66075DE0531F38A8C0351F00000001

echo ${keypass} | mkstore -wrl ${ORACLE_BASE}/admin/${ORACLE_SID}/wallet/tde \
  -createEntry ORACLE.SECURITY.KT.ENCRYPTION.AfUlOfRdpE/VvwpTbMGlxlIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA DEV1_MK_ACT

Open the keystore in the database and you can access your TDE encrypted data again.

administer key management
  set keystore open
  identified by "<KEYSTORE-PASSWORD>";

 
Some thoughts
From a security perspective I think the Master Encryption Keys should not be extractable by printing it in the terminal session. This is not secure. On the other hand you probably should be using a HSM anyway.
TDE can be scary as you might possibly loose all the data in the encrypted tablespaces. In the odd case you use software key stores the Master Encryption Key extract can be a live saver. Just get your security officer involved to discuss how to handle the plain-text key information in a secure manner (and maybe now you’ll get a HSM).