Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ssl-config-core 0.6.0 (was 0.4.3) + cachecontrol 2.1.0 #590

Merged
merged 7 commits into from
Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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