El siguiente procedimiento está destinado a la configuración de una conexión segura entre Tomcat y Oracle 12.1.0 de forma que podamos garantizar la seguridad e integridad de las comunicaciones entre Tomcat y Oracle empleando canales seguros y cifrados en ambos extremos.
En primer lugar, debe configurarse correctamente el canal Oracle SSL en el listener. En este ejemplo usaremos el puerto #2484, pero se puede usar el que sea. sqlnet.ora debe configurarse con el protocolo de cifrado adecuado (SSL_CIPHER_SUITES) puesto que JAVA no acepta algunos protocolos por defecto de Oracle.
SQL_NET.AUTHENTICATION_SERVICES debe incluir el protocolo TCPS
Preparación
ORACLE_ID=$(docker run -d -p 1522:1521 -p 2484 -v /opt/oracle/data:/u01/app/oracle sath89/oracle-12c) docker exec -it $ORACLE_ID bash su - oracle export ORACLE_HOME=/u01/app/oracle/product/11.2.0/EE/ export PATH=$PATH:$ORACLE_HOME/bin cd $ORACLE_HOME orapki wallet create -wallet /u01/app/oracle/wallet -auto_login -pwd Passw0rd orapki wallet add -wallet /u01/app/oracle/wallet -dn CN=$(uname -n) -keysize 2048 -self_signed -validity 3650 -pwd Passw0rd orapki wallet display -wallet /u01/app/oracle/wallet cd $ORACLE_HOME/network/admin
Oracle
1.- Configuramos listener.ora de una manera apropiada para permitir conexiones ssl (TCPS)
listener.ora
WALLET_LOCATION = (SOURCE = (METHOD = FILE) (METHOD_DATA = (DIRECTORY = /u01/app/oracle/wallet) ) ) SSL_CLIENT_AUTHENTICATION = FALSE LISTENER = (DESCRIPTION_LIST = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = )(PORT = 1521)) (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521)) (ADDRESS = (PROTOCOL = TCPS)(HOST = )(PORT = 2484)) ) ) ADR_BASE_LISTENER = /u01/app/oracle
2.- También el tnsnames.ora
tnsnames.ora
TEST = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = )(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = )) ) TESTSSSL = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCPS)(HOST = )(PORT = 2484) ) (CONNECT_DATA = (SERVER = DEDICATED)(SERVICE_NAME = )) )
3.- A continuación el sqlnet.ora
sqlnet.ora
WALLET_LOCATION = (SOURCE = (METHOD = FILE) (METHOD_DATA = (DIRECTORY = /u01/app/oracle/wallet) ) ) SQLNET.AUTHENTICATION_SERVICES = (TCPS,NTS,BEQ) SSL_CLIENT_AUTHENTICATION = FALSE SSL_CIPHER_SUITES = (SSL_RSA_WITH_AES_256_CBC_SHA) SQLNET.WALLET_OVERRIDE = TRUE
4.- Una vez configurados estos 3 archivos, se debe reiniciar el listener. (lsnrctl stop ; lsnrctl start, y esperamos unos 30 segundos)
5.- Verificamos que responde correctamente
root@72795752816f:/u01/app/oracle/product/12.1.0/xe/network/admin# lsnrctl status LSNRCTL for Linux: Version 12.1.0.2.0 - Production on 23-JUN-2017 17:24:28 Copyright (c) 1991, 2014, Oracle. All rights reserved. Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=72795752816f)(PORT=1521))) STATUS of the LISTENER ------------------------ Alias LISTENER Version TNSLSNR for Linux: Version 12.1.0.2.0 - Production Start Date 21-JUN-2017 14:15:25 Uptime 2 days 3 hr. 9 min. 2 sec Trace Level off Security ON: Local OS Authentication SNMP OFF Listener Parameter File /u01/app/oracle/product/12.1.0/xe/network/admin/listener.ora Listener Log File /u01/app/oracle/diag/tnslsnr/72795752816f/listener/alert/log.xml Listening Endpoints Summary... (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=72795752816f)(PORT=1521))) (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521))) (DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)(HOST=72795752816f)(PORT=2484))) (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=72795752816f)(PORT=8080))(Presentation=HTTP)(Session=RAW)) Services Summary... Service "xe.oracle.docker" has 1 instance(s). Instance "xe", status READY, has 1 handler(s) for this service... Service "xeXDB.oracle.docker" has 1 instance(s). Instance "xe", status READY, has 1 handler(s) for this service... The command completed successfully
6.- Verificamos que podamos conectarnos con monedero (wallet) en modo simple y también en modo SSL
6.1.- Test tnsping
root@72795752816f:/u01/app/oracle/product/12.1.0/xe/network/admin# tnsping TEST TNS Ping Utility for Linux: Version 12.1.0.2.0 - Production on 23-JUN-2017 17:14:54 Copyright (c) 1997, 2014, Oracle. All rights reserved. Used parameter files: /u01/app/oracle/product/12.1.0/xe/network/admin/sqlnet.ora Used TNSNAMES adapter to resolve the alias Attempting to contact (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 72795752816f)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = xe.oracle.docker))) OK (0 msec) root@72795752816f:/u01/app/oracle/product/12.1.0/xe/network/admin# tnsping TESTSSL TNS Ping Utility for Linux: Version 12.1.0.2.0 - Production on 23-JUN-2017 17:14:56 Copyright (c) 1997, 2014, Oracle. All rights reserved. Used parameter files: /u01/app/oracle/product/12.1.0/xe/network/admin/sqlnet.ora Used TNSNAMES adapter to resolve the alias Attempting to contact (DESCRIPTION = (ADDRESS = (PROTOCOL = TCPS) (HOST = 72795752816f) (PORT = 2484)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = xe.oracle.docker))) OK (20 msec)
6.2.- Conectamos en plano
root@72795752816f:/u01/app/oracle/product/12.1.0/xe/network/admin# sqlplus system/oracle@TEST SQL*Plus: Release 12.1.0.2.0 Production on Fri Jun 23 17:17:54 2017 Copyright (c) 1982, 2014, Oracle. All rights reserved. Last Successful login time: Fri Jun 23 2017 17:12:14 +00:00 Connected to: Oracle Database 12c Standard Edition Release 12.1.0.2.0 - 64bit Production SQL> SELECT SYS_CONTEXT('USERENV', 'network_protocol') FROM DUAL; SYS_CONTEXT('USERENV','NETWORK_PROTOCOL') -------------------------------------------------------------------------------- tcp
6.3.- Conectamos en modo SSL
root@72795752816f:/u01/app/oracle/product/12.1.0/xe/network/admin# sqlplus /@TESTSSL SQL*Plus: Release 12.1.0.2.0 Production on Fri Jun 23 17:17:54 2017 Copyright (c) 1982, 2014, Oracle. All rights reserved. Last Successful login time: Fri Jun 23 2017 17:12:14 +00:00 Connected to: Oracle Database 12c Standard Edition Release 12.1.0.2.0 - 64bit Production SQL> SELECT SYS_CONTEXT('USERENV', 'network_protocol') FROM DUAL; SYS_CONTEXT('USERENV','NETWORK_PROTOCOL') -------------------------------------------------------------------------------- tcps
Tomcat
1.- Incluimos los certificados SSL en Oracle
[root@e62e32a0e821 tmp]# echo "" | openssl s_client -connect :2484 -showcerts 2>/dev/null | openssl x509 -out /tmp/certfile.txt
2.- Descargamos el fichero Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy (Obligatorio si se usa Oracle v12 … quizás también con las últimas versiones de Oracle v11) de e instalamos
echo "Hacemos un backup de los ficheros actuales" cp US_export_policy.jar $JAVA_HOME/lib/security/US_export_policy.jar cp local_policy.jar $JAVA_HOME/lib/security/local_policy.jar cp UnlimitedJCEPolicyJDK8/* $JAVA_HOME/lib/security
3.- Importamos en el trustrore de JAVA. Aquí podemos hacerlo de 2 maneras:
1.- Importando en el JAVA_HOME default trustStore
[root@e62e32a0e821 tmp]# cd $JAVA_HOME/lib/secutiry ; keytool -import -alias ca -file /tmp/certfile.txt -keystore cacerts -storepass changeit
2.-Importando en un nuevo trustStore
[root@e62e32a0e821 wallet]# cd $TOMCAT_WALLET_DIRECTORY ; [root@e62e32a0e821 wallet]# keytool -import -file /tmp/certfile.txt -alias oraclev12 -keystore truststore.jks Enter keystore password: Re-enter new password: Owner: CN=72795752816f Issuer: CN=72795752816f Serial number: 0 Valid from: Tue Jun 20 10:08:09 UTC 2017 until: Fri Jun 18 10:08:09 UTC 2027 Certificate fingerprints: MD5: DC:B8:01:5C:38:7B:DC:47:50:96:DC:21:D5:F5:48:60 SHA1: D9:38:20:22:0C:25:9F:03:17:18:CA:12:16:0D:79:BB:24:5F:4D:05 SHA256: 89:70:88:BA:83:A3:27:44:F8:A5:64:F9:BF:2C:FE:8C:01:80:29:12:45:94:35:75:EE:C6:D4:77:20:A1:D4:00 Signature algorithm name: MD5withRSA Version: 1 Trust this certificate? [no]: yes Certificate was added to keystore
4.- Generamos un keystore
[root@e62e32a0e821 wallet]# keytool -keystore clientkeystore.jks -genkey -alias client Enter keystore password: Re-enter new password: What is your first and last name? [Unknown]: What is the name of your organizational unit? [Unknown]: What is the name of your organization? [Unknown]: What is the name of your City or Locality? [Unknown]: What is the name of your State or Province? [Unknown]: What is the two-letter country code for this unit? [Unknown]: Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct? [no]: yes Enter key password for (RETURN if same as keystore password):
5.- Verificamos los datos del keystore
[root@e62e32a0e821 wallet]# keytool -list -v -keystore clientkeystore.jks Enter keystore password: Keystore type: JKS Keystore provider: SUN Your keystore contains 1 entry Alias name: client Creation date: Jun 23, 2017 Entry type: PrivateKeyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown Issuer: CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown Serial number: 602f2c40 Valid from: Fri Jun 23 16:28:51 UTC 2017 until: Thu Sep 21 16:28:51 UTC 2017 Certificate fingerprints: MD5: 19:F6:2E:65:00:76:97:14:85:8B:6F:30:3B:98:1F:80 SHA1: 2E:99:B9:20:54:2F:26:A3:68:AB:C3:27:4E:50:20:EC:D8:68:15:AB SHA256: 71:01:B2:12:53:8E:CC:7A:05:CD:69:4F:23:CD:C7:0B:C1:86:F0:68:71:21:A2:0B:43:9E:1F:3D:9C:02:B6:9C Signature algorithm name: SHA1withDSA Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: DA 20 DD 2D 8B 23 89 D1 A6 F7 B5 18 A1 54 FA AE . .-.#.......T.. 0010: 7D 60 44 42 .`DB ] ] ******************************************* *******************************************
6.- Verificamos que tnsnames.ora de Tomcat contiene el canal TCPS
tnsnames.ora
TESTSSSL = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCPS)(HOST = )(PORT = 2484)) (CONNECT_DATA = (SERVER = DEDICATED)(SERVICE_NAME = )) )
7.- Agregamos los siguientes parámetros a Tomcat’s setenv.sh
setenv.sh
############################### # DB CONNECTION CONFIGURATION # ############################### # Oracle DB (JNDI) CATALINA_OPTS+=" -Dspring.profiles.active=database-jndi " CATALINA_OPTS+=" -Doracle.net.tns_admin=/tomcat/wallet " CATALINA_OPTS+=" -Djavax.net.ssl.keyStore=/tomcat/wallet/keystore.jks " CATALINA_OPTS+=" -Djavax.net.ssl.keyStoreType=JKS " CATALINA_OPTS+=" -Djavax.net.ssl.keyStorePassword=Passw0rd " CATALINA_OPTS+=" -Djavax.net.ssl.trustStore=/tomcat/wallet/truststore.jks " # If we have used point #2.2 (If we have used JAVA default trustStore, comment this line out) CATALINA_OPTS+=" -Djavax.net.ssl.trustStorePassword=Passw0rd " # If we have used point #2.2 (If we have used JAVA default trustStore, comment this line out) CATALINA_OPTS+=" -Doracle.net.authentication_services=TCPS " CATALINA_OPTS+=" -Doracle.net.ssl_cipher_suites=TLS_RSA_WITH_AES_256_CBC_SHA " # Cipher protocol MUST match with the Oracle server
8.- El context.xml de Tomcat debería ser similar a
Troubleshooting
Podemos usar groovy http://groovy-lang.org/ si no queremos detener e iniciar Tomcat para probar los parámetros que vamos a pasar a Tomcat
1.- Generar un archivo test.groovy.
import java.security.Security; import java.sql.*; import java.io.FileInputStream; import java.io.Console; import java.util.Properties; import java.util.Scanner; import oracle.jdbc.*; this.getClass().classLoader.rootLoader.addURL(new File("./lib/ojdbc8.jar").toURL()) String url = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCPS)(HOST=<nombre del servidor Oracle/IP>)(PORT=2484))(CONNECT_DATA=(SERVICE_NAME=)))"; System.out.println("set properties"); Properties props = new Properties(); props.setProperty("user", ""); props.setProperty("password", ""); System.out.println("get connection"); Connection con = DriverManager.getConnection(url, props); System.out.println("got a connection"); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("select sysdate from dual"); while (rs.next()) { System.out.println("result = "+rs.getString(1)); } rs.close(); stmt.close(); con.close();
2.- Probamos el fichero groovy
[root@e62e32a0e821 bin]# /tomcat/groovy-2.4.11/bin/groovy -Djavax.net.debug=all \ -Djavax.net.ssl.keyStore=/tomcat/wallet/keystore.jks -Djavax.net.ssl.keyStoreType=JKS \ -Djavax.net.ssl.keyStorePassword=Passw0rd \ -Djavax.net.ssl.trustStore=/tomcat/wallet/truststore.jks \ -Djavax.net.ssl.trustStorePassword=Passw0rd \ -Doracle.net.authentication_services=TCPS \ -Dhttps.protocols=TLSv1.2 \ -Dhttps.cipherSuites=TLS_RSA_WITH_AES_128_CBC_SHA \ /tomcat/test.groovy set properties get connection *** found key for : tomcat chain [0] = [ [ Version: V3 Subject: CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11 .... (some time later) ... [Raw write]: length = 37 0000: 15 03 01 00 20 43 38 DA D4 24 6C C1 E7 8C A5 B9 .... C8..$l..... 0010: 4D 1C D2 20 07 B6 EF 5C C8 F9 E6 59 29 0A 9A 92 M.. ...\...Y)... 0020: 45 6A 41 59 94 EjAY. main, called closeSocket(true)
3.- Podemos eliminar el -Djavax.net.debug=all y probar de nuevo y veremos el resultado de la consulta en el archivo groovy:
( ResultSet rs = stmt.executeQuery("select sysdate from dual"); )
[root@e62e32a0e821 bin]# /tomcat/groovy-2.4.11/bin/groovy \ -Djavax.net.ssl.keyStore=/tomcat/wallet/keystore.jks -Djavax.net.ssl.keyStoreType=JKS \ -Djavax.net.ssl.keyStorePassword=Passw0rd \ -Djavax.net.ssl.trustStore=/tomcat/wallet/truststore.jks \ -Djavax.net.ssl.trustStorePassword=Passw0rd \ -Doracle.net.authentication_services=TCPS \ -Dhttps.protocols=TLSv1.2 \ -Dhttps.cipherSuites=TLS_RSA_WITH_AES_128_CBC_SHA \ /tomcat/test.groovy set properties get connection got a connection result = 2017-06-23 17:12:14.0