You don’t need a forecast about AI anymore. Just compare how your team worked six months ago to how they work now.
The analyst who used to lose a morning to a spreadsheet finishes before her coffee gets cold. The proposal that took three people and two rounds of review gets drafted in one sitting. Nobody sent a memo about any of this. People just quietly stopped doing things the slow way.
Attackers made the same switch. They didn’t have to fill out a procurement form to do it.
Every capability that makes AI useful to your business makes it just as useful to the people trying to break into it.
As I mentioned earlier, threat actors are already using these tools. What I didn’t get into is why that matters more than it sounds. The bottleneck for many attackers used to be skill. Writing a working exploit chain, or a script that pivots cleanly across an environment, took someone who actually knew what they were doing. That gate is mostly gone. The plan and the code now take minutes, and they don’t require the person running them to understand either one.
My honest read: we’re going to see a lot more breaches over the next few years, and many are already underway in environments where nobody has noticed yet. The people who should be losing sleep over this are cloud architects, database architects, and database engineers, because when the perimeter stops holding, what’s left is whatever you did to the data itself.
Which brings me to data at rest. If you haven’t looked at it seriously, now is the time. Transparent Data Encryption has been in Oracle for a while, but it’s shifted from a nice-to-have to something auditors and cloud baselines increasingly assume you’ve already turned on.
In this blog, I’ll walk through enabling TDE and how to verify the keystore is actually open and working afterward – the part people tend to skip.
Everything below is from a local VM running a 19c container database. If you’re on ExaCS, DBCS, or Base Database Service, the paths are different, and some of this is already provisioned for you. Check your service docs before copying paths verbatim.
TDE needs somewhere to keep the keystore files. Create it before touching the database.
[oracle@db-pri ORCL19C]$ pwd/u01/app/oracle/admin/ORCL19C[oracle@db-pri ORCL19C]$ mkdir wallet_root[oracle@db-pri ORCL19C]$ cd wallet_root/[oracle@db-pri wallet_root]$ mkdir tde |
The tde subdirectory name isn’t optional. When WALLET_ROOT is set, Oracle looks for a software keystore in WALLET_ROOT/tde specifically.
SQL> ALTER SYSTEM SET WALLET_ROOT='/u01/app/oracle/admin/ORCL19C/wallet_root' SCOPE=SPFILE;SQL> shut immediate;SQL> startup; |
Confirm it came back the way you expect:
| SQL> show parameter wallet NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ ssl_wallet string wallet_root string /u01/app/oracle/admin/ORCL19C/ wallet_root |
Once the database is up, set the keystore type. This one is dynamic; no restart needed:
SQL> ALTER SYSTEM SET TDE_CONFIGURATION="KEYSTORE_CONFIGURATION=FILE" SCOPE=BOTH; |
WALLET_ROOT plus TDE_CONFIGURATION is the modern way to do this. If you’ve followed older guides, you’ll have seen ENCRYPTION_WALLET_LOCATION in sqlnet.ora instead; that’s deprecated, and mixing the two is a good way to spend an afternoon debugging a keystore that won’t open.
SQL> ADMINISTER KEY MANAGEMENT CREATE KEYSTORE '/u01/app/oracle/admin/ORCL19C/wallet_root/tde' IDENTIFIED BY "F1LE2025!";keystore altered.SQL> ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY "F1LE2025!";keystore altered. |
Because WALLET_ROOT is set, you don’t need to pass a path. Oracle puts the keystore in WALLET_ROOT/tde on its own. Passing the full path works too, but leaving it out means one less place for the two to drift apart.
Obviously, don’t ship F1LE2025! to production. And keep the real password out of shell history and out of any script that ends up in Git.
Creating the keystore doesn’t give you a key. This does:
SQL> ADMINISTER KEY MANAGEMENT SET KEY IDENTIFIED BY "F1LE2025!" WITH BACKUP;keystore altered. |
WITH BACKUP snapshots the keystore before it’s modified. For a password-based keystore, it’s mandatory, and it’s the reason you’ll see an extra file appear in a moment. You can add USING 'pre_first_key' or similar to tag the backup, which is worth doing once you have more than one.
At this point, the keystore is open, but only because you opened it manually. Restart now, and it comes back closed, and anything encrypted is unreadable until someone logs in and opens it by hand.
No DBA wants to hand-open a wallet at 3 am after an unplanned restart. An auto-login keystore opens itself when the database starts.
SQL> ADMINISTER KEY MANAGEMENT CREATE AUTO_LOGIN KEYSTORE 2 FROM KEYSTORE '/u01/app/oracle/admin/ORCL19C/wallet_root/tde' 3 IDENTIFIED BY "F1LE2025!";keystore altered. |
There’s a second form of this command worth knowing about:
Note: AUTO_LOGIN vs LOCAL AUTO_LOGIN
ADMINISTER KEY MANAGEMENT CREATE LOCAL AUTO_LOGIN KEYSTOREFROM KEYSTORE '/u01/app/oracle/admin/ORCL19C/wallet_root/tde'IDENTIFIED BY "F1LE2025!"; |
A local auto-login keystore is tied to the host that created it. Copy the cwallet.sso to another server, and it won’t open there – you’d need the password-protected file ewallet.p12 and the password to get anywhere. That’s a meaningful hardening step given everything above about stolen files and misplaced backups.
The tradeoff is portability. Cloning the database to a new host, refreshing a lower environment, or standing up a standby all mean regenerating the .sso on each machine, because the one you have won’t travel. On Data Guard in particular, plan for creating it separately on the standby.
Rule of thumb: LOCAL on anything holding real data, and plain AUTO_LOGIN where you’re cloning and refreshing constantly and the convenience is worth more than the extra barrier.
Now restart the database and check whether the wallet comes back on its own. Don’t open anything manually; that’s the whole point of the test.
|
|
SQL> !ls -l /u01/app/oracle/admin/ORCL19C/wallet_root/tdetotal 12-rw-------. 1 oracle oinstall 4038 Jul 28 11:03 cwallet.sso-rw-------. 1 oracle oinstall 2553 Jul 28 11:03 ewallet_2026072815034150.p12-rw-------. 1 oracle oinstall 3993 Jul 28 11:03 ewallet.p12 |
Three files, each with a job:
ewallet.p12 – the password-protected keystore. This is the real one.
ewallet_<timestamp>.p12 – the backup taken by WITH BACKUP in step 4.
cwallet.sso – the auto-login keystore from step 5.
Permissions should be 600 and owned by the Oracle software owner. If they’re anything looser, fix that now.
The file listing tells you files exist. It doesn’t tell you the database is using them. This does:
set lines 600col WALLET for a20col WALLET_LOCATION for a80col Status for a20select WRL_TYPE wallet,status,WALLET_TYPE,wrl_parameter wallet_location,KEYSTORE_MODE from v$encryption_wallet;WALLET STATUS WALLET_TYPE WALLET_LOCATION KEYSTORE-------------------- -------------------- -------------------- -------------------------------------------------- --------FILE OPEN AUTOLOGIN /u01/app/oracle/admin/ORCL19C/wallet_root/tde/ NONEFILE OPEN AUTOLOGIN UNITEDFILE OPEN_NO_MASTER_KEY AUTOLOGIN UNITED |
You want STATUS = OPEN and WALLET_TYPE = AUTOLOGIN. If WALLET_TYPE still says PASSWORD, the .sso isn’t being picked up. The real test is a restart: bounce the database, run the query again without opening anything manually, and confirm it comes back OPEN on its own.
Everything so far has been at the container level: the wallet, the keystore, the master key in CDB$ROOT. That doesn’t cover your pluggable databases.
This trips people up constantly. The CDB is configured, V$ENCRYPTION_WALLET says OPEN, so it feels done. But in united mode, each PDB needs its own master encryption key, and until you create one, that PDB has nothing to encrypt with. Here’s how to check, using HR_PDB as the example.
SQL> show pdbs CON_ID CON_NAME OPEN MODE RESTRICTED---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 HR_PDB READ WRITE NOSQL> alter session set container=HR_PDB;Session altered.SQL> set lines 600col CREATION_TIME for a40col TAG for a30select key_id,tag,keystore_type,creation_time from v$encryption_keys;SQL> SQL> SQL>no rows selectedSQL> |
No rows. The PDB has no master key yet, which is exactly what you’d expect at this point.
alter session set container=HR_PDB;ADMINISTER KEY MANAGEMENT SET KEY FORCE KEYSTORE IDENTIFIED BY "F1LE2025!" WITH BACKUP; |
FORCE KEYSTORE is there because of the auto-login keystore you created earlier. With auto-login in place, the keystore is open in auto-login mode, and a password-based operation like this one would otherwise fail. FORCE KEYSTORE tells Oracle to use the password anyway.
WITH BACKUP does the same thing it did at the CDB level: snapshots the keystore before modifying it. You’ll see another timestamped .p12 land in the tde directory.
SQL> set lines 600col CREATION_TIME for a40col TAG for a30select key_id,tag,keystore_type,creation_time from v$encryption_keys;SQL> SQL> SQL>KEY_ID TAG KEYSTORE_TYPE CREATION_TIME------------------------------------------------------------------------------ ------------------------------ ----------------- ----------------------------------------ASdx87E3Sk8yvyFMwMy83+0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA SOFTWARE KEYSTORE 06-AUG-26 03.33.59.225768 PM +00:00SQL> |
A key, a keystore type, a creation timestamp. That PDB can now encrypt data.
Repeat this for every PDB that needs encryption. And if you plug in a PDB later, or clone one, it comes over without a key of its own. So this step belongs in your provisioning checklist, not just your one-time setup notes.
Here’s a problem you’ll hit eventually: someone hands you a keystore password, and you need to know whether it’s the right one.
The obvious test- close the keystore and reopen it- is not something you do on a running production database. Close it, and every encrypted tablespace becomes unreadable until it’s back open. That’s an outage, and it’s a bad way to find out the password was wrong.
mkstore gives you a read-only way to check. Point it at the wallet directory and list the contents:
mkstore -wrl /u01/app/oracle/admin/ORCL19C/wallet_root/tde/ -list |
It prompts for the password and reads ewallet.p12 directly off disk. The database isn’t involved at all, and nothing about its state changes.
[oracle@db-pri ~]$ mkstore -wrl /u01/app/oracle/admin/ORCL19C/wallet_root/tde/ -listOracle Secret Store Tool Release 19.0.0.0.0 - ProductionVersion 19.4.0.0.0Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved.Enter wallet password:Oracle Secret Store entries:ORACLE.SECURITY.DB.ENCRYPTION.ASdx87E3Sk8yvyFMwMy83+0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAORACLE.SECURITY.DB.ENCRYPTION.AW23RaiVQk8iv76dr14HYkEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAORACLE.SECURITY.DB.ENCRYPTION.MASTERKEYORACLE.SECURITY.DB.ENCRYPTION.MASTERKEY.579C7A65500978CBE0650A0027DC3B82ORACLE.SECURITY.ID.ENCRYPTION.ORACLE.SECURITY.KB.ENCRYPTION.ORACLE.SECURITY.KM.ENCRYPTION.ASdx87E3Sk8yvyFMwMy83+0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAORACLE.SECURITY.KM.ENCRYPTION.AW23RaiVQk8iv76dr14HYkEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[oracle@db-pri ~]$ |
If you get a listing, the password is correct. If it’s wrong, mkstore fails to open the wallet and tells you so — no listing, no impact on the database.
The entries themselves are worth a quick read. ORACLE.SECURITY.DB.ENCRYPTION.MASTERKEY points to the master key currently in use, and the KM.ENCRYPTION entry with the matching key ID is the key material itself. After a rekey, you’ll see the older key IDs still listed, which is how the database keeps reading data encrypted under previous keys.
A few practical notes:
Run this as the Oracle software owner. The wallet files are 600, so nobody else can read them.
The trailing slash on the path matters in some releases. If mkstore complains it can’t find a wallet, that’s the first thing to check.
Type the password at the prompt rather than passing it on the command line. Anything you type as an argument shows up in ps output and in your shell history.
Use the same approach before a maintenance window that involves the keystore. Confirming the password beforehand is cheaper than discovering it’s wrong halfway through a rekey.
Enabling TDE in 19c comes down to a handful of steps: create the wallet directory, point WALLET_ROOT at it, create and open the keystore, set the master key with WITH BACKUP, and add auto-login so the wallet opens on its own after a restart. If you’re running a CDB, remember that each PDB needs its own key; the container-level setup doesn’t cover them. And when you need to verify the keystore password on a live system, mkstore -list does it without touching the database.
None of that is difficult. The reason to do it now is that the cost of getting into an environment is falling fast, and unencrypted data files are the easiest thing in the world to walk away with. TDE won’t stop a stolen credential, but it makes sure a stolen file is worthless.
Get this in place, then look at moving your keys off the database host. Encryption only helps if the keys don’t travel with the data, and an auto-login wallet sitting next to the datafiles it protects is exactly the arrangement you want to get away from. Oracle Key Vault is the natural next step – I’ve written about it in more detail here:
Oracle Key Vault Endpoint Health Checks: Best Practices and Troubleshooting – keeping endpoints healthy once OKV is running.
Protecting Your Encryption Keys: Lessons from the Oracle Cloud Security Breach (OKV Part 1) – why centralized key management matters, framed around a real incident.
Protecting Your Encryption Keys (OKV Part 2) – the follow-up.
For more information, check out our Oracle Database Services, or contact us today, and one of our experts will be in touch.