Scala:如何建立与PostgreSQL服务器的安全连接?

时间:2013-12-29 14:52:17

标签: postgresql scala ssl

我想以编程方式安全地连接到我的PostgreSQL服务器 Scala使用基于JDBC的库。此外,我想存储所有的 由openssl生成的格式的SSL证书和密钥文件 (即。本机Java生成的KeyStore格式 库)。

我该怎么做?

我正在使用:

  • PostgreSQL 9.3
  • JVM 1.6
  • Scala 2.10.3

1 个答案:

答案 0 :(得分:2)

延长 org.postgresql.ssl.WrappedFactory 属于的一部分 PostgreSQL JDBC Driver,然后通过 扩展类的名称作为setSslfactory()方法的参数 BaseDataSource的适当子类。

实施例

假设您使用自签名证书作为PostgreSQL 服务器的证书( N.B。,服务器的证书在文件中 其名称指定为配置参数的值 ssl_cert_file)和

假设您将使用PostgreSQL login role建立 连接名为database_user

使用PostgreSQL服务器的证书签署客户端证书 具有通用名称(CN)属性database_user。 然后,使用服务器的证书,客户端的证书和 客户端计算机上的openssl PEM格式的客户端私钥, 以下内容将实现安全的双边身份验证连接 到PostgreSQL服务器:

package mypackage

import org.postgresql.ssl.WrappedFactory

import org.bouncycastle.util.io.pem.PemReader

import java.io.{FileInputStream,FileReader}
import javax.net.ssl.{SSLContext,KeyManagerFactory,TrustManagerFactory}
import java.security.{KeyStore,KeyFactory,SecureRandom}
import java.security.cert.CertificateFactory
import java.security.spec.PKCS8EncodedKeySpec

object SocketFactory {
  final val PRIVATE_KEY_FILENAME = "private.key"
  final val CLIENT_CERT_FILENAME = "client.crt"
  final val SERVER_CERT_FILENAME = "/home/me/.ssl/postgres_server.crt"

  def certFromFile(fileName: String) =
    CertificateFactory getInstance "X.509" generateCertificate {
      new FileInputStream(fileName)
    }
}

class SocketFactory extends WrappedFactory {
  import SocketFactory._

  val sslContext = SSLContext getInstance "TLS"

  sslContext init (
    { // key managers: Array[KeyManager] 
      val kmf = KeyManagerFactory getInstance "SunX509"
      kmf init ({
        val keyStore = KeyStore getInstance KeyStore.getDefaultType
        keyStore load (null, null)
        keyStore setKeyEntry ( "",
          KeyFactory getInstance "RSA" generatePrivate (
            new PKCS8EncodedKeySpec (
              new PemReader (
                new FileReader(PRIVATE_KEY_FILENAME)
              ).readPemObject.getContent
            )
          ),
          Array[Char](), // no password for this entry
          Array(certFromFile(CLIENT_CERT_FILENAME))
        )
        keyStore
      }, Array[Char]())
      kmf.getKeyManagers
    },
    { // trust managers: Array[TrustManager]
      val tmf = TrustManagerFactory getInstance "PKIX"
      tmf init {
        val keyStore = KeyStore getInstance KeyStore.getDefaultType
        keyStore load (null, null)
        keyStore setCertificateEntry ("", certFromFile(SERVER_CERT_FILENAME))
        keyStore
      }            
      tmf.getTrustManagers
    },
    SecureRandom getInstance "SHA1PRNG"
  )

  _factory = sslContext.getSocketFactory

}

使用上述内容,您可以获得Java DataSource 像这样的东西:

def dataSource = {
  val ds = new PGSimpleDataSource
  ds.setDatabaseName("my_database")
  ds.setServerName("myHost.com")
  ds.setUser("database_user")
  ds.setSsl(true)
  ds.setSslfactory("mypackage.SocketFactory")
  ds
}

说明:

  1. 此代码不会捕获任何异常,也不会关闭Streams / Readers。根据需要进行修改。
  2. 此代码不会针对服务器验证客户端证书 证书,也不是客户证书的私钥 (超出连接尝试)。提防 相关错误,并根据需要进行修改。
  3. 基础Java方法通常接受null有意义 参数。如果你发现了奇怪的行为,请确保你不是 意外地在某处传递了null方法参数。
  4. 上述内容使用RSA密钥对。更新版本的 openssl可以使用椭圆曲线加密。
  5. 实现目标 forward secrecy, 考虑在PostgreSQL中指定适当的密码套件 配置,例如

    ssl_ciphers = 'DHE-RSA-AES256-SHA:DHE-RSA-CAMELLIA256-SHA'

  6. 其他档案

    前面的示例使用了以下库依赖项 build.sbt档案:

    libraryDependencies ++= Seq(
      "org.bouncycastle"   % "bcpkix-jdk15on" % "1.50",
      "org.postgresql"     % "postgresql"   % "9.3-1100-jdbc4"
    )
    

    pg_hba.conf 文件将需要一个适当的条目,如:

    hostssl my_database database_user <client.ip.number>/32 trust clientcert=1