背景
基于安全性考虑,除了应用启用SSL访问以外,数据库也要启用SSL访问。涉及几块内容:
- Oracle启用SSL设置
- 客户端访问SSL设置(Java客户端, NET客户端)
- 客户端工具连接访问
Oracle启用SSL设置(以Linux服务器为例)
服务端创建Wallet
主要用于存储产生的一些证书,和秘钥信息
- 一般oracle管理,都是会建在单独的oracle用户下
mkdir -p /home/oracle/wallets
cd /home/oracle/wallets #进入当前目录
- 创建auto-login的wallet
orapki wallet create -wallet ./server_wallet -auto_login -pwd Zjzs_123
# 其中 ./server_wallet 表示当前路径的server_wallet的文件夹下,也就是/home/oracle/wallets
# -pwd Zjzs_123 代表需要秘钥,Zjzs_123这个可以自己设置,后面要用到
- 创建一个自签名的证书
orapki wallet add -wallet ./server_wallet -dn "CN=server" -keysize 1024 -self_signed -validity 365 -pwd Zjzs_123
# 注意路径和密码
- 发布服务器的wallet信息
orapki wallet display -wallet ./server_wallet
# 注意路径
- 导出证书信息(秘钥)
orapki wallet export -wallet ./server_wallet -dn "CN=server" -cert ./server_wallet/cert.txt
# 注意路径
基本上到这里,服务端的wallet已经搞定
最终生成三个文件
cert.txt (导出证书的秘钥信息,后续用于和客户端进行秘钥认证)
cwallet.sso 证书和秘钥
ewallet.p12 证书和秘钥
客户端创建wallet
客户端一般是Windows环境,注意安装Oracle客户端,以及设置环境变量,如果没有设置环境变量,则进入到$ORACLE_CLIENT_HOME/bin路径下,使用oracle pki命令
启用cmd命令提示符
- 创建客户端wallet
orapki wallet create -wallet E:\client_wallet -auto_login -pwd Zjzs_123
# E:\client_wallet wallet存储路径
# -pwd 证书密码 ,自己设置
- 创建自签名证书
orapki wallet add -wallet E:\client_wallet -dn "CN=client" -keysize 1024 -self_signed -validity 365 -pwd Zjzs_123
# 注意路径和密码
- 导出证书信息(秘钥)
orapki wallet export -wallet E:\client_wallet -dn "CN=client" -cert E:\client_wallet\cert.txt
到此,客户端的wallet和证书已经创建成功
最终生成三个文件
cert.txt (导出证书的秘钥信息,后续用于和服务端进行秘钥认证)
cwallet.sso 证书和秘钥
ewallet.p12 证书和秘钥
服务端和客户端的秘钥互认证
服务端认证客户端秘钥
- 客户端产生的cert.txt 上传到服务器 (E:\client_wallet\cert.txt)
- trust认证
orapki wallet add -wallet /home/oracle/wallets/server_wallet -trusted_cert -cert /home/oracle/cert.txt -pwd Zjzs_123
# /home/oracle/wallets/server_wallet服务端wallet路径
# /home/oracle/cert.txt 上传的客户端cert.txt路径
# -pwd Zjzs_123 客户端产生的cert.txt的秘钥
客户端认证服务端秘钥
- 服务端产生的cert.txt 下载到客户端 (/home/oracle/wallets/server_wallet/cert.txt)
- trust认证
orapki wallet add -wallet E:\client_wallet -trusted_cert -cert C:\Users\zace\Documents\cert.txt -pwd Zjzs_123
# E:\client_wallet客户端 wallet路径
# C:\Users\zace\Documents\cert.txt 服务端下载的cert.txt路径
# -pwd Zjzs_123 服务端产生的cert.txt 密码
服务端监听配置
文件路径: $ORACLE_HOME/network/admin/listener.ora
SSL_CLIENT_AUTHENTICATION = FALSE
WALLET_LOCATION =
(SOURCE =
(METHOD = FILE)
(METHOD_DATA = (DIRECTORY = /home/oracle/wallets/server_wallet)
# DIRECTORY = /home/oracle/wallets/server_wallet wallet路径
)
)
SSL_CIPHER_SUITES= (SSL_RSA_WITH_AES_128_CBC_SHA, SSL_DH_anon_WITH_3DES_EDE_CBC_SHA)
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.xx.xx)(PORT = 1521))
# PROTOCOL = TCP,原始的非SSL连接,继续可以使用,端口默认1521
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
(ADDRESS = (PROTOCOL = TCPS)(HOST = 192.168.xx.xx)(PORT = 2484))
# PROTOCOL = TCPS SSL连接启用TCPS,注意客户端配置的时候也要是TCPS,端口是2484
)
)
# 参考原来的路径,不用参照这个
ADR_BASE_LISTENER = /mnt/oracle
重启监听 lsnrctl stop/start
服务端sqlnet.ora配置
文件路径: $ORACLE_HOME/network/admin/sqlnet.ora
NAMES.DIRECTORY_PATH= (TNSNAMES, ONAMES, HOSTNAME)
SQLNET.EXPIRE_TIME=2
SQLNET.AUTHENTICATION_SERVICES=(TCPS,NTS)
SSL_CLIENT_AUTHENTICATION = FALSE
WALLET_LOCATION =
(SOURCE =
(METHOD = FILE)
# (METHOD_DATA = (DIRECTORY = /home/oracle/wallets/server_wallet)) wallet路径
(METHOD_DATA = (DIRECTORY = /home/oracle/wallets/server_wallet))
)
SSL_CIPHER_SUITES= (SSL_RSA_WITH_AES_128_CBC_SHA, SSL_DH_anon_WITH_3DES_EDE_CBC_SHA)
# 参考原来的路径,不用参照这个
ADR_BASE = /mnt/oracle
到此为止,服务端的配置基本完成了,客户端可以进行访问了
客户端访问配置
配置客户端的sqlnet.ora
路径 $ORACLE_CLIENT_HOME/network/admin/sqlnet.ora
SQLNET.AUTHENTICATION_SERVICES = (NTS)
SQLNET.AUTHENTICATION_SERVICES=(TCPS,NTS)
SSL_CLIENT_AUTHENTICATION = FALSE
WALLET_LOCATION =
(SOURCE =
(METHOD = FILE)
# (DIRECTORY = E:\client_wallet)) 客户端wallet路径
(METHOD_DATA = (DIRECTORY = E:\client_wallet))
)
SSL_CIPHER_SUITES= (SSL_RSA_WITH_AES_128_CBC_SHA, SSL_DH_anon_WITH_3DES_EDE_CBC_SHA)
配置tnsname.ora信息
路径 $ORACLE_CLIENT_HOME/network/admin/tnsname.ora
sslName =
(DESCRIPTION =
# PROTOCOL = TCPS PORT = 2484 这两个是和普通连接有差别的,其他是一样的
(ADDRESS = (PROTOCOL = TCPS)(HOST = 192.168.xx.xxx)(PORT = 2484))
(CONNECT_DATA = (SERVER = DEDICATED)(SERVICE_NAME = sslname))
)
客户端连接测试(navicat,plsql都可以)
注意,连接要使用网络服务名的形式,也就是客户端tnsname.ora配置的网络服务名,直连的方式,因为没有带证书信息是连接不上的
JAVA程序连接启用SSL的Oracle
JDBC直连方式
public static void getOracleSSLConnection(){
/**
* 很重要,比如要执行,注入new OraclePKIProvider(),不然无法解析秘钥文件等
*/
Security.insertProviderAt(new OraclePKIProvider(), 3);
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
} catch (ClassNotFoundException e) {
System.out.println("ERROR: Oracle JDBC Driver not found");
e.printStackTrace();
return;
}
System.out.println("Oracle JDBC Driver Registered!");
Connection connection = null;
String hostname = "192.168.xx.xxx";
String port = "2484";
String serviceName = "dbServiceName";
String userName = "username";
String passWord = "password";
/**
* 核心连接字符串
* 其中 PROTOCOL=tcps 特别注意,是启动tcps 的
*/
String oracleURL = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=tcps)(HOST="+hostname+")(PORT="+port+")))(CONNECT_DATA=(SERVICE_NAME="+serviceName+")))";
Properties props = new Properties();
props.setProperty("user", userName);
props.setProperty("password", passWord);
/**
* SSL证书信息,这个由服务端配置的时候统一生成,需要后缀是.p12的文件,密码也是在哪个时候生成的
*/
props.setProperty("javax.net.ssl.trustStore", "E:\\client_wallet\\ewallet.p12");
props.setProperty("javax.net.ssl.trustStoreType","PKCS12");
//密码,前面创建证书的时候,设置的密码
props.setProperty("javax.net.ssl.trustStorePassword","password");
try {
connection = DriverManager.getConnection(oracleURL, props);
Statement stmt=connection.createStatement(); //创建语句(Statement)
ResultSet res=stmt.executeQuery("select id,name from ssl_table where rownum<=10");
while (res.next()){
System.out.println(res.getString("id"));
System.out.println(res.getString("name"));
}
res.close();
stmt.close();
connection.close();
} catch (SQLException e) {
System.out.println("Connection Failed! Check output console");
System.out.println("Error code: " + e.getErrorCode());
System.out.println("SQL State: " + e.getSQLState());
e.printStackTrace();
}
}
Druid连接池连接方式
/**
* druid数据源方式访问SSL
*/
public static void createDruidDataSource(){
/**
* 很重要,比如要执行,注入new OraclePKIProvider(),不然无法解析秘钥文件等
*/
Security.insertProviderAt(new OraclePKIProvider(), 3);
String hostname = "192.168.xx.xx";
String port = "2484";
String serviceName = "dbServiceName";
String userName = "username";
String passWord = "password";
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
/**
* 核心连接字符串
* 其中 PROTOCOL=tcps 特别注意,是启动tcps 的
*/
druidDataSource.setUrl("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCPS)(HOST="+hostname+")(PORT="+port+")))(CONNECT_DATA=(SERVICE_NAME="+serviceName+")))");
druidDataSource.setUsername(userName);
druidDataSource.setPassword(passWord);
druidDataSource.setInitialSize(1);
druidDataSource.setMaxActive(1);
druidDataSource.setTestOnBorrow(true);
Properties props = new Properties();
/**
* SSL证书信息,这个由服务端配置的时候统一生成,需要后缀是.p12的文件,密码也是在哪个时候生成的
*/
props.setProperty("javax.net.ssl.trustStore", "E://client_wallet/ewallet.p12");
props.setProperty("javax.net.ssl.trustStoreType","PKCS12");
//密码,前面创建证书的时候,设置的密码
props.setProperty("javax.net.ssl.trustStorePassword","password");
druidDataSource.setConnectProperties(props);
/**
* SSL证书信息,这个由服务端配置的时候统一生成,需要后缀是.p12的文件,密码也是在哪个时候生成的
*/
try{
//这里单存是测试方便,正常用mybatis就行
Connection connection =druidDataSource.getConnection();
Statement stmt=connection.createStatement(); //创建语句(Statement)
ResultSet res=stmt.executeQuery("select deptno,deptname from dept where rownum<=10");
while (res.next()){
System.out.println(res.getString("deptno"));
System.out.println(res.getString("deptname"));
}
res.close();
stmt.close();
connection.close();
}
catch (Exception er){
er.printStackTrace();
}
}
和非SSL的差别说明
jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=tcps)(HOST="+hostname+")(PORT="+port+")))(CONNECT_DATA=(SERVICE_NAME="+serviceName+")))
其中 PROTOCOL=tcps
要特别注意,这里tcps标识启动ssl,如果不是SSL的时候也可以用这个连接串,就是这里要改成tcp
SSL认证需要额外的jar包
主要是这三个包
oraclekpi.jar
osdt_Cert.jar
osdt_core.jar
一般oracle服务端,都会有的,根据指定的版本去获取 路径 $ORACLE_HOME/jlib
NET程序连接启用SSL的Oracle
使用Oracle客户端配置证书和连接信息,以及网络服务名,参考前面的客户端连接步骤就行
程序调用
特别说明
- 如果使用的是System.Data.OracleClient或者 Oracle.DataAccess.dll这类需要Oracle客户端的驱动
则,连接字符串如下
String connectString2 = "Data Source=dbServiceName;User Id=xx;Password=xx";
- 如果用的是 Oracle.ManageDataAccess.dll这类免客户端的驱动,用下面的字符串
String connectString2 = "Data Source=192.168.xx.xx/dbServiceName;User Id=hr;Password=hr";
和上面的区别就是DataSource要加上IP
|