9

With such code:

val html = Source.fromURL("https://scans.io/json")

Getting exception:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1886)
...

I can find how to fix in Java but have no idea - how to fix it in Scala?

littleAlien
  • 721
  • 2
  • 8
  • 20

1 Answers1

11

You can achieve this by configuring a SSLContext.

Here is a working code

import javax.net.ssl._
import java.security.cert.X509Certificate
import scala.io.Source

// Bypasses both client and server validation.
object TrustAll extends X509TrustManager {
  val getAcceptedIssuers = null

  override def checkClientTrusted(x509Certificates: Array[X509Certificate], s: String) = {}

  override def checkServerTrusted(x509Certificates: Array[X509Certificate], s: String) = {}
}

// Verifies all host names by simply returning true.
object VerifiesAllHostNames extends HostnameVerifier {
  def verify(s: String, sslSession: SSLSession) = true
}

// Main class
object Test extends App {
  // SSL Context initialization and configuration
  val sslContext = SSLContext.getInstance("SSL")
  sslContext.init(null, Array(TrustAll), new java.security.SecureRandom())
  HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory)
  HttpsURLConnection.setDefaultHostnameVerifier(VerifiesAllHostNames)
  
  // Actual call
  val html = Source.fromURL("https://scans.io/json")
  println(html.mkString)
}

How it works

Source.fromURL uses java.net.HttpURLConnection behind the scene. So this code simply works because TrustAll bypasses checkClientTrusted and checkServerTrusted methods.

AmirHd
  • 10,308
  • 11
  • 41
  • 60
Nader Ghanbari
  • 4,120
  • 1
  • 23
  • 26
  • 3
    By the way I would never do this, especially in production, unless you have a real reason behind this (temporary, just for testing, etc.) – Nader Ghanbari Feb 28 '15 at 22:51
  • Thanks, it works! I see no other way to solve this. Except like add that certificate to trust store, but I prefer to just ignore that error. This is not secure information. – littleAlien Mar 01 '15 at 00:00
  • Bypassing is not recommended. Instead try to add the certificate to your trust-store as I mentioned below. – masoodg Jan 29 '16 at 16:39
  • Note that creating the TrustAll object this way is not possible in scala 2.10 due to a bug (which has been fixed in 2.11): https://issues.scala-lang.org/browse/SI-8011 In 2.10, this seems to work: `class TrustAll extends X509TrustManager { override def getAcceptedIssuers: Array[X509Certificate] = {null} override def checkClientTrusted(x509Certificates: Array[X509Certificate], s: String): Unit = {} override def checkServerTrusted(x509Certificates: Array[X509Certificate], s: String): Unit = {} }` – Alexander Tronchin-James Aug 11 '16 at 21:28
  • getting a ```java.lang.InternalError: Malformed class name at java.lang.Class.getSimpleName(Class.java:1190) at java.lang.Class.getCanonicalName(Class.java:1233) at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:540) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1300) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254) ...``` – marios Aug 21 '16 at 20:52