Oracle TLS/SSL Encryption with CA-Signed Certificates

Introduction

This tutorial walks through enabling TLS/SSL encryption between an application and Oracle database, including basic encryption, certificate validation, distinguished name validation, and mutual authentication.

It also demonstrates how to use existing private keys and certificates rather than private keys and self-signed certificates generated by Oracle-provided tools.

The certificates may have been generated by a well-known certificate authority or by an in-house certificate authority. Either way, they are presumed to already exist and the desire is to use them to enable TLS/SSL encryption, rather than creating new ones.

Background

Oracle really wants you to use their wallets in the following manner:

  • Create a wallet.
  • Create a certificate signing request in the wallet.
  • Export the certificate signing request.
  • Send the certificate signing request to a certificate authority and obtain a certificate.
  • Add the certificate to the wallet.

Because of the way that the Oracle-provided tools work, though, the "Create a certificate signing request in the wallet" step also (silently) creates a private key and adds it to the wallet. There is no other way to add a key to a wallet, and attempts to add certificates for which there is no matching key will fail.

Thus, if you already have a certificate and key, acquired by some other means, then there is no way (using Oracle-provided tools) to add them to a wallet.

There is a relatively straightforward workaround though.

Since:

  • A "standard" Oracle wallet is just a directory containing a PKCS#12 keystore, and a lock file.
  • Oracle-provided tools can upgrade "standard" wallets to auto-login wallets.
  • We need an auto-login wallet.
  • The upgrade process just reads the PKCS#12 keystore and creates the other files, as necessary.

And:

  • OpenSSL can create a PKCS#12 keystore and populate it with any cert, key, and CA cert that you give it.

Then:

  • We can create a "standard" wallet manually and then upgrade it to an auto-login wallet.

That is the approach we'll use in this tutorial.

Assumptions

The following assumptions are made:

  • An Oracle database is running on a linux server named db.firstworks.com.
  • The database was installed by the oracle user.
  • The database contains an instance named ora1 which is accessible using scott/tiger credentials.
  • An application which accesses the database is installed on a linux server named app.firstworks.com.
  • Oracle Instant Client is the only Oracle software installed on app.firstworks.com.
  • It is possible to scp from db.firstworks.com to root@app.firstworks.com.
  • It is possible to scp from app.firstworks.com to oracle@db.firstworks.com.
  • A private key and certificate for the database exist, named db.key and db.crt.
  • A private key and certificate for the application exist, named app.key and app.crt.
  • A certificate chain for the certificate authority which signed db.crt and app.crt exists, named ca.pem.

Basic Encryption

First, we will configure basic TLS/SSL encryption of the communications between the database and application server.

Database Server Configuration

Copy db.crt, db.key, and ca.pem to the database server.

Log in to the database server as the oracle user, and...

  • Create a wallet manually.
  • Upgrade it to an auto-login wallet.
mkdir /u01/app/oracle/wallet
openssl pkcs12 -export -in db.crt -inkey db.key -certfile ca.pem -out /u01/app/oracle/wallet/ewallet.p12
orapki wallet create -wallet /u01/app/oracle/wallet -auto_login -pwd Passw0rd

Enter Passw0rd for the password when prompted by openssl.

The openssl options above do the following things:

  • -export causes an output file to be created
  • -in and -inkey specify the cert and key
  • -certfile specifies the certificate authority's certificate chain
  • -out specifies the output file (this must be ewallet.p12)

The orapki options above do the following things:

  • -wallet specifies the path for the wallet.
    • A wallet is just a directory with some files under it.
    • If you need to delete a wallet, just delete the directory.
    • Since the path already exists, and contains a properly named PKCS#12 file, the wallet will be upgraded in-place, rather than created.
  • -auto_login indicates that the wallet will be used for logins.
    • There is also an alternative -auto_login_local option that locks the wallet down to the local machine. If the wallet is copied or moved to a different machine then attempts to use it will fail.
    • These options can also be omitted, but it's not clear what a wallet created without one of them can be used for.
  • -pwd specifies a password for the wallet.
    • This password must be supplied to modify the wallet, but is not required to use the wallet. It may be used by any user with permissions to read its files.
    • If the -pwd option is omitted then the user will be prompted for the password on the command line.
    • Passwords must contain 8 characters, and must contain at least 1 number.
    • Obviously a more secure password than Passw0rd should be used.

You can display contents of the wallet as follows:

orapki wallet display -wallet /u01/app/oracle/wallet

You should see CN=db.firstworks.com as User Certificate and your certificate authority as a Trusted Certificate.

Next, configure the database instance to use the wallet.

Edit $ORACLE_HOME/network/admin/sqlnet.ora and append these lines:

WALLET_LOCATION =
   (SOURCE =
     (METHOD = FILE)
     (METHOD_DATA =
       (DIRECTORY = /u01/app/oracle/wallet)
     )
   )

SQLNET.AUTHENTICATION_SERVICES = ALL
SSL_CLIENT_AUTHENTICATION = FALSE
SSL_CIPHER_SUITES = (SSL_RSA_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA)

These lines:

  • Provide the path to the wallet.
  • Enable "all" authentication services (including TLS/SSL), as opposed to only allowing database user/password authentication.
  • Disables authentication of the peer's certificate. For now we just want to encrypt communications.
  • Enables a set of cipher suites. The default is "none" so a set of ciphers has to be explicitly provided.

Edit $ORACLE_HOME/network/admin/listener.ora and add these lines near the top:

WALLET_LOCATION =
  (SOURCE =
    (METHOD = FILE)
    (METHOD_DATA =
      (DIRECTORY = /u01/app/oracle/wallet)
    )
  )

SSL_CLIENT_AUTHENTICATION = FALSE

These lines do the same things that they do in sqlnet.ora. It's not clear why they have to be present in both files.

Also, edit $ORACLE_HOME/network/admin/listener.ora and update the LISTENER definition, adding an entry for the TCPS protocol on port 2484:

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = db.firstworks.com)(PORT = 1521))
      (ADDRESS = (PROTOCOL = TCPS)(HOST = db.firstworks.com)(PORT = 2484))
    )
  )

Now, bounce the listener:

lsnrctl stop
lsnrctl start

That's all on the database server.

Application Server Configuration

Oracle clients don't create ephemeral keys, so on the application server, a wallet must also be created and populated with a User Certificate for the application.

Fortunately, this is relatively straightforward.

Copy app.crt, app.key, and ca.pem to the database server.

Log in to the database server as the oracle user, and...

  • Create a wallet in the local diretory, manually.
  • Upgrade it to an auto-login wallet.
  • Tar up the local wallet.
  • Copy it to the application server.
  • Clean up.
mkdir ./wallet
openssl pkcs12 -export -in app.crt -inkey app.key -certfile ca.pem -out ./wallet/ewallet.p12
orapki wallet create -wallet ./wallet -auto_login -pwd Passw0rd
tar cf wallet.tar wallet
scp wallet.tar root@app.firstworks.com:
rm -r wallet wallet.tar

Log in to the application server as root, and...

  • Create a directory to serve as the ORACLE_HOME, with network/admin subdirectories.
  • Extract the wallet into the ORACLE_HOME.
  • Fix permissions.
  • Clean up.
mkdir -p /var/oraclehome
mkdir -p /var/oraclehome/network/admin
tar xf ~/wallet.tar
chmod 755 wallet
chmod 644 wallet/*
rm ~/wallet.tar

Create /var/oraclehome/network/admin/sqlnet.ora with the following contents:

WALLET_LOCATION =
   (SOURCE =
     (METHOD = FILE)
     (METHOD_DATA =
       (DIRECTORY = /u01/app/oracle/wallet)
     )
   )

SQLNET.AUTHENTICATION_SERVICES = ALL
SSL_CLIENT_AUTHENTICATION = FALSE
SSL_CIPHER_SUITES = (SSL_RSA_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA)

Create /var/oraclehome/network/admin/tnsnames.ora with the following contents:

ora1=
(DESCRIPTION =
        (ADDRESS =
                (PROTOCOL = TCPS)
                (HOST = db.firstworks.com)
                (PORT = 2484)
        )
        (CONNECT_DATA =
                (SERVER = DEDICATED)
                (SERVICE_NAME = ora1)
        )
)

Note that PROTOCOL = TPCS and PORT = 2484 instead of TCP/1521.

That's all on the application server.

Testing With SQLPlus

Connect to the database from the application server using sqlplus as follows:

ORACLE_HOME=/var/oraclehome sqlplus scott/tiger@ora1

You can verify that the connection is TLS/SSL-secured by running:

select sys_context('USERENV','network_protocol') from dual;

SYS_CONTEXT('USERENV','NETWORK_PROTOCOL')
--------------------------------------------------------------------------------
tcps

If the connection fails, then it may fail for one of these common, but also very cryptic errors:

  • ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
    • The listener isn't ready.
    • It can take a while. Try again in a minute or two.
  • ORA-28864: SSL connection closed gracefully
    • The wallet on the database server was created without the -auto_login option.
    • Remove the wallet, re-create the wallet with the -auto_login option, and bounce the listener.
  • ORA-29106: Cannot import PKSC #12 wallet
    • The application server's wallet was created with the -auto_login_local option.
    • Remove the wallet, re-create the wallet with the -auto_login option instead of -auto_login_local, and move the new wallet to the application server.
  • ORA-29024: Certificate validation failure
    • You accidentally omitted or mispelled SSL_CLIENT_AUTHENTICATION = FALSE in sqlnet.ora, on the application server.
    • Update sqlnet.ora on the application server with SSL_CLIENT_AUTHENTICATION = FALSE
  • ORA-28860: Fatal SSL error
    • You accidentally omitted or mispelled SSL_CLIENT_AUTHENTICATION = FALSE in sqlnet.ora and/or listener.ora, on the database server.
    • Update sqlnet.ora and/or listener.ora on the database server with SSL_CLIENT_AUTHENTICATION = FALSE and bounce the listener.

Updating Your Application

To get your application to use the new configuration:

You must set the environment variable ORACLE_HOME to /var/oraclehome, either system-wide, in the app's startup script, or in the app's config file.

You must update your application to use the SID "ora1" defined in the tnsnames.ora files, rather than the full database connection string. This is typically done in the app's config file, or sometimes in app code itself. For some apps, you have to set the environment variable ORACLE_SID, either system-wide, in the app's startup script, or in the app's config file.

Adding Database Certificate Validation

So far, we have encrypted communications between the application and the database, but we can make the application more secure by validating the database server's certificate.

The basic steps are:

  • Extract the database server's certificate.
  • Add it to the application server's wallet.
  • Set SSL_CLIENT_AUTHENTICATION = TRUE on the application server.

Conceptually, this isn't very complex, but lack of orapki on the application server makes the process take a lot of unintuitive steps.

Log in to the database server as the oracle user, and export the database server's certificate.

orapki wallet export -wallet /u01/app/oracle/wallet -dn CN=db.firstworks.com -cert db.crt -pwd Passw0rd

Log in to the application server as root, and...

  • Tar up the wallet.
  • Copy it to the database server.
  • Clean up.
cd /var/oraclehome
tar cf wallet.tar wallet
scp wallet.tar oracle@db.firstworks.com:
rm wallet.tar

Log in to the database server as the oracle user, and...

  • Extract the wallet locally.
  • Add the database server's certificate as a Trusted Certificate to the local wallet.
  • Tar up the local wallet.
  • Copy it to the application server.
  • Clean up.
tar xf wallet.tar
rm wallet.tar
orapki wallet add -wallet ./wallet -trusted_cert -cert db.crt -pwd Passw0rd
tar cf wallet.tar wallet
scp wallet.tar root@app.firstworks.com:
rm -r wallet wallet.tar
rm db.crt

Log in to the application server as root, and...

  • Remove the old wallet.
  • Extract the updated wallet.
  • Fix permissions.
  • Clean up.
cd /var/oraclehome
rm -r wallet
tar xf ~/wallet.tar
chmod 755 wallet
chmod 644 wallet/*
rm ~/wallet.tar

Reconfigure the application server to validate the database's certificate against the set of Trusted Certificates in the wallet by enabling SSL_CLIENT_AUTHENTICATION. Edit /var/oraclehome/network/admin/sqlnet.ora and set:

SSL_CLIENT_AUTHENTICATION = TRUE

The parameter name SSL_CLIENT_AUTHENTICATION is a bit of a misnomer, since we're using it to authenticate the database server's certificate. It should probably be called SSL_PEER_AUTHENTICATION.

Test the configuration using sqlplus:

ORACLE_HOME=/var/oraclehome sqlplus scott/tiger@ora1

If the connection fails, then it will likely fail with:

  • ORA-29024: Certificate validation failure
    • SSL_CLIENT_AUTHENTICATION = TRUE may be misspelled.
    • Some wallet-update or wallet-transfer step failed or was skipped.
    • In either case, just try again.

Adding Database Certificate Distinguished Name Validation

So far, we are encrypting communications between the application and the database and validating the database server's certificate, but we can also make the application more secure by validating the distinguished name in the database server's certificate.

Log in to the application server as root.

Edit /var/oraclehome/network/admin/sqlnet.ora, and add the line:

SSL_SERVER_DN_MATCH = TRUE

Edit /var/oraclehome/network/admin/tnsnames.ora, and add a SECURITY section to the ora1 entry as follows:

ora1=
(DESCRIPTION =
        (ADDRESS =
                (PROTOCOL = TCPS)
                (HOST = db.firstworks.com)
                (PORT = 2484)
        )
        (CONNECT_DATA =
                (SERVER = DEDICATED)
                (SERVICE_NAME = ora1)
        )
 (SECURITY =
  (SSL_SERVER_CERT_DN = "CN=db.firstworks.com")
 )
)

Then you can test the configuration using sqlplus:

ORACLE_HOME=/var/oraclehome sqlplus scott/tiger@ora1

If the connection fails, then it will likely fail with:

  • ORA-29003: SSL transport detected mismatched server certificate.
    • The distinguished name in the database server's cert didn't match the SSL_SERVER_CERT_DN entry in the tnsnames.ora file.
    • Verify the spelling of the distinguished name in the tnsnames.ora and certificate, and update the entry in the tnsnames.ora accordingly. Alternatively, you could delete both wallets, start completely over and make sure to spell the name correctly in the certificate.

Adding Mutual Authentication

Just as we configured the application server to validate the database's certificate, it is also possible to configure the database server to validate the application server's certificate.

The basic steps are:

  • Extract the application server's certificate.
  • Add it to the database server's wallet.
  • Set SSL_CLIENT_AUTHENTICATION = TRUE on the database server.
  • Bounce the listener.

Conceptually, this isn't very complex, but lack of orapki on the application server makes the process take several unintuitive steps.

Log in to the application server as root, and...

  • Tar up the wallet.
  • Copy it to the database server.
  • Clean up.
cd /var/oraclehome
tar cf wallet.tar wallet
scp wallet.tar oracle@db.firstworks.com:
rm wallet.tar

Log in to the database server as the oracle user, and...

  • Extract the wallet locally.
  • Export the application server's certificate from the local wallet.
  • Add the application server's certificate as a Trusted Certificate to the database server's wallet.
  • Clean up.
tar xf wallet.tar
orapki wallet export -wallet ./wallet -dn CN=app.firstworks.com -cert app.crt -pwd Passw0rd
orapki wallet add -wallet /u01/app/oracle/wallet -trusted_cert -cert app.crt -pwd Passw0rd
rm -r wallet wallet.tar app.crt

Edit $ORACLE_HOME/network/admin/sqlnet.ora, and update the SSL_CLIENT_AUTHENTICATION parameter to:

SSL_CLIENT_AUTHENTICATION = TRUE

Edit $ORACLE_HOME/network/admin/listener.ora, and update the SSL_CLIENT_AUTHENTICATION parameter to:

SSL_CLIENT_AUTHENTICATION = TRUE

This line does the same thing that it does in sqlnet.ora. It's not clear why it has to be present in both files.

Bounce the listener:

lsnrctl stop
lsnrctl start

Then you can test the configuration from the application server, using sqlplus:

ORACLE_HOME=/var/oraclehome sqlplus scott/tiger@ora1

If the connection fails, then it will likely fail with:

  • ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
    • The listener isn't ready.
    • It can take a while. Try again in a minute or two.
  • ORA-28860: Fatal SSL error
    • SSL_CLIENT_AUTHENTICATION = TRUE may be omitted or misspelled in sqlnet.ora and/or listener.ora.
    • Some wallet-update or wallet-transfer step failed or was skipped.
    • In either case, just try again and bounce the listener.

There is no obvious way to validate the distinguished name in the application server's certificate. I suspect that this just isn't supported.

No comments:

Post a Comment