Skip to content

Instantly share code, notes, and snippets.

@abevoelker
Forked from skipoleschris/SSLCommunications.scala
Last active June 20, 2016 15:39
Show Gist options
  • Save abevoelker/a6c87246fe0835fbb24f to your computer and use it in GitHub Desktop.
Save abevoelker/a6c87246fe0835fbb24f to your computer and use it in GitHub Desktop.

Revisions

  1. Chris Turner created this gist Feb 16, 2012.
    77 changes: 77 additions & 0 deletions SSLCommunications.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,77 @@
    import java.security._
    import java.net.URL
    import javax.net.ssl.{KeyManagerFactory, TrustManagerFactory}
    import org.apache.http.conn.ssl.SSLSocketFactory
    import org.apache.http.conn.scheme.Scheme
    import dispatch._

    // Gist trait for doing SSL with mutual certificate exchange using dispatch.
    // This works without having to set up global JDK-wide keystore and truststore files.
    //
    // Assumes the following:
    // You have the server certificate from the site you are calling and you have created a JKS
    // format keystore containing this file, which will act as your truststore:
    // > keytool -import -alias foo -file server.crt -keystore my.truststore
    //
    // You have been issued with a client certificate and private key and these have been issued
    // as a pkcs12 format keystore.
    //
    trait SSLCommunications {

    // Implement these methods to define the keystore and truststore locations and the passwords for each
    protected val keystoreLocation: String
    protected val keystorePassword: Array[Char]
    protected val truststoreLocation: String
    protected val truststorePassword: Array[Char]

    // Usage:
    // val targetUrl = new URL("https://www.example.com/someResource")
    // val req = url(targetUrl.toString)
    // Https(targetUrl)(req as_str))
    //
    def Https(targetUrl: URL) = new Http {
    schemeFor(targetUrl, sslSocketFactory) foreach { scheme =>
    client.getConnectionManager.getSchemeRegistry.register(scheme)
    }
    }

    private val SSLPort = 443

    private def schemeFor(url: URL, sslSocketFactory: SSLSocketFactory) = {
    val port = if ( url.getPort == -1 ) SSLPort else url.getPort

    if ( url.getProtocol == "https" ) Some(new Scheme("https", port, sslSocketFactory)) else None
    }

    private lazy val sslSocketFactory = createSSLSocketFactory

    private def createSSLSocketFactory = {
    val sslContext = javax.net.ssl.SSLContext.getInstance("TLS")
    sslContext.init(keyManagers, trustManagers, new SecureRandom())
    new SSLSocketFactory(sslContext)
    }

    private def keyManagers = {
    val factory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
    factory.init(populateKeystore("PKCS12", keystoreLocation, keystorePassword), keystorePassword)
    factory.getKeyManagers
    }

    private def trustManagers = {
    val factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
    factory.init(populateKeystore("JKS", truststoreLocation, truststorePassword))
    factory.getTrustManagers
    }

    private def populateKeystore(keystoreType: String, location: String, password: Array[Char]) = {
    val keyStore = KeyStore.getInstance(keystoreType)
    val is = getClass.getResourceAsStream(location)
    if ( is == null ) throw new IllegalArgumentException("Unable to load the keystore at the given location: " + location)
    try {
    keyStore.load(is, password)
    } finally {
    is.close()
    }
    keyStore
    }
    }