MongoDB配置SSL/TLS安全连接、java连接

10/17/2021 mongodbssltsl

# 服务器端证书配置

服务器端需两个文件: ca.pemserver.pem

# 生成ca.pem

openssl req -out ca.pem -new -x509 -days 3650 -subj "/C=CN/ST=BeiJing/O=bigdata/CN=root/CN=docker.wsl2.mongodb/emailAddress=yangxc@163.com"
# 此处会要求配置ca.pem密码,后续将会用到
1
2

参数说明

-x509: 用于生成自签证书,如果不是自签证书则不需要此项

-days: 证书的有效期限,默认是365天

# 生成server.pem

# 生成服务器端私钥
openssl genrsa -out server.key 2048
# 生成服务器端申请文件
openssl req -key server.key -new -out server.req -subj "/C=CN/ST=BeiJing/O=bigdata/CN=server1/CN=localhost/CN=docker.wsl2.mongodb/emailAddress=yangxc@163.com"
# 生成服务器端证书
openssl x509 -req -in server.req -CA ca.pem -CAkey privkey.pem -CAcreateserial -out server.crt -days 3650

# Can't load /root/.rnd into RNG
openssl rand -writerand /home/ubuntu/.rnd

# 合并服务器端私钥和服务器端证书,生成server.pem
cat server.key server.crt > server.pem
# 校验服务器端pem文件
openssl verify -CAfile ca.pem server.pem
server.pem: OK
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 服务器端配置

# 修改配置文件

mongodb的ssl/tsl配置默认是关闭的,需更改配置文件进行开启

  • ssl
vim mongod.conf

net:
#  port: 27017
#  bindIp: 127.0.0.1
  ssl:
    # 必须使用ssl连接
    mode: requireSSL
    # 必须使用绝对路径
    PEMKeyFile: /etc/server.pem
    # 必须使用绝对路径
    CAFile: /etc/ca.pem
    # 允许不可用主机名
    allowInvalidHostnames: true
    # 允许使用自签证书,如果使用自签证书必须配置该项,否则会认证失败
    allowInvalidCertificates: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • tls
vim mongod.conf

net:
#  port: 27017
#  bindIp: 127.0.0.1
  tls:
    # 必须使用ssl连接
    mode: requireTLS
    # 必须使用绝对路径
    certificateKeyFile: /etc/server.pem
    # 必须使用绝对路径
    CAFile: /etc/ca.pem
    # 允许不可用主机名
    allowInvalidHostnames: true
    # 允许使用自签证书,如果使用自签证书必须配置该项,否则会认证失败
    allowInvalidCertificates: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 启动MongoDB数据库

docker run --name mongo_test -d --restart always -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=123456 -v /home/ubuntu/mongo/mongod.conf:/etc/mongod.conf -v /home/ubuntu/mongo/etc/ca.pem:/etc/ca.pem -v /home/ubuntu/mongo/etc/server.pem:/etc/server.pem mongo --auth --config /etc/mongod.conf
1

# 客户端证书配置

# 生成client.pem

# 生成客户端私钥
openssl genrsa -out client.key 2048
# 生成客户端申请文件
openssl req -key client.key -new -out client.req -subj "/C=CN/ST=BeiJing/O=bigdata/CN=server1/CN=localhost/CN=docker.wsl2.mongodb/emailAddress=yangxc@163.com"
# 生成客户端证书
openssl x509 -req -in client.req -CA ca.pem -CAkey privkey.pem -CAserial ca.srl  -out client.crt -days 3650
# 合并客户端私钥和客户端证书,生成client.pem
cat client.key client.crt > client.pem
# 校验客户端pem文件
openssl verify -CAfile ca.pem client.pem
client.pem: OK
1
2
3
4
5
6
7
8
9
10
11

# 测试连接

ca.pemclient.pem/server.pem拷贝到客户端主机,然后用navicat测试连接

# java客户端ssl配置

两种方法,一种是直接导入到jvm ,第二种是动态管理

# 生成trustStore 和 keyStore

# keytool 方式

#根证书信息 trustStore
keytool -import -keystore cacerts -file ca.pem -storepass 123456
1
2

参数说明:

-storepass: 密钥库密码

-keystore cacerts: cacertes为密钥库文件

#客户端 keyStore
openssl pkcs12 -export -out mongodb.pkcs12 -in client.pem
1
2

# openssl 方式

#根证书信息 trustStore
openssl pkcs12 -export -out server.p12 -in server.crt -inkey server.key -name server.12
1
2
#客户端 keyStore
openssl pkcs12 -export -out keystore -in client.pem
1
2

# 第一种ssl方法

# 添加依赖

<dependency>
   <groupId>org.mongodb</groupId>
   <artifactId>mongo-java-driver</artifactId>
   <version>3.8.1</version>
</dependency>
1
2
3
4
5

# 连接代码

package com.example;

import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;

public class Mongo {
  public static void main(String[] args) {
    // 配置信任库
    System.setProperty("javax.net.ssl.trustStore", "cacerts");
    System.setProperty("javax.net.ssl.trustStorePassword", "123456");
    // 配置信任证书
    System.setProperty("javax.net.ssl.keyStore", "mongodb.pkcs12");
    System.setProperty("javax.net.ssl.keyStorePassword", "password");
    // 连接mongo数据库
    MongoClientURI uri = new MongoClientURI("mongodb://admin:123456@localhost:27017/?ssl=true&authSource=admin");
    MongoClient client = new MongoClient(uri);
    // 获取mongo数据库中的库名
    System.out.println(client.listDatabaseNames().first());
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 第二种动态加载ssl

  1. 通过System setProperty设置程序运行时属性的方式,可能会增加程序的不安全性,特别是在使用第三方库的时候。自定义KeyManager TrustManager,生成定制SSLContext实例可以解决这个问题,而且不会污染系统属性。
  2. 不需要添加服务端证书至JVM证书库,只需在程序中指定jdk支持格式的证书、client端keystore即可。

# 添加依赖

    <dependency>
      <groupId>org.mongodb</groupId>
      <artifactId>mongo-java-driver</artifactId>
      <version>3.8.1</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-mongodb</artifactId>
      <version>3.2.5</version>
    </dependency>
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpcore</artifactId>
      <version>4.4.9</version>
    </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 连接代码

package com.example;

import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.SecureRandom;

import javax.net.SocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;

public class Mongo {
  public static void main(String[] args) {
    try {

      String trustStore = "cacerts";
      String trustStorePassword = "123456";

      String keyStore = "mongodb.pkcs12";
      String keyStorePassword = "password";

      // 添加SSL认证
      SocketFactory socketFactory = createSocketFactory(trustStore, trustStorePassword, keyStore, keyStorePassword);
      // 设置ssl配置
      MongoClientOptions sslOptions = MongoClientOptions.builder().socketFactory(socketFactory).sslEnabled(true)
          .sslInvalidHostNameAllowed(true).build();

      // init mongo
      String username = "admin";
      String password = "123456";
      String dbName = "admin";
      String ip = "127.0.0.1";
      int port = 27017;
      MongoCredential credential = MongoCredential.createCredential(username, dbName, password.toCharArray());

      MongoClient client2 = new MongoClient(new ServerAddress(ip, port), credential, sslOptions);
      System.out.println(client2.listDatabaseNames().first());
      client2.close();
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

  }

  /**
   * 创建一个SocketFactory.
   *
   * @param trustStorePath keyStore 真实路径
   * @param trustStorePwd  keyStore 真实路径
   * @param keyStorePath   keyStore 路径
   * @param keyStorePwd    keyStore密码
   * @return
   * @throws Exception
   */
  private static SocketFactory createSocketFactory(String trustStorePath, String trustStorePwd, String keyStorePath,
      String keyStorePwd) throws Exception {
    SSLContext sslContext = SSLContext.getInstance("SSL");
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    FileInputStream myKeyStore = new FileInputStream(keyStorePath);
    keyStore.load(myKeyStore, keyStorePwd.toCharArray());
    myKeyStore.close();
    // default SunX509
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmf.init(keyStore, keyStorePwd.toCharArray());

    // set up a TrustManager that trusts everything
    // 1.trust specific sslCaKeystore
    KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    FileInputStream myTrustStore = new FileInputStream(trustStorePath);
    trustStore.load(myTrustStore, trustStorePwd.toCharArray());
    myTrustStore.close();
    tmf.init(trustStore);
    sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
    return sslContext.getSocketFactory();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
Last Updated: 12/26/2022, 11:54:10 AM