Skip to content

Commit

Permalink
Merge pull request playframework#590 from scala-steward/update/ssl-co…
Browse files Browse the repository at this point in the history
…nfig-core-0.5.0

ssl-config-core 0.6.0 (was 0.4.3) + cachecontrol 2.1.0
  • Loading branch information
mkurz authored Aug 11, 2021
2 parents eceb4ae + dfe3ea0 commit c842903
Show file tree
Hide file tree
Showing 4 changed files with 5 additions and 176 deletions.
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ lazy val mimaSettings = Seq(
),
// these exclusions are only for main branch and are targeting 2.2.x
mimaBinaryIssueFilters ++= Seq(
ProblemFilters.exclude[DirectMissingMethodProblem]("play.api.libs.ws.ahc.AhcConfigBuilder.validateDefaultTrustManager"),
ProblemFilters.exclude[MissingTypesProblem]("play.api.libs.ws.ahc.AhcWSClientConfig$"),
ProblemFilters.exclude[IncompatibleResultTypeProblem]("play.api.libs.ws.ahc.AhcWSClientConfig.<init>$default$6"),
ProblemFilters.exclude[IncompatibleResultTypeProblem]("play.api.libs.ws.ahc.AhcWSClientConfig.<init>$default$8"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

package play.api.libs.ws.ahc

import java.security.KeyStore
import java.security.cert.CertPathValidatorException
import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Singleton
Expand Down Expand Up @@ -248,14 +246,6 @@ class AhcConfigBuilder(ahcConfig: AhcWSClientConfig = AhcWSClientConfig()) {
Protocols.recommendedProtocols.filter(existingProtocols.contains).toArray
}

if (!sslConfig.loose.allowWeakProtocols) {
val deprecatedProtocols = Protocols.deprecatedProtocols
for (deprecatedProtocol <- deprecatedProtocols) {
if (definedProtocols.contains(deprecatedProtocol)) {
throw new IllegalStateException(s"Weak protocol $deprecatedProtocol found in ws.ssl.protocols!")
}
}
}
definedProtocols
}

Expand All @@ -266,17 +256,9 @@ class AhcConfigBuilder(ahcConfig: AhcWSClientConfig = AhcWSClientConfig()) {
configuredCiphers.filter(existingCiphers.contains(_)).toArray

case None =>
Ciphers.recommendedCiphers.filter(existingCiphers.contains(_)).toArray
existingCiphers
}

if (!sslConfig.loose.allowWeakCiphers) {
val deprecatedCiphers = Ciphers.deprecatedCiphers
for (deprecatedCipher <- deprecatedCiphers) {
if (definedCiphers.contains(deprecatedCipher)) {
throw new IllegalStateException(s"Weak cipher $deprecatedCipher found in ws.ssl.ciphers!")
}
}
}
definedCiphers
}

Expand All @@ -288,7 +270,6 @@ class AhcConfigBuilder(ahcConfig: AhcWSClientConfig = AhcWSClientConfig()) {
// context!
val sslContext = if (sslConfig.default) {
logger.info("buildSSLContext: play.ws.ssl.default is true, using default SSLContext")
validateDefaultTrustManager(sslConfig)
SSLContext.getDefault
} else {
// break out the static methods as much as we can...
Expand Down Expand Up @@ -328,38 +309,4 @@ class AhcConfigBuilder(ahcConfig: AhcWSClientConfig = AhcWSClientConfig()) {
def buildTrustManagerFactory(ssl: SSLConfigSettings): TrustManagerFactoryWrapper = {
new DefaultTrustManagerFactoryWrapper(ssl.trustManagerConfig.algorithm)
}

def validateDefaultTrustManager(sslConfig: SSLConfigSettings): Unit = {
// If we are using a default SSL context, we can't filter out certificates with weak algorithms
// We ALSO don't have access to the trust manager from the SSLContext without doing horrible things
// with reflection.
//
// However, given that the default SSLContextImpl will call out to the TrustManagerFactory and any
// configuration with system properties will also apply with the factory, we can use the factory
// method to recreate the trust manager and validate the trust certificates that way.
//
// This is really a last ditch attempt to satisfy https://wiki.mozilla.org/CA:MD5and1024 on root certificates.
//
// http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/sun/security/ssl/SSLContextImpl.java#79

val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
tmf.init(null.asInstanceOf[KeyStore])
val trustManager: X509TrustManager = tmf.getTrustManagers()(0).asInstanceOf[X509TrustManager]

val constraints = sslConfig.disabledKeyAlgorithms
.map(a => AlgorithmConstraintsParser.parseAll(AlgorithmConstraintsParser.expression, a).get)
.toSet
val algorithmChecker = new AlgorithmChecker(loggerFactory, Set(), constraints)
for (cert <- trustManager.getAcceptedIssuers) {
try {
algorithmChecker.checkKeyAlgorithms(cert)
} catch {
case e: CertPathValidatorException =>
logger.warn(
"You are using play.ws.ssl.default=true and have a weak certificate in your default trust store! (You can modify play.ws.ssl.disabledKeyAlgorithms to remove this message.)",
e
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package play.api.libs.ws.ahc

import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import com.typesafe.sslconfig.ssl.Ciphers
import com.typesafe.sslconfig.ssl.Protocols
import com.typesafe.sslconfig.ssl.SSLConfigFactory
import com.typesafe.sslconfig.ssl.SSLConfigSettings
Expand All @@ -15,7 +14,6 @@ import org.specs2.mutable.Specification
import play.api.libs.ws.WSClientConfig
import play.shaded.ahc.org.asynchttpclient.proxy.ProxyServerSelector
import play.shaded.ahc.org.asynchttpclient.util.ProxyUtils
import uk.org.lidalia.slf4jtest.TestLoggerFactory

import scala.concurrent.duration._

Expand Down Expand Up @@ -58,7 +56,6 @@ class AhcConfigBuilderSpec extends Specification with Mockito {
actual.isFollowRedirect must_== defaultWsConfig.followRedirects
actual.getCookieStore must_== null

actual.getEnabledCipherSuites.toSeq must not contain Ciphers.deprecatedCiphers
actual.getEnabledProtocols.toSeq must not contain Protocols.deprecatedProtocols
}

Expand Down Expand Up @@ -189,48 +186,6 @@ class AhcConfigBuilderSpec extends Specification with Mockito {
asyncClientConfig.getSslEngineFactory must not(beNull)
}

"use the default with a current certificate" in {
// You can't get the value of SSLContext out from the JSSE SSL engine factory, so
// checking SSLContext.getDefault from a default = true is hard.
// Unless we can mock this, it doesn't seem like it's easy to unit test.
pending("AHC 2.0 does not provide a reference to a configured SSLContext")

//val tmc = TrustManagerConfig()
//val wsConfig = defaultWsConfig.copy(ssl = SSLConfig(default = true, trustManagerConfig = tmc))
//val config = defaultConfig.copy(wsClientConfig = wsConfig)
//val builder = new AhcConfigBuilder(config)
//
//val asyncClientConfig = builder.build()
//val sslEngineFactory = asyncClientConfig.getSslEngineFactory
}

"log a warning if sslConfig.default is passed in with an weak certificate" in {
import scala.collection.JavaConverters._
// Pass in a configuration which is guaranteed to fail, by banning RSA, DSA and EC certificates
val underlyingConfig = parseSSLConfig("""
|play.ws.ssl.default=true
|play.ws.ssl.disabledKeyAlgorithms=["RSA", "DSA", "EC"]
""".stripMargin)
val sslConfigSettings = SSLConfigFactory.parse(underlyingConfig)

val wsConfig = defaultWsConfig.copy(ssl = sslConfigSettings)
val config = defaultConfig.copy(wsClientConfig = wsConfig)

// clear the logger of any messages...
TestLoggerFactory.clear()
val builder = new AhcConfigBuilder(config)

// Run method that will trigger the logger warning
builder.configureSSL(sslConfig = sslConfigSettings)

val loggerFactory = TestLoggerFactory.getInstance()
val logger = loggerFactory.getLogger(builder.getClass)
val messages = logger.getLoggingEvents.asList().asScala.map(_.getMessage)
messages must contain(
"You are using play.ws.ssl.default=true and have a weak certificate in your default trust store! (You can modify play.ws.ssl.disabledKeyAlgorithms to remove this message.)"
)
}

"should validate certificates" in {
val sslConfig = SSLConfigSettings()
val wsConfig = defaultWsConfig.copy(ssl = sslConfig)
Expand Down Expand Up @@ -279,47 +234,6 @@ class AhcConfigBuilderSpec extends Specification with Mockito {

actual.toSeq must containTheSameElementsAs(Seq("derp", "baz", "quux"))
}

"throw exception on deprecated protocols from explicit list" in {
val deprecatedProtocol = Protocols.deprecatedProtocols.head

// the enabled protocol list has a deprecated protocol in it.
val underlyingConfig =
parseSSLConfig(s"""play.ws.ssl.enabledProtocols=["$deprecatedProtocol", "goodOne", "goodTwo"]""")
val sslConfig = SSLConfigFactory.parse(underlyingConfig)
val wsConfig = defaultWsConfig.copy(ssl = sslConfig)
val config = defaultConfig.copy(wsClientConfig = wsConfig)

val builder = new AhcConfigBuilder(config)

// The existing protocols is larger than the enabled list, and out of order.
val existingProtocols = Array("goodTwo", "badOne", "badTwo", deprecatedProtocol, "goodOne")

builder.configureProtocols(existingProtocols, sslConfig).must(throwAn[IllegalStateException])
}

"not throw exception on deprecated protocols from list if allowWeakProtocols is enabled" in {
val deprecatedProtocol = Protocols.deprecatedProtocols.head

// the enabled protocol list has a deprecated protocol in it.
val underlyingConfig = parseSSLConfig(s"""
|play.ws.ssl.enabledProtocols=["$deprecatedProtocol", "goodOne", "goodTwo"]
|play.ws.ssl.loose.allowWeakProtocols=true
""".stripMargin)
val sslConfig = SSLConfigFactory.parse(underlyingConfig)
val wsConfig = defaultWsConfig.copy(ssl = sslConfig)
val config = defaultConfig.copy(wsClientConfig = wsConfig)

val builder = new AhcConfigBuilder(config)

// The existing protocols is larger than the enabled list, and out of order.
val existingProtocols = Array("goodTwo", "badOne", "badTwo", deprecatedProtocol, "goodOne")

val actual = builder.configureProtocols(existingProtocols, sslConfig)

// We should only have the list in order, including the deprecated protocol.
actual.toSeq must containTheSameElementsAs(Seq(deprecatedProtocol, "goodOne", "goodTwo"))
}
}

"with ciphers" should {
Expand All @@ -336,39 +250,6 @@ class AhcConfigBuilderSpec extends Specification with Mockito {

actual.toSeq must containTheSameElementsAs(Seq("goodone", "goodtwo"))
}

"throw exception on deprecated ciphers from the explicit cipher list" in {
val underlyingConfig = parseSSLConfig(
s"""play.ws.ssl.enabledCipherSuites=[${Ciphers.deprecatedCiphers.head}, "goodone", "goodtwo"]"""
)
val enabledCiphers = Seq(Ciphers.deprecatedCiphers.head, "goodone", "goodtwo")
val sslConfig = SSLConfigFactory.parse(underlyingConfig)
val wsConfig = defaultWsConfig.copy(ssl = sslConfig)
val config = defaultConfig.copy(wsClientConfig = wsConfig)
val builder = new AhcConfigBuilder(config)
val existingCiphers = enabledCiphers.toArray

builder.configureCipherSuites(existingCiphers, sslConfig).must(throwAn[IllegalStateException])
}

"not throw exception on deprecated ciphers if allowWeakCiphers is enabled" in {
// User specifies list with deprecated ciphers...
val underlyingConfig = parseSSLConfig("""
|play.ws.ssl.enabledCipherSuites=[badone, "goodone", "goodtwo"]
|play.ws.ssl.loose.allowWeakCiphers=true
""".stripMargin)

val sslConfig = SSLConfigFactory.parse(underlyingConfig)
val wsConfig = defaultWsConfig.copy(ssl = sslConfig)
val config = defaultConfig.copy(wsClientConfig = wsConfig)
val builder = new AhcConfigBuilder(config)
val existingCiphers = Array("badone", "goodone", "goodtwo")

val actual = builder.configureCipherSuites(existingCiphers, sslConfig)

actual.toSeq must containTheSameElementsAs(Seq("badone", "goodone", "goodtwo"))
}

}
}
}
Expand Down
6 changes: 3 additions & 3 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,18 @@ object Dependencies {

val javaxInject = Seq("javax.inject" % "javax.inject" % "1")

val sslConfigCore = Seq("com.typesafe" %% "ssl-config-core" % "0.4.3")
val sslConfigCore = Seq("com.typesafe" %% "ssl-config-core" % "0.6.0")

val scalaXml = Seq("org.scala-lang.modules" %% "scala-xml" % "2.0.1")

val oauth = Seq("oauth.signpost" % "signpost-core" % "2.1.1")

val cachecontrol = Seq("com.typesafe.play" %% "cachecontrol" % "2.0.0")
val cachecontrol = Seq("com.typesafe.play" %% "cachecontrol" % "2.1.0")

val asyncHttpClient = Seq("org.asynchttpclient" % "async-http-client" % "2.12.3")

val akkaStreams = Seq("com.typesafe.akka" %% "akka-stream" % "2.6.15")
val akkaHttp = Seq("com.typesafe.akka" %% "akka-http" % "10.1.14")
val akkaHttp = Seq("com.typesafe.akka" %% "akka-http" % "10.2.6")

val reactiveStreams = Seq("org.reactivestreams" % "reactive-streams" % "1.0.3")

Expand Down

0 comments on commit c842903

Please sign in to comment.