Skip to main content

Oracle Transparent Data Encryption (TDE)

Oracle Transparent Data Encryption overview

Introduction

Transparent Data Encryption (TDE) is Oracle's native encryption feature that protects sensitive data by encrypting it at rest. It automatically encrypts data before writing it to disk and transparently decrypts it for authorized users. Since encryption and decryption happen inside the Oracle Database engine, applications need no code changes, which makes TDE a seamless and efficient security solution.

Oracle TDE supports two encryption methods:

  1. Tablespace Encryption: encrypts all data stored within a tablespace, offering broader and easier protection.
  2. Column Encryption: encrypts only selected sensitive columns.

Prerequisites

  • Oracle Database Enterprise Edition (TDE is not available in Standard Edition).
  • Downtime for a database bounce to set WALLET_ROOT and open the keystore.
  • A secure OS-level directory to hold the wallet, with restricted permissions.
  • The wallet password recorded somewhere safe, since it's required for every future keystore open or encrypted backup restore.

Environment Used in This Guide

OS OL 9.1
Database Version 23.26.1.0.0
Database SID CDBORCL

Summary of Activities

This article walks through the following, in order:

Seeing the Impact of Not Using TDE

With TDE not yet enabled, data isn't encrypted, and anyone with OS-level access to the physical datafile or a backup file can read it directly, without ever touching the database. Let's demonstrate that before fixing it.

Step 1: Create a Tablespace and a Table


Create a Tablespace:
SQL> show pdbs CON_ID CON_NAME OPEN MODE RESTRICTED ---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDBORCL READ WRITE NO SQL> alter session set container=PDBORCL; Session altered. SQL> set lines 333 pages 333 SQL> select name from v$datafile; NAME ------------------------------------------------------------------------------------------------------------ /u01/app/oracle/oradata/CDBORCL/501F9BE43D9F38CEE0650A0027BEE017/datafile/o1_mf_system_nymz5qxb_.dbf /u01/app/oracle/oradata/CDBORCL/501F9BE43D9F38CEE0650A0027BEE017/datafile/o1_mf_sysaux_nymz5r3g_.dbf /u01/app/oracle/oradata/CDBORCL/501F9BE43D9F38CEE0650A0027BEE017/datafile/o1_mf_undotbs1_nymz5r3j_.dbf /u01/app/oracle/oradata/CDBORCL/501F9BE43D9F38CEE0650A0027BEE017/datafile/o1_mf_users_nymzk6kx_.dbf SQL> create tablespace test1 datafile '/u01/app/oracle/oradata/CDBORCL/501F9BE43D9F38CEE0650A0027BEE017/datafile/test1.dbf' size 1g autoextend on; Tablespace created. Create a Table and Insert Data:
SQL> CREATE TABLE clear_text_tab (info VARCHAR2(50)) TABLESPACE test1; Table created. SQL> INSERT INTO clear_text_tab VALUES ('Test_data'); 1 row created. SQL> commit; Commit complete. Force to write data in the datafile:
SQL> ALTER SYSTEM FLUSH BUFFER_CACHE; System altered. SQL> ALTER SYSTEM CHECKPOINT; System altered. SQL> exit

Step 2: Try to Read Data from the Datafile

The Linux strings command extracts printable text from a binary file. Running it directly against the datafile shows exactly what's stored inside, because nothing is encrypting it yet.


[oracle@orcl ~]$ strings /u01/app/oracle/oradata/CDBORCL/501F9BE43D9F38CEE0650A0027BEE017/datafile/test1.dbf|grep "Test_data"
Test_data
[oracle@orcl ~]$

As shown above, the table data is fully readable using the strings command on the datafile.

Step 3: Take an RMAN Backup of the Tablespace


[oracle@orcl cdborcl]$ rman target /

Recovery Manager: Release 23.26.1.0.0 - Production on Wed Jun 24 14:14:36 2026
Version 23.26.1.0.0

Copyright (c) 1982, 2026, Oracle and/or its affiliates.  All rights reserved.

connected to target database: CDBORCL (DBID=3216361938)

RMAN> BACKUP TABLESPACE PDBORCL:test1 FORMAT '/tmp/rman_unencrypted_file.bkp';
BACKUP TABLESPACE PDBORCL:test1 FORMAT '/tmp/rman_unencrypted_file.bkp';
Starting backup at 24-JUN-26
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=40 device type=DISK
channel ORA_DISK_1: starting full datafile backup set
channel ORA_DISK_1: specifying datafile(s) in backup set
input datafile file number=00013 name=/u01/app/oracle/oradata/CDBORCL/501F9BE43D9F38CEE0650A0027BEE017/datafile/test1.dbf
channel ORA_DISK_1: starting piece 1 at 24-JUN-26
channel ORA_DISK_1: finished piece 1 at 24-JUN-26
piece handle=/tmp/rman_unencrypted_file.bkp tag=TAG20260624T141450 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:01
Finished backup at 24-JUN-26

Starting Control File and SPFILE Autobackup at 24-JUN-26
piece handle=/u01/app/oracle/fast_recovery_area/CDBORCL/autobackup/2026_06_24/o1_mf_s_1236780892_o3q685fv_.bkp comment=NONE
Finished Control File and SPFILE Autobackup at 24-JUN-26

RMAN> exit
exit

Step 4: Try to Read Data from the Backup File


[oracle@orcl cdborcl]$ strings /tmp/rman_unencrypted_file.bkp|grep "Test_data"
Test_data
[oracle@orcl ~]$

The table data is readable in the backup file too. This means anyone with OS-level access to a datafile or backup, without any database privileges at all, can pull sensitive data straight off disk. TDE exists specifically to close this gap.

Configure the TDE Wallet

The TDE wallet is a secure keystore that holds the encryption keys Oracle uses to encrypt and decrypt data transparently. Before any tablespace or column can be encrypted, the wallet needs to be created, opened, and populated with a master encryption key.

Step 1: Check Current Parameters and Create a Pfile


Check the Current TDE Parameters:
SQL> show pdbs CON_ID CON_NAME OPEN MODE RESTRICTED ---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDBORCL READ WRITE NO SQL> SQL> show parameter TDE_CONFIGURATION NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ tde_configuration string SQL> SQL> show parameter WALLET_ROOT NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ wallet_root string SQL> SQL> show parameter uniq NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ db_unique_name string cdborcl SQL> create pfile='/home/oracle/initcdborcl.ora' from spfile; File created.

Step 2: Create the Wallet Directory


[oracle@orcl ~]$ echo $ORACLE_BASE
/u01/app2/oracle

[oracle@orcl ~]$ mkdir -p /u01/app2/oracle/admin/cdborcl/wallet
[oracle@orcl ~]$ chmod 700 /u01/app2/oracle/admin/cdborcl/wallet
[oracle@orcl ~]$ cd /u01/app2/oracle/admin/cdborcl
[oracle@orcl cdborcl]$ ls -lrth
total 0
drwxr-x---. 2 oracle oinstall 44 Jun 24 13:17 xdb_wallet
drwx------. 2 oracle oinstall  6 Jun 24 13:20 wallet
[oracle@orcl cdborcl]$

Step 3: Set WALLET_ROOT and Bounce the Database

WALLET_ROOT points Oracle at the directory where the keystore lives, and TDE_CONFIGURATION tells it what kind of keystore to use. Both need a database restart to take effect.


SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDBORCL                        READ WRITE NO
         
SQL> ALTER SYSTEM SET WALLET_ROOT='/u01/app/oracle/admin/cdborcl/wallet' SCOPE=SPFILE;

System altered.

SQL> shut immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.

Total System Global Area 2027132112 bytes
Fixed Size                  5010640 bytes
Variable Size             587202560 bytes
Database Buffers         1426063360 bytes
Redo Buffers                8855552 bytes
Database mounted.
Database opened.
SQL>
SQL> show parameter WALLET_ROOT

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------------
wallet_root                          string      /u01/app/oracle/admin/cdborcl/wallet
                                                 
SQL> ALTER SYSTEM SET TDE_CONFIGURATION="KEYSTORE_CONFIGURATION=FILE" SCOPE=BOTH;

System altered.

SQL> show parameter TDE_CONFIGURATION

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
tde_configuration                    string      KEYSTORE_CONFIGURATION=FILE
SQL>

SQL> col WRL_PARAMETER for a45
SQL> SELECT con_id, wrl_type, wrl_parameter, wallet_type, status FROM v$encryption_wallet;

    CON_ID WRL_TYPE             WRL_PARAMETER                                 WALLET_TYPE          STATUS
---------- -------------------- --------------------------------------------- -------------------- ------------------------------
         1 FILE                 /u01/app/oracle/admin/cdborcl/wallet/tde/     UNKNOWN              NOT_AVAILABLE
         2 FILE                                                               UNKNOWN              NOT_AVAILABLE
         3 FILE                                                               UNKNOWN              NOT_AVAILABLE

SQL>

At this point WALLET_TYPE shows UNKNOWN and status is NOT_AVAILABLE, because the wallet exists on disk but hasn't been given a password yet or had a key created. That's next.

Step 4: Set a Password and Open the Wallet

Creating the keystore sets its password, opening it makes the keys usable for the current session, and the master encryption key is what actually gets used to encrypt data. Both the CDB and the PDB need their own key. Keep the wallet password safe, since it's needed for every future open or backup restore involving encrypted data.


Create the Software Keystore (TDE Wallet):
SQL> ADMINISTER KEY MANAGEMENT CREATE KEYSTORE IDENTIFIED BY Wallat26ai#123; keystore altered. SQL> SELECT con_id, wrl_type, wrl_parameter, wallet_type, status FROM v$encryption_wallet; CON_ID WRL_TYPE WRL_PARAMETER WALLET_TYPE STATUS ---------- -------------------- --------------------------------------------- -------------------- ------------------------------ 1 FILE /u01/app/oracle/admin/cdborcl/wallet/tde/ UNKNOWN CLOSED 2 FILE UNKNOWN CLOSED 3 FILE UNKNOWN CLOSED SQL> Open the Keystore for All Containers:
SQL> ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY Wallat26ai#123 CONTAINER=ALL; keystore altered. SQL> SELECT con_id, wrl_type, wrl_parameter, wallet_type, status FROM v$encryption_wallet; CON_ID WRL_TYPE WRL_PARAMETER WALLET_TYPE STATUS ---------- -------------------- --------------------------------------------- -------------------- ------------------------------ 1 FILE /u01/app/oracle/admin/cdborcl/wallet/tde/ PASSWORD OPEN_NO_MASTER_KEY 2 FILE PASSWORD OPEN_NO_MASTER_KEY 3 FILE PASSWORD OPEN_NO_MASTER_KEY Create the Master Encryption Key for the CDB:
SQL> ADMINISTER KEY MANAGEMENT SET KEY USING TAG 'CDB_ROOT_KEY' FORCE KEYSTORE IDENTIFIED BY Wallat26ai#123 WITH BACKUP; keystore altered. SQL> SELECT con_id, wrl_type, wrl_parameter, wallet_type, status FROM v$encryption_wallet; CON_ID WRL_TYPE WRL_PARAMETER WALLET_TYPE STATUS ---------- -------------------- --------------------------------------------- -------------------- ------------------------------ 1 FILE /u01/app/oracle/admin/cdborcl/wallet/tde/ PASSWORD OPEN 2 FILE PASSWORD OPEN 3 FILE PASSWORD OPEN_NO_MASTER_KEY Create the Master Encryption Key for the PDB:
SQL> ALTER SESSION SET CONTAINER=PDBORCL; Session altered. SQL> ADMINISTER KEY MANAGEMENT SET KEY USING TAG 'PDB_ORCL_KEY' FORCE KEYSTORE IDENTIFIED BY Wallat26ai#123 WITH BACKUP; keystore altered. SQL> SELECT con_id, wrl_type, wrl_parameter, wallet_type, status FROM v$encryption_wallet; CON_ID WRL_TYPE WRL_PARAMETER WALLET_TYPE STATUS ---------- -------------------- --------------------------------------------- -------------------- ------------------------------ 3 FILE PASSWORD OPEN Verify Wallet Status Across All Containers:
SQL> conn /as sysdba Connected. SQL> SELECT con_id, wrl_type, wrl_parameter, wallet_type, status FROM v$encryption_wallet; CON_ID WRL_TYPE WRL_PARAMETER WALLET_TYPE STATUS ---------- -------------------- --------------------------------------------- -------------------- ------------------------------ 1 FILE /u01/app/oracle/admin/cdborcl/wallet/tde/ PASSWORD OPEN 2 FILE PASSWORD OPEN 3 FILE PASSWORD OPEN SQL>

The TDE wallet configuration is now complete, with every container showing status OPEN. Next, let's use it to create an encrypted tablespace.

Tablespace Encryption

Tablespace encryption protects everything stored in a tablespace automatically, without touching individual columns or application code. This is usually the simplest and broadest option for protecting data at rest.

Encrypts everything stored in the tablespace automatically, so every table and index created there is protected without extra effort. Since it's applied once at the datafile level, using AES256 here adds negligible overhead while giving the strongest protection.

Step 1: Create an Encrypted Tablespace and Table


Create the Encrypted Tablespace:
SQL> show pdbs CON_ID CON_NAME OPEN MODE RESTRICTED ---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDBORCL READ WRITE NO SQL> alter session set container=PDBORCL; Session altered. SQL> CREATE TABLESPACE test_tde DATAFILE '/u01/app/oracle/oradata/CDBORCL/501F9BE43D9F38CEE0650A0027BEE017/datafile/test_tde1.dbf' SIZE 500M ENCRYPTION USING 'AES256' DEFAULT STORAGE (ENCRYPT); Tablespace created. Create a Table and Insert Data:
SQL> CREATE TABLE test_tde_table (info VARCHAR2(30)) TABLESPACE test_tde; Table created. SQL> INSERT INTO test_tde_table VALUES ('TestData'); 1 row created. SQL> commit; Commit complete. Force to write data in the datafile:
SQL> ALTER SYSTEM FLUSH BUFFER_CACHE; System altered. SQL> ALTER SYSTEM CHECKPOINT; System altered. SQL> exit

Step 2: Try to Read Data from the Datafile


[oracle@orcl cdborcl]$ strings /u01/app/oracle/oradata/CDBORCL/501F9BE43D9F38CEE0650A0027BEE017/datafile/test_tde1.dbf| grep "TestData"

This time, nothing comes back. The strings command finds no readable trace of "TestData" in the datafile, because tablespace encryption is now protecting it at rest.

Step 3: Take an RMAN Backup of the Encrypted Tablespace


RMAN> BACKUP TABLESPACE PDBORCL:test_tde FORMAT '/tmp/rman_encrypted_file.bkp';
BACKUP TABLESPACE PDBORCL:test_tde FORMAT '/tmp/rman_encrypted_file.bkp';
Starting backup at 24-JUN-26
using channel ORA_DISK_1
channel ORA_DISK_1: starting full datafile backup set
channel ORA_DISK_1: specifying datafile(s) in backup set
input datafile file number=00014 name=/u01/app/oracle/oradata/CDBORCL/501F9BE43D9F38CEE0650A0027BEE017/datafile/test_tde1.dbf
channel ORA_DISK_1: starting piece 1 at 24-JUN-26
channel ORA_DISK_1: finished piece 1 at 24-JUN-26
piece handle=/tmp/rman_encrypted_file.bkp tag=TAG20260624T141459 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:01
Finished backup at 24-JUN-26

Starting Control File and SPFILE Autobackup at 24-JUN-26
piece handle=/u01/app/oracle/fast_recovery_area/CDBORCL/autobackup/2026_06_24/o1_mf_s_1236780901_o3q68fgq_.bkp comment=NONE
Finished Control File and SPFILE Autobackup at 24-JUN-26

RMAN> exit
exit

Step 4: Try to Read Data from the Backup File


[oracle@orcl cdborcl]$ strings /tmp/rman_encrypted_file.bkp|grep "TestData"
[oracle@orcl cdborcl]$

Same result as the datafile: the backup file gives up nothing to the strings command either, since a tablespace backed by an encrypted tablespace is encrypted on disk too.

Auto Login

By default, the wallet stays closed after a database restart until someone manually opens it with the password. Auto login lets the database open the wallet on its own at startup, which is what most production systems need so an unattended restart doesn't leave the database serving ORA-28365 errors until a DBA logs in.

Step 1: Bounce the Database Without Auto Login


Shutdown the Database:
SQL> show pdbs CON_ID CON_NAME OPEN MODE RESTRICTED ---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDBORCL READ WRITE NO SQL> shut immediate Database closed. Database dismounted. ORACLE instance shut down. SQL> Startup the Database:
SQL> startup ORACLE instance started. Total System Global Area 2027132112 bytes Fixed Size 5010640 bytes Variable Size 587202560 bytes Database Buffers 1426063360 bytes Redo Buffers 8855552 bytes Database mounted. Database opened. SQL> show pdbs CON_ID CON_NAME OPEN MODE RESTRICTED ---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDBORCL READ WRITE NO Check the Wallet Status and Try to Fetch the Table Data:
SQL> set lines 333 pages 333 SQL> col WRL_PARAMETER for a45 SQL> SELECT con_id, wrl_type, wrl_parameter, wallet_type, status FROM v$encryption_wallet CON_ID WRL_TYPE WRL_PARAMETER WALLET_TYPE STATUS ---------- -------------------- --------------------------------------------- -------------------- ------------------------------ 1 FILE /u01/app/oracle/admin/cdborcl/wallet/tde/ UNKNOWN CLOSED 2 FILE UNKNOWN CLOSED 3 FILE UNKNOWN CLOSED SQL> alter session set container=PDBORCL; Session altered. SQL> select count(*) from test_tde_table; select count(*) from test_tde_table * ERROR at line 1: ORA-28365: Wallet is not open. Help: https://docs.oracle.com/error-help/db/ora-28365/

The ORA-28365 error confirms the wallet closed on restart, as expected without auto login, and encrypted data is inaccessible until it's reopened manually.

Step 2: Open the Wallet Manually


SQL> conn /as sysdba
Connected.
SQL> ALTER SESSION SET CONTAINER=CDB$ROOT;

SQL> ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY "Wallat26ai#123" CONTAINER=ALL;

Session altered.

SQL> SELECT con_id, wrl_type, wrl_parameter, wallet_type, status FROM v$encryption_wallet;

    CON_ID WRL_TYPE             WRL_PARAMETER                                 WALLET_TYPE          STATUS
---------- -------------------- --------------------------------------------- -------------------- ------------------------------
         1 FILE                 /u01/app/oracle/admin/cdborcl/wallet/tde/     PASSWORD             OPEN
         2 FILE                                                               PASSWORD             OPEN
         3 FILE                                                               PASSWORD             OPEN

SQL>
SQL> alter session set container=PDBORCL;

Session altered.

SQL> select count(*) from test_tde_table;

  COUNT(*)
----------
         1
         

With the wallet open, the query succeeds. Doing this by hand every restart doesn't scale, which is exactly what auto login solves.

Step 3: Configure Auto Login


SQL> conn /as sysdba
Connected.
SQL> ALTER SESSION SET CONTAINER=CDB$ROOT;

SQL> SELECT con_id, wrl_type, wrl_parameter, wallet_type, status FROM v$encryption_wallet;

    CON_ID WRL_TYPE             WRL_PARAMETER                                 WALLET_TYPE          STATUS
---------- -------------------- --------------------------------------------- -------------------- ------------------------------
         1 FILE                 /u01/app/oracle/admin/cdborcl/wallet/tde/     PASSWORD             OPEN
         2 FILE                                                               PASSWORD             OPEN
         3 FILE                                                               PASSWORD             OPEN

SQL> ADMINISTER KEY MANAGEMENT CREATE AUTO_LOGIN KEYSTORE FROM KEYSTORE IDENTIFIED BY "Wallat26ai#123";

keystore altered.

SQL> SELECT con_id, wrl_type, wrl_parameter, wallet_type, status FROM v$encryption_wallet;

    CON_ID WRL_TYPE             WRL_PARAMETER                                 WALLET_TYPE          STATUS
---------- -------------------- --------------------------------------------- -------------------- ------------------------------
         1 FILE                 /u01/app/oracle/admin/cdborcl/wallet/tde/     AUTOLOGIN            OPEN
         2 FILE                                                               AUTOLOGIN            OPEN
         3 FILE                                                               AUTOLOGIN            OPEN
         

Step 4: Bounce the Database Again

With an auto-login keystore in place, the wallet should now open on its own without any manual intervention.


SQL> shut immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL>
SQL> startup
ORA-32004: obsolete or deprecated parameter(s) specified for RDBMS instance
ORACLE instance started.

Total System Global Area 2027132112 bytes
Fixed Size                  5010640 bytes
Variable Size             587202560 bytes
Database Buffers         1426063360 bytes
Redo Buffers                8855552 bytes
Database mounted.
Database opened.

SQL> SELECT con_id, wrl_type, wrl_parameter, wallet_type, status FROM v$encryption_wallet;

    CON_ID WRL_TYPE             WRL_PARAMETER                                 WALLET_TYPE          STATUS
---------- -------------------- --------------------------------------------- -------------------- ------------------------------
         1 FILE                 /u01/app/oracle/admin/cdborcl/wallet/tde/     AUTOLOGIN            OPEN
         2 FILE                                                               AUTOLOGIN            OPEN
         3 FILE                                                               AUTOLOGIN            OPEN
        
SQL> alter session set container=PDBORCL;

Session altered.

SQL> select count(*) from test_tde_table;

  COUNT(*)
----------
         1
         

The wallet opened automatically this time, and the encrypted table's data is immediately queryable with no manual step required.

Column Encryption

Encrypts only the specific columns you choose, useful when just a few sensitive fields need protection, like a salary or national ID field, rather than an entire tablespace. Because it adds overhead on every read and write of that column, AES192 is often used as a practical middle ground between security and performance.

Step 1: Create a Table with an Encrypted Column


SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDBORCL                        READ WRITE NO
SQL> alter session set container=PDBORCL;

Session altered.

SQL> CREATE TABLE system.emp_secure (
    emp_id NUMBER,
    emp_name VARCHAR2(50),
    salary NUMBER ENCRYPT USING 'AES192'
) TABLESPACE test1; 

Table created.

SQL> INSERT INTO system.emp_secure VALUES (101, 'Rahul', 95000);

1 row created.

SQL> commit;

Commit complete.

Force to write data in the datafile:
SQL> ALTER SYSTEM FLUSH BUFFER_CACHE; System altered. SQL> ALTER SYSTEM CHECKPOINT; System altered.

Step 2: Try to Read Data from the Datafile


[oracle@orcl ~]$ strings /u01/app/oracle/oradata/CDBORCL/501F9BE43D9F38CEE0650A0027BEE017/datafile/test1.dbf | grep -E "Rahul|95000"
Rahul4
[oracle@orcl ~]$ strings /u01/app/oracle/oradata/CDBORCL/501F9BE43D9F38CEE0650A0027BEE017/datafile/test1.dbf | grep  "Rahul"
Rahul4
[oracle@orcl ~]$
[oracle@orcl ~]$ strings /u01/app/oracle/oradata/CDBORCL/501F9BE43D9F38CEE0650A0027BEE017/datafile/test1.dbf | grep  "95000"
[oracle@orcl ~]$

The unencrypted name column still shows up in plain text, since only the salary column was encrypted, but the salary value itself never appears.

The trailing character (like the "4" after Rahul) shows up because the Linux strings command blindly extracts any run of printable characters from the raw data block. It captures the unencrypted text column, then glues on adjacent binary metadata or length bytes from the encrypted column next to it, which happen to map to a printable ASCII character.

RMAN Backup Encryption

Encrypting tablespaces and columns protects data inside the database, but an RMAN backup can still be restored onto an unauthorized server unless the backup itself is also encrypted. Let's check the default behavior first, then lock it down.

Step 1: Check the Current RMAN Configuration and Take a Backup


RMAN> show all;
show all;
using target database control file instead of recovery catalog
RMAN configuration parameters for database with db_unique_name CDBORCL are:
CONFIGURE RETENTION POLICY TO REDUNDANCY 1; # default
CONFIGURE BACKUP OPTIMIZATION OFF; # default
CONFIGURE DEFAULT DEVICE TYPE TO DISK; # default
CONFIGURE CONTROLFILE AUTOBACKUP ON; # default
CONFIGURE CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO '%F'; # default
CONFIGURE DEVICE TYPE DISK PARALLELISM 1 BACKUP TYPE TO BACKUPSET; # default
CONFIGURE DATAFILE BACKUP COPIES FOR DEVICE TYPE DISK TO 1; # default
CONFIGURE ARCHIVELOG BACKUP COPIES FOR DEVICE TYPE DISK TO 1; # default
CONFIGURE MAXSETSIZE TO UNLIMITED; # default
CONFIGURE ENCRYPTION FOR DATABASE OFF; # default
CONFIGURE ENCRYPTION ALGORITHM 'AES256'; # default
CONFIGURE COMPRESSION ALGORITHM 'BASIC' AS OF RELEASE 'DEFAULT' OPTIMIZE FOR LOAD TRUE ; # default
CONFIGURE RMAN OUTPUT TO KEEP FOR 7 DAYS; # default
CONFIGURE ARCHIVELOG DELETION POLICY TO NONE; # default
CONFIGURE SNAPSHOT CONTROLFILE NAME TO '/u01/app2/oracle/product/26ai/dbhome_1/dbs/snapcf_cdborcl.f'; # default

RMAN>
RMAN> backup current controlfile format '/tmp/control_backup.bkp';
backup current controlfile format '/tmp/control_backup.bkp';
Starting backup at 24-JUN-26
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=49 device type=DISK
channel ORA_DISK_1: starting full datafile backup set
channel ORA_DISK_1: specifying datafile(s) in backup set
including current control file in backup set
channel ORA_DISK_1: starting piece 1 at 24-JUN-26
channel ORA_DISK_1: finished piece 1 at 24-JUN-26
piece handle=/tmp/control_backup.bkp tag=TAG20260624T142243 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:01
Finished backup at 24-JUN-26

Starting Control File and SPFILE Autobackup at 24-JUN-26
piece handle=/u01/app/oracle/fast_recovery_area/CDBORCL/autobackup/2026_06_24/o1_mf_s_1236781365_o3q6pyhc_.bkp comment=NONE
Finished Control File and SPFILE Autobackup at 24-JUN-26

RMAN> exit
exit

Notice that "ENCRYPTION FOR DATABASE" is OFF by default. Let's try restoring this controlfile backup on a separate test instance and see what happens.

Step 2: Restore the Backup to a Test Instance


[oracle@orcl ~]$ cat /home/oracle/testdbpfile.ora
db_name=CDBORCL
db_unique_name=testdb
sga_target=1G
pga_aggregate_target=500M
compatible=23.6.0.0
enable_pluggable_database=true
control_files='/tmp/control01.ctl'

[oracle@orcl ~]$ . oraenv
ORACLE_SID = [cdborcl] ? testdb
ORACLE_HOME = [/home/oracle] ? /u01/app2/oracle/product/26ai/dbhome_1
The Oracle base remains unchanged with value /u01/app2/oracle
[oracle@orcl ~]$ sqlplus / as sysdba

SQL*Plus: Release 23.26.1.0.0 - Production on Wed Jun 24 15:07:08 2026
Version 23.26.1.0.0

Copyright (c) 1982, 2025, Oracle.  All rights reserved.

Connected to an idle instance.

SQL> startup nomount pfile='/home/oracle/testdbpfile.ora';
ORACLE instance started.

Total System Global Area 1070370360 bytes
Fixed Size                  5017144 bytes
Variable Size             276824064 bytes
Database Buffers          780140544 bytes
Redo Buffers                8388608 bytes
SQL> exit
Disconnected from Oracle AI Database 26ai Enterprise Edition Release 23.26.1.0.0 - Production
Version 23.26.1.0.0
[oracle@orcl ~]$ rman target /

Recovery Manager: Release 23.26.1.0.0 - Production on Wed Jun 24 15:07:46 2026
Version 23.26.1.0.0

Copyright (c) 1982, 2026, Oracle and/or its affiliates.  All rights reserved.

connected to target database: CDBORCL (not mounted)

RMAN> restore controlfile from '/tmp/control_backup.bkp';
restore controlfile from '/tmp/control_backup.bkp';
Starting restore at 24-JUN-26
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=41 device type=DISK

channel ORA_DISK_1: restoring control file
channel ORA_DISK_1: restore complete, elapsed time: 00:00:01
output file name=/tmp/control01.ctl
Finished restore at 24-JUN-26

RMAN> alter database mount;
alter database mount;
released channel: ORA_DISK_1
Statement processed

The backup restores cleanly on a completely different instance, with no wallet or password required. That's the security risk: even with TDE protecting the live database, an unencrypted backup can walk straight out the door and be restored anywhere. Fixing this means changing the RMAN configuration itself.

Step 3: Enable Encryption for Database in RMAN


RMAN> show all;
show all;
using target database control file instead of recovery catalog
RMAN configuration parameters for database with db_unique_name CDBORCL are:
CONFIGURE RETENTION POLICY TO REDUNDANCY 1; # default
CONFIGURE BACKUP OPTIMIZATION OFF; # default
CONFIGURE DEFAULT DEVICE TYPE TO DISK; # default
CONFIGURE CONTROLFILE AUTOBACKUP ON; # default
CONFIGURE CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO '%F'; # default
CONFIGURE DEVICE TYPE DISK PARALLELISM 1 BACKUP TYPE TO BACKUPSET; # default
CONFIGURE DATAFILE BACKUP COPIES FOR DEVICE TYPE DISK TO 1; # default
CONFIGURE ARCHIVELOG BACKUP COPIES FOR DEVICE TYPE DISK TO 1; # default
CONFIGURE MAXSETSIZE TO UNLIMITED; # default
CONFIGURE ENCRYPTION FOR DATABASE OFF; # default
CONFIGURE ENCRYPTION ALGORITHM 'AES256'; # default
CONFIGURE COMPRESSION ALGORITHM 'BASIC' AS OF RELEASE 'DEFAULT' OPTIMIZE FOR LOAD TRUE ; # default
CONFIGURE RMAN OUTPUT TO KEEP FOR 7 DAYS; # default
CONFIGURE ARCHIVELOG DELETION POLICY TO NONE; # default
CONFIGURE SNAPSHOT CONTROLFILE NAME TO '/u01/app2/oracle/product/26ai/dbhome_1/dbs/snapcf_cdborcl.f'; # default

RMAN>
RMAN> CONFIGURE ENCRYPTION FOR DATABASE ON;
CONFIGURE ENCRYPTION FOR DATABASE ON;
new RMAN configuration parameters:
CONFIGURE ENCRYPTION FOR DATABASE ON;
new RMAN configuration parameters are successfully stored

RMAN> show all;
show all;
RMAN configuration parameters for database with db_unique_name CDBORCL are:
CONFIGURE RETENTION POLICY TO REDUNDANCY 1; # default
CONFIGURE BACKUP OPTIMIZATION OFF; # default
CONFIGURE DEFAULT DEVICE TYPE TO DISK; # default
CONFIGURE CONTROLFILE AUTOBACKUP ON; # default
CONFIGURE CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO '%F'; # default
CONFIGURE DEVICE TYPE DISK PARALLELISM 1 BACKUP TYPE TO BACKUPSET; # default
CONFIGURE DATAFILE BACKUP COPIES FOR DEVICE TYPE DISK TO 1; # default
CONFIGURE ARCHIVELOG BACKUP COPIES FOR DEVICE TYPE DISK TO 1; # default
CONFIGURE MAXSETSIZE TO UNLIMITED; # default
CONFIGURE ENCRYPTION FOR DATABASE ON;
CONFIGURE ENCRYPTION ALGORITHM 'AES256'; # default
CONFIGURE COMPRESSION ALGORITHM 'BASIC' AS OF RELEASE 'DEFAULT' OPTIMIZE FOR LOAD TRUE ; # default
CONFIGURE RMAN OUTPUT TO KEEP FOR 7 DAYS; # default
CONFIGURE ARCHIVELOG DELETION POLICY TO NONE; # default
CONFIGURE SNAPSHOT CONTROLFILE NAME TO '/u01/app2/oracle/product/26ai/dbhome_1/dbs/snapcf_cdborcl.f'; # default

RMAN>

Step 4: Take an Encrypted Backup


RMAN> BACKUP CURRENT CONTROLFILE FORMAT '/u01/backup/control.bkp';
BACKUP CURRENT CONTROLFILE FORMAT '/u01/backup/control.bkp';
Starting backup at 24-JUN-26
using channel ORA_DISK_1
channel ORA_DISK_1: starting full datafile backup set
channel ORA_DISK_1: specifying datafile(s) in backup set
including current control file in backup set
channel ORA_DISK_1: starting piece 1 at 24-JUN-26
channel ORA_DISK_1: finished piece 1 at 24-JUN-26
piece handle=/u01/backup/control.bkp tag=TAG20260624T154701 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:01
Finished backup at 24-JUN-26

Starting Control File and SPFILE Autobackup at 24-JUN-26
piece handle=/u01/app/oracle/fast_recovery_area/CDBORCL/autobackup/2026_06_24/o1_mf_s_1236786424_o3qco18b_.bkp comment=NONE
Finished Control File and SPFILE Autobackup at 24-JUN-26

RMAN>

Step 5: Attempt to Restore Without the Wallet


[oracle@orcl ~]$ . oraenv
ORACLE_SID = [cdborcl] ? testdb
ORACLE_HOME = [/home/oracle] ? /u01/app2/oracle/product/26ai/dbhome_1
The Oracle base remains unchanged with value /u01/app2/oracle
[oracle@orcl ~]$
[oracle@orcl ~]$
[oracle@orcl ~]$ !sq
sqlplus / as sysdba

SQL*Plus: Release 23.26.1.0.0 - Production on Wed Jun 24 15:48:10 2026
Version 23.26.1.0.0

Copyright (c) 1982, 2025, Oracle.  All rights reserved.

Connected to an idle instance.

SQL> def
DEFINE _DATE           = "24-JUN-26" (CHAR)
DEFINE _CONNECT_IDENTIFIER = "testdb" (CHAR)
DEFINE _USER           = "SYS" (CHAR)
DEFINE _PRIVILEGE      = "AS SYSDBA" (CHAR)
DEFINE _SQLPLUS_RELEASE = "2326010000" (CHAR)
DEFINE _EDITOR         = "vi" (CHAR)
DEFINE _O_VERSION      = "" (CHAR)
DEFINE _O_RELEASE      = "" (CHAR)
SQL>
SQL> startup nomount pfile='/home/oracle/testdbpfile.ora';
ORACLE instance started.

Total System Global Area 1070370360 bytes
Fixed Size                  5017144 bytes
Variable Size             276824064 bytes
Database Buffers          780140544 bytes
Redo Buffers                8388608 bytes

[oracle@orcl ~]$ rman target /

Recovery Manager: Release 23.26.1.0.0 - Production on Wed Jun 24 15:49:34 2026
Version 23.26.1.0.0

Copyright (c) 1982, 2026, Oracle and/or its affiliates.  All rights reserved.

connected to target database: CDBORCL (not mounted)

RMAN> restore controlfile from '/u01/backup/control.bkp';
restore controlfile from '/u01/backup/control.bkp';
Starting restore at 24-JUN-26
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=200 device type=DISK

channel ORA_DISK_1: restoring control file
RMAN Command Id : 2026-06-24T15:49:34
RMAN Command Id : 2026-06-24T15:49:34
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of restore command at 06/24/2026 15:50:08
ORA-19870: error while restoring backup piece /u01/backup/control.bkp
ORA-19913: unable to decrypt backup
ORA-28365: Wallet is not open.
RMAN Client Diagnostic Trace file : /u01/app2/oracle/diag/clients/user_oracle/RMAN_4119929589_116/trace/ora_rman_18257_0.trc
RMAN Server Diagnostic Trace file : /u01/app2/oracle/diag/rdbms/testdb/testdb/trace/testdb_ora_18277.trc
RMAN Server Diagnostic Trace file : /u01/app2/oracle/diag/rdbms/testdb/testdb/trace/testdb_ora_18259.trc

RMAN>

This time the restore fails outright with ORA-19913 and ORA-28365, because the backup is encrypted and this test instance has no wallet to decrypt it. That's exactly the protection RMAN encryption is meant to provide. To restore it legitimately, the wallet needs to travel with the backup.

Step 6: Copy the Wallet to the Test Instance


[oracle@orcl ~]$ mkdir -p /u01/testdb/
[oracle@orcl ~]$ cd /u01/app/oracle/admin/cdborcl
[oracle@orcl cdborcl]$ ls
adump  dpdump  pfile  wallet  xdb_wallet
[oracle@orcl cdborcl]$
[oracle@orcl cdborcl]$ cp -r wallet /u01/testdb
[oracle@orcl cdborcl]$ cd /u01/testdb
[oracle@orcl testdb]$ ls
wallet

With the wallet copied over, point the test instance's pfile at it before starting up.


[oracle@orcl ~]$ cat testdbpfile.ora
db_name=CDBORCL
db_unique_name=testdb
sga_target=1G
pga_aggregate_target=500M
compatible=23.6.0.0
enable_pluggable_database=true
control_files='/u01/testdb/control01.ctl'
wallet_root=/u01/testdb/wallet
tde_configuration="KEYSTORE_CONFIGURATION=FILE"

[oracle@orcl ~]$

[oracle@orcl ~]$ . oraenv
ORACLE_SID = [cdborcl] ? testdb
ORACLE_HOME = [/home/oracle] ? /u01/app2/oracle/product/26ai/dbhome_1
The Oracle base remains unchanged with value /u01/app2/oracle
[oracle@orcl ~]$ sqlplus / as sysdba

SQL*Plus: Release 23.26.1.0.0 - Production on Wed Jun 24 15:56:26 2026
Version 23.26.1.0.0

Copyright (c) 1982, 2025, Oracle.  All rights reserved.

Connected to an idle instance.

SQL> def
DEFINE _DATE           = "24-JUN-26" (CHAR)
DEFINE _CONNECT_IDENTIFIER = "testdb" (CHAR)
DEFINE _USER           = "SYS" (CHAR)
DEFINE _PRIVILEGE      = "AS SYSDBA" (CHAR)
DEFINE _SQLPLUS_RELEASE = "2326010000" (CHAR)
DEFINE _EDITOR         = "vi" (CHAR)
DEFINE _O_VERSION      = "" (CHAR)
DEFINE _O_RELEASE      = "" (CHAR)
SQL> startup nomount pfile='/home/oracle/testdbpfile.ora';
ORACLE instance started.

Total System Global Area 1070370360 bytes
Fixed Size                  5017144 bytes
Variable Size             276824064 bytes
Database Buffers          780140544 bytes
Redo Buffers                8388608 bytes
SQL> show parameter wallet

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
ssl_wallet                           string
wallet_root                          string      /u01/testdb/wallet
SQL>
SQL> show parameter tde

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
one_step_plugin_for_pdb_with_tde     boolean     FALSE
tde_configuration                    string      KEYSTORE_CONFIGURATION=FILE
tde_key_cache                        boolean     FALSE
SQL>

Step 7: Restore the Backup Successfully


[oracle@orcl ~]$ rman target /

Recovery Manager: Release 23.26.1.0.0 - Production on Wed Jun 24 15:57:16 2026
Version 23.26.1.0.0

Copyright (c) 1982, 2026, Oracle and/or its affiliates.  All rights reserved.

connected to target database: CDBORCL (not mounted)

RMAN> restore controlfile from '/u01/backup/control.bkp';
restore controlfile from '/u01/backup/control.bkp';
Starting restore at 24-JUN-26
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=202 device type=DISK

channel ORA_DISK_1: restoring control file
channel ORA_DISK_1: restore complete, elapsed time: 00:00:01
output file name=/u01/testdb/control01.ctl
Finished restore at 24-JUN-26

RMAN> alter database mount;
alter database mount;
Statement processed

With the correct wallet in place, the restore now succeeds. This is the intended behavior: an encrypted RMAN backup is only usable by whoever also holds the matching wallet, so a stolen backup file on its own is worthless without it.

That covers the core building blocks of Oracle TDE: seeing the risk of unencrypted data at rest, setting up the wallet, encrypting at the tablespace and column level, enabling auto login for unattended restarts, and extending that same protection to RMAN backups.

Thank you for reading!

I hope this content has been helpful to you. Your feedback and suggestions are always welcome. Feel free to leave a comment or reach out with any queries.

Abhishek Shrivastava

Oracle DBA with hands-on experience managing production Data Guard, RAC, GoldenGate, and APEX environments. I write practical, tested installation and troubleshooting guides based on real deployment work.

📧 Email: oraeasyy@gmail.com
🌐 Website: www.oraeasy.com

Follow Us

Comments