Search code examples
scalasslhttps

Scala https client with SSL certificate


I want to connect to a server using https protocol. I have the self-signed certificate of this website (.crt file). Now I want to connect to this website using this certificate. I am currently using the client org.apache.http.impl.nio.client, but I am ready to use another client if it proves to be useful.

How to connect to a server over https given I have the ssl certificate of this server ?


Solution

  • In order to allow only a specific certificate in your application, you have to follow the following procedure:

    1- Download certificate

    To do this, I opened firefox, paste the address of the website from which I want to get the certificate. Add exception to download this certificate. You can then access it by clicking on the green lock on the right of the address bar. The screen-shot will help you find how to download it.

    enter image description here

    Note that you should download the chain certificate, not the single certificate of the website. Here this is done in the file explorer of ubuntu, when choosing the type of file you want to export.

    2- Create a java key store

    Execute this command with the file you just downloaded :

    keytool -import -file file_you_just_downloaded.crt -alias description_of_certificate -keystore /path/toyour/java/jre/lib/security/cacerts
    #password by default is: changeit
    

    You now have a java key store with all the required certificate to connect to your website using https.

    3- Create a client with those certificate

    Those example are made with the apache nio web client.

    import java.io.FileInputStream
    import java.security.cert.X509Certificate
    import java.security.{KeyStore, SecureRandom}
    import javax.net.ssl._
    
    import org.apache.http.conn.ssl.NoopHostnameVerifier
    import org.apache.http.impl.nio.client.{CloseableHttpAsyncClient, HttpAsyncClients}
    import org.apache.commons.io.IOUtils
    import org.apache.http.ssl.SSLContexts
    
    def httpClientFactory(
      keyStoreFileName: String
    ): CloseableHttpAsyncClient = {
      val httpClientBuilder = HttpAsyncClients.custom()
    
      // activating or not the certificate checking
      if (checkCertificate) {
        // import keystore
        val keyStorePassword = jksPassword // the password you used whit the command keytool
        val ks = KeyStore.getInstance(KeyStore.getDefaultType)
        val keyStorePath = getClass.getClassLoader.getResource(keyStoreFileName)
        val inputStream = new FileInputStream(keyStorePath.getPath)
        ks.load(inputStream, keyStorePassword.toArray)
        IOUtils.closeQuietly(inputStream)
        // create trust manager from keystore
        val tmf = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
        tmf.init(ks)
        val trustManager = tmf.getTrustManagers
        // associate trust manager with the httpClient
        val sslContext = SSLContext.getInstance("TLS")
        sslContext.init(Array(), trustManager, null)
        httpClientBuilder.setSSLContext(sslContext)
      } else {
        logger.warn("Warning ! Https connections will be done without checking certificate. Do not use in production.")
        val sslContext = SSLContexts.createDefault()
        sslContext.init(null, Array(new X509TrustManager {
          override def getAcceptedIssuers: Array[X509Certificate] = Array.empty[X509Certificate]
          override def checkClientTrusted(x509Certificates: Array[X509Certificate], s: String): Unit = {}
          override def checkServerTrusted(x509Certificates: Array[X509Certificate], s: String): Unit = {}
        }), new SecureRandom())
        httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
          .setSSLContext(sslContext)
      }
    
      // ending httpClient creation
      httpClientBuilder.build()
    }
    

    4- Use the HttpClient

    Nothing change here.