__BlueCat 2019-09-23 10:09 采纳率: 0%
浏览 570

pgjdbc数据库的ssl证书验证源码有几点不懂的地方

postgresql 数据库用证书验证,看了一下源码,但是ssl证书验证有几点不懂的地方.
下面是pgjdbc获取完数据库的连接后, 开始启用ssl证书验证的流程.
我的问题: 过程我都看明白了, 但是不知道为什么它要这么做, 还是说这是个标准, 有人可以帮我讲解一下,或者给点资料参考也行, 谢谢
为了保持代码的简洁性,代码有删减.

 private PGStream enableSSL(PGStream pgStream, SslMode sslMode, Properties info,
      int connectTimeout)
      throws IOException, PSQLException {
    // 发送SSL请求包
    pgStream.sendInteger4(8);//发送一个4字节的整数到后端。
    pgStream.sendInteger2(1234);//发送一个2字节整数(短)到后端。
    pgStream.sendInteger2(5679);
    pgStream.flush();//将任何挂起的输出刷新到后端。
    // Now get the response from the backend, one of N, E, S. 现在从后端(N, E, S)中获取响应。
    int beresp = pgStream.receiveChar();//从后端接收单个字符。
    switch (beresp) {
      case 'S':
        //  ssl 服务器支持ssl
   **org.herodbsql.ssl.MakeSSL.convert(pgStream, info);**
        return pgStream;
    }
  }

上面可以看到,jdbc给数据库发了几个字节的整数, 然后获取响应,走不同的switch,但是不知道为什么这么做,接着进去看,convert方法

public static void convert(PGStream stream, Properties info)
      throws PSQLException, IOException {
    LOGGER.log(Level.FINE, "converting regular socket connection to ssl");

  **SSLSocketFactory factory = SocketFactoryFactory.getSslSocketFactory(info);**
    SSLSocket newConnection;
        // 将常规套接字连接转换为ssl
      newConnection = (SSLSocket) factory.createSocket(stream.getSocket(), stream.getHostSpec().getHost(), stream.getHostSpec().getPort(), true);
      // 我们必须手动调用,否则将隐藏异常
      newConnection.setUseClientMode(true);// 设置使用客户端模式
      newConnection.startHandshake();// 开始握手
      stream.changeSocket(newConnection);
  }

在看SocketFactoryFactory.getSslSocketFactory(info);

public static SSLSocketFactory getSslSocketFactory(Properties info) throws PSQLException {//获取Ssl套接字工厂
    String classname = PGProperty.SSL_FACTORY.get(info);// 获取要使用的SSL工厂的类名
    if (classname == null
        || "org.herodbsql.ssl.jdbc4.LibPQFactory".equals(classname)
        || "org.herodbsql.ssl.LibPQFactory".equals(classname)) {
      return new LibPQFactory(info);
    }
  }

接着看 new LibPQFactory(info);
这个方法应该就是加载证书的地方

 public LibPQFactory(Properties info) throws PSQLException {
    try {
      SSLContext ctx = SSLContext.getInstance("TLS"); // or "SSL" ?

      // 确定默认文件位置
      String pathsep = System.getProperty("file.separator");// 获取windows或者linux的文件夹分隔符
      String defaultdir;
      boolean defaultfile = false;
      if (System.getProperty("os.name").toLowerCase().contains("windows")) { // It is Windows
        defaultdir = System.getenv("APPDATA") + pathsep + "herodbsql" + pathsep; // 获取指定环境变量的值。环境变量是一个依赖于系统的外部命名值。
      } else {
        defaultdir = System.getProperty("user.home") + pathsep + ".herodbsql" + pathsep;
      }

      // Load the client's certificate and key 加载客户机的证书和密钥
      String sslcertfile = PGProperty.SSL_CERT.get(info);
      if (sslcertfile == null) { // Fall back to default 退回到默认状态
        defaultfile = true;
        sslcertfile = defaultdir + "herodbsql.crt";
      }
      String sslkeyfile = PGProperty.SSL_KEY.get(info);
      if (sslkeyfile == null) { // Fall back to default
        defaultfile = true;
        sslkeyfile = defaultdir + "herodbsql.pk8";
      }

      // Determine the callback handler 确定回调处理程序
      CallbackHandler cbh;
      String sslpasswordcallback = PGProperty.SSL_PASSWORD_CALLBACK.get(info);
      if (sslpasswordcallback != null) {
        try {
          cbh = (CallbackHandler) ObjectFactory.instantiate(sslpasswordcallback, info, false, null);
        } catch (Exception e) {
          throw new PSQLException(
              GT.tr("The password callback class provided {0} could not be instantiated.",
                  sslpasswordcallback),
              PSQLState.CONNECTION_FAILURE, e);
        }
      } else {
        cbh = new ConsoleCallbackHandler(PGProperty.SSL_PASSWORD.get(info));// 获取 sslkey的加密密码
      }

      // If the properties are empty, give null to prevent client key selection 如果属性为空,则为null,以防止选择客户机密钥
      km = new LazyKeyManager(("".equals(sslcertfile) ? null : sslcertfile),
          ("".equals(sslkeyfile) ? null : sslkeyfile), cbh, defaultfile);//创建lazykeymanagerduixinag

      TrustManager[] tm;
      SslMode sslMode = SslMode.of(info);
      if (!sslMode.verifyCertificate()) {
        // server validation is not required 不需要服务器验证
        tm = new TrustManager[]{new NonValidatingTM()};
      } else {
        // Load the server certificate 加载服务器证书

        TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");// 创建证书信任管理器工厂
        KeyStore ks;
        try {
          ks = KeyStore.getInstance("jks");// 读取秘钥是所需要用到的工具类
        } catch (KeyStoreException e) {
          // this should never happen
          throw new NoSuchAlgorithmException("jks KeyStore not available");
        }
        String sslrootcertfile = PGProperty.SSL_ROOT_CERT.get(info);//获取根证书
        if (sslrootcertfile == null) { // Fall back to default
          sslrootcertfile = defaultdir + "root.crt";
        }
        FileInputStream fis;
        try {
          fis = new FileInputStream(sslrootcertfile); // 获取根证书ca的文件流
        } catch (FileNotFoundException ex) {
          throw new PSQLException(
              GT.tr("Could not open SSL root certificate file {0}.", sslrootcertfile),
              PSQLState.CONNECTION_FAILURE, ex);
        }
        try {
          CertificateFactory cf = CertificateFactory.getInstance("X.509");// // 获取X.509工厂实例
          // Certificate[] certs = cf.generateCertificates(fis).toArray(new Certificate[]{}); //Does
          // not work in java 1.4
          Object[] certs = cf.generateCertificates(fis).toArray(new Certificate[]{});//生成ca证书的数组视图
          ks.load(null, null);
          for (int i = 0; i < certs.length; i++) {
            ks.setCertificateEntry("cert" + i, (Certificate) certs[i]);//设置证书条目 将给定的可信证书分配给给定的别名。
          }
          tmf.init(ks);
        } catch (IOException ioex) {
          throw new PSQLException(
              GT.tr("Could not read SSL root certificate file {0}.", sslrootcertfile),
              PSQLState.CONNECTION_FAILURE, ioex);
        } catch (GeneralSecurityException gsex) {
          throw new PSQLException(
              GT.tr("Loading the SSL root certificate {0} into a TrustManager failed.",
                      sslrootcertfile),
              PSQLState.CONNECTION_FAILURE, gsex);
        } finally {
          try {
            fis.close();
          } catch (IOException e) {
            /* ignore */
          }
        }
        tm = tmf.getTrustManagers();
      }

      // finally we can initialize the context 最后,我们可以初始化上下文
      try {
        ctx.init(new KeyManager[]{km}, tm, null);
      } catch (KeyManagementException ex) {
        throw new PSQLException(GT.tr("Could not initialize SSL context."),
            PSQLState.CONNECTION_FAILURE, ex);
      }

      factory = ctx.getSocketFactory();
    } catch (NoSuchAlgorithmException ex) {
      throw new PSQLException(GT.tr("Could not find a java cryptographic algorithm: {0}.",
              ex.getMessage()), PSQLState.CONNECTION_FAILURE, ex);
    }
  }
  • 写回答

1条回答 默认 最新

  • 关注
    评论

报告相同问题?

悬赏问题

  • ¥15 如何让企业微信机器人实现消息汇总整合
  • ¥50 关于#ui#的问题:做yolov8的ui界面出现的问题
  • ¥15 如何用Python爬取各高校教师公开的教育和工作经历
  • ¥15 TLE9879QXA40 电机驱动
  • ¥20 对于工程问题的非线性数学模型进行线性化
  • ¥15 Mirare PLUS 进行密钥认证?(详解)
  • ¥15 物体双站RCS和其组成阵列后的双站RCS关系验证
  • ¥20 想用ollama做一个自己的AI数据库
  • ¥15 关于qualoth编辑及缝合服装领子的问题解决方案探寻
  • ¥15 请问怎么才能复现这样的图呀