@@ -9,13 +9,15 @@ import java.net.URL
99import java .security .KeyStore
1010import java .security .SecureRandom
1111import java .util .Optional
12+ import javax .net .ssl .HostnameVerifier
1213import javax .net .ssl .KeyManagerFactory
1314import javax .net .ssl .TrustManagerFactory
1415
1516import scala .collection .immutable
1617import scala .language .existentials
1718
1819import com .typesafe .config .Config
20+ import com .typesafe .config .ConfigFactory
1921import com .typesafe .sslconfig .util .EnrichedConfig
2022import com .typesafe .sslconfig .util .LoggerFactory
2123
@@ -259,34 +261,40 @@ object SSLDebugConfig {
259261 * default.
260262 * @param disableHostnameVerification Whether hostname verification should be disabled. Be aware: SSL Config itself is not using this config.
261263 * However, it was kept because 3rd party libraries rely on its existence.
264+ * @param disableSNI Whether SNI should be disabled (up to client library to respect this setting or not). Be aware: SSL Config itself is not using this config.
265+ * However, it was kept because 3rd party libraries rely on its existence.
262266 * @param acceptAnyCertificate Whether any X.509 certificate should be accepted or not.
263267 */
264268final class SSLLooseConfig private [sslconfig] (
265269 val acceptAnyCertificate : Boolean = false ,
266270 val allowLegacyHelloMessages : Option [Boolean ] = None ,
267271 val allowUnsafeRenegotiation : Option [Boolean ] = None ,
268272 val disableHostnameVerification : Boolean = false ,
273+ val disableSNI : Boolean = false ,
269274) {
270275
271276 def withAcceptAnyCertificate (value : Boolean ): SSLLooseConfig = copy(acceptAnyCertificate = value)
272277 def withAllowLegacyHelloMessages (value : Option [Boolean ]): SSLLooseConfig = copy(allowLegacyHelloMessages = value)
273278 def withAllowUnsafeRenegotiation (value : Option [Boolean ]): SSLLooseConfig = copy(allowUnsafeRenegotiation = value)
274279 def withDisableHostnameVerification (value : Boolean ): SSLLooseConfig = copy(disableHostnameVerification = value)
280+ def withDisableSNI (value : Boolean ): SSLLooseConfig = copy(disableSNI = value)
275281
276282 private def copy (
277283 acceptAnyCertificate : Boolean = acceptAnyCertificate,
278284 allowLegacyHelloMessages : Option [Boolean ] = allowLegacyHelloMessages,
279285 allowUnsafeRenegotiation : Option [Boolean ] = allowUnsafeRenegotiation,
280286 disableHostnameVerification : Boolean = disableHostnameVerification,
287+ disableSNI : Boolean = disableSNI,
281288 ): SSLLooseConfig = new SSLLooseConfig (
282289 acceptAnyCertificate = acceptAnyCertificate,
283290 allowLegacyHelloMessages = allowLegacyHelloMessages,
284291 allowUnsafeRenegotiation = allowUnsafeRenegotiation,
285292 disableHostnameVerification = disableHostnameVerification,
293+ disableSNI = disableSNI,
286294 )
287295
288296 override def toString =
289- s """ SSLLooseConfig( ${acceptAnyCertificate}, ${allowLegacyHelloMessages}, ${allowUnsafeRenegotiation}, ${disableHostnameVerification}) """
297+ s """ SSLLooseConfig( ${acceptAnyCertificate}, ${allowLegacyHelloMessages}, ${allowUnsafeRenegotiation}, ${disableHostnameVerification}, ${disableSNI} ) """
290298}
291299object SSLLooseConfig {
292300 def apply () = new SSLLooseConfig ()
@@ -295,6 +303,34 @@ object SSLLooseConfig {
295303 def getInstance () = apply()
296304}
297305
306+ /**
307+ * Carries values which will be later set on an [[javax.net.ssl.SSLParameters ]] object.
308+ *
309+ * @param clientAuth see [[ClientAuth ]] for detailed docs on ClientAuth modes
310+ */
311+ final class SSLParametersConfig private [sslconfig] (
312+ val clientAuth : ClientAuth = ClientAuth .Default ,
313+ val protocols : scala.collection.immutable.Seq [String ] = Nil
314+ ) {
315+
316+ def withClientAuth (value : com.typesafe.sslconfig.ssl.ClientAuth ): SSLParametersConfig = copy(clientAuth = value)
317+ def withProtocols (value : scala.collection.immutable.Seq [String ]): SSLParametersConfig = copy(protocols = value)
318+
319+ private def copy (
320+ clientAuth : com.typesafe.sslconfig.ssl.ClientAuth = clientAuth,
321+ protocols : scala.collection.immutable.Seq [String ] = protocols
322+ ): SSLParametersConfig = new SSLParametersConfig (clientAuth = clientAuth, protocols = protocols)
323+
324+ override def toString =
325+ s """ SSLParametersConfig( ${clientAuth}, ${protocols}) """
326+ }
327+ object SSLParametersConfig {
328+ def apply () = new SSLParametersConfig ()
329+
330+ /** Java API */
331+ def getInstance () = apply()
332+ }
333+
298334/**
299335 * The SSL configuration.
300336 *
@@ -304,8 +340,12 @@ object SSLLooseConfig {
304340 * @param revocationLists The revocation lists to check.
305341 * @param enabledCipherSuites If defined, override the platform default cipher suites.
306342 * @param enabledProtocols If defined, override the platform default protocols.
343+ * @param sslParametersConfig Be aware: SSL Config itself is not using this config.
344+ * However, it was kept because 3rd party libraries rely on its existence.
307345 * @param keyManagerConfig The key manager configuration.
308346 * @param trustManagerConfig The trust manager configuration.
347+ * @param hostnameVerifierClass The hostname verifier class. Be aware: SSL Config itself is not using this config.
348+ * However, it was kept because 3rd party libraries rely on its existence.
309349 * @param secureRandom The SecureRandom instance to use. Let the platform choose if None.
310350 * @param debug The debug config.
311351 * @param loose Loose configuratino parameters
@@ -317,8 +357,10 @@ final class SSLConfigSettings private[sslconfig] (
317357 val revocationLists : Option [immutable.Seq [URL ]] = None ,
318358 val enabledCipherSuites : Option [immutable.Seq [String ]] = None ,
319359 val enabledProtocols : Option [immutable.Seq [String ]] = Some (List (" TLSv1.3" , " TLSv1.2" )),
360+ val sslParametersConfig : SSLParametersConfig = SSLParametersConfig (),
320361 val keyManagerConfig : KeyManagerConfig = KeyManagerConfig (),
321362 val trustManagerConfig : TrustManagerConfig = TrustManagerConfig (),
363+ val hostnameVerifierClass : Class [? <: HostnameVerifier ] = classOf [NoopHostnameVerifier ],
322364 val secureRandom : Option [SecureRandom ] = None ,
323365 val debug : SSLDebugConfig = SSLDebugConfig (),
324366 val loose : SSLLooseConfig = SSLLooseConfig ()
@@ -331,13 +373,17 @@ final class SSLConfigSettings private[sslconfig] (
331373 copy(enabledCipherSuites = value)
332374 def withEnabledProtocols (value : Option [scala.collection.immutable.Seq [String ]]): SSLConfigSettings =
333375 copy(enabledProtocols = value)
376+ def withHostnameVerifierClass (value : Class [? <: javax.net.ssl.HostnameVerifier ]): SSLConfigSettings =
377+ copy(hostnameVerifierClass = value)
334378 def withKeyManagerConfig (value : com.typesafe.sslconfig.ssl.KeyManagerConfig ): SSLConfigSettings =
335379 copy(keyManagerConfig = value)
336380 def withLoose (value : com.typesafe.sslconfig.ssl.SSLLooseConfig ): SSLConfigSettings = copy(loose = value)
337381 def withProtocol (value : String ): SSLConfigSettings = copy(protocol = value)
338382 def withRevocationLists (value : Option [scala.collection.immutable.Seq [java.net.URL ]]): SSLConfigSettings =
339383 copy(revocationLists = value)
340- def withSecureRandom (value : Option [java.security.SecureRandom ]): SSLConfigSettings = copy(secureRandom = value)
384+ def withSecureRandom (value : Option [java.security.SecureRandom ]): SSLConfigSettings = copy(secureRandom = value)
385+ def withSslParametersConfig (value : com.typesafe.sslconfig.ssl.SSLParametersConfig ): SSLConfigSettings =
386+ copy(sslParametersConfig = value)
341387 def withTrustManagerConfig (value : com.typesafe.sslconfig.ssl.TrustManagerConfig ): SSLConfigSettings =
342388 copy(trustManagerConfig = value)
343389
@@ -347,28 +393,32 @@ final class SSLConfigSettings private[sslconfig] (
347393 default : Boolean = default,
348394 enabledCipherSuites : Option [scala.collection.immutable.Seq [String ]] = enabledCipherSuites,
349395 enabledProtocols : Option [scala.collection.immutable.Seq [String ]] = enabledProtocols,
396+ hostnameVerifierClass : Class [? <: javax.net.ssl.HostnameVerifier ] = hostnameVerifierClass,
350397 keyManagerConfig : com.typesafe.sslconfig.ssl.KeyManagerConfig = keyManagerConfig,
351398 loose : com.typesafe.sslconfig.ssl.SSLLooseConfig = loose,
352399 protocol : String = protocol,
353400 revocationLists : Option [scala.collection.immutable.Seq [java.net.URL ]] = revocationLists,
354401 secureRandom : Option [java.security.SecureRandom ] = secureRandom,
402+ sslParametersConfig : com.typesafe.sslconfig.ssl.SSLParametersConfig = sslParametersConfig,
355403 trustManagerConfig : com.typesafe.sslconfig.ssl.TrustManagerConfig = trustManagerConfig
356404 ): SSLConfigSettings = new SSLConfigSettings (
357405 checkRevocation = checkRevocation,
358406 debug = debug,
359407 default = default,
360408 enabledCipherSuites = enabledCipherSuites,
361409 enabledProtocols = enabledProtocols,
410+ hostnameVerifierClass = hostnameVerifierClass,
362411 keyManagerConfig = keyManagerConfig,
363412 loose = loose,
364413 protocol = protocol,
365414 revocationLists = revocationLists,
366415 secureRandom = secureRandom,
416+ sslParametersConfig = sslParametersConfig,
367417 trustManagerConfig = trustManagerConfig
368418 )
369419
370420 override def toString =
371- s """ SSLConfig( ${checkRevocation}, ${debug}, ${default}, ${enabledCipherSuites}, ${enabledProtocols}, ${keyManagerConfig}, ${loose}, ${protocol}, ${revocationLists}, ${secureRandom}, ${trustManagerConfig}) """
421+ s """ SSLConfig( ${checkRevocation}, ${debug}, ${default}, ${enabledCipherSuites}, ${enabledProtocols}, ${hostnameVerifierClass} , ${ keyManagerConfig}, ${loose}, ${protocol}, ${revocationLists}, ${secureRandom} , ${sslParametersConfig }, ${trustManagerConfig}) """
372422}
373423object SSLConfigSettings {
374424 def apply () = new SSLConfigSettings ()
@@ -419,10 +469,19 @@ class SSLConfigParser(c: EnrichedConfig, classLoader: ClassLoader, loggerFactory
419469 val ciphers = Some (c.getSeq[String ](" enabledCipherSuites" )).filter(_.nonEmpty)
420470 val protocols = Some (c.getSeq[String ](" enabledProtocols" )).filter(_.nonEmpty)
421471
472+ val hostnameVerifierClass = c.getOptional[String ](" hostnameVerifierClass" ) match {
473+ case None => classOf [NoopHostnameVerifier ]
474+ case Some (fqcn) => classLoader.loadClass(fqcn).asSubclass(classOf [HostnameVerifier ])
475+ }
476+
422477 val keyManagers = parseKeyManager(c.get[EnrichedConfig ](" keyManager" ))
423478
424479 val trustManagers = parseTrustManager(c.get[EnrichedConfig ](" trustManager" ))
425480
481+ val sslParametersConfig = parseSSLParameters(
482+ c.getOptional[EnrichedConfig ](" sslParameters" ).getOrElse(new EnrichedConfig (ConfigFactory .empty()))
483+ )
484+
426485 new SSLConfigSettings (
427486 default = default,
428487 protocol = protocol,
@@ -431,6 +490,8 @@ class SSLConfigParser(c: EnrichedConfig, classLoader: ClassLoader, loggerFactory
431490 enabledCipherSuites = ciphers,
432491 enabledProtocols = protocols,
433492 keyManagerConfig = keyManagers,
493+ hostnameVerifierClass = hostnameVerifierClass,
494+ sslParametersConfig = sslParametersConfig,
434495 trustManagerConfig = trustManagers,
435496 secureRandom = None ,
436497 debug = debug,
@@ -446,12 +507,14 @@ class SSLConfigParser(c: EnrichedConfig, classLoader: ClassLoader, loggerFactory
446507 val allowMessages = config.getOptional[Boolean ](" allowLegacyHelloMessages" )
447508 val allowUnsafeRenegotiation = config.getOptional[Boolean ](" allowUnsafeRenegotiation" )
448509 val disableHostnameVerification = config.getOptional[Boolean ](" disableHostnameVerification" ).getOrElse(false )
510+ val disableSNI = config.getOptional[Boolean ](" disableSNI" ).getOrElse(false )
449511 val acceptAnyCertificate = config.get[Boolean ](" acceptAnyCertificate" )
450512
451513 new SSLLooseConfig (
452514 allowLegacyHelloMessages = allowMessages,
453515 allowUnsafeRenegotiation = allowUnsafeRenegotiation,
454516 disableHostnameVerification = disableHostnameVerification,
517+ disableSNI = disableSNI,
455518 acceptAnyCertificate = acceptAnyCertificate
456519 )
457520 }
@@ -555,4 +618,45 @@ class SSLConfigParser(c: EnrichedConfig, classLoader: ClassLoader, loggerFactory
555618
556619 new TrustManagerConfig (algorithm, trustStoreInfos)
557620 }
621+
622+ def parseSSLParameters (config : EnrichedConfig ): SSLParametersConfig = {
623+ // could instantiate SSLParameters directly, but seems less clean, here we only parse config
624+
625+ val clientAuth = config.getOptional[String ](" clientAuth" ) match {
626+ case Some (" none" ) => ClientAuth .None
627+ case Some (" want" ) => ClientAuth .Want
628+ case Some (" need" ) => ClientAuth .Need
629+ case None | Some (_) => ClientAuth .Default
630+ }
631+
632+ val protocols = if (config.underlying.hasPath(" protocols" )) {
633+ config.getSeq[String ](" protocols" )
634+ } else {
635+ immutable.Seq .empty[String ]
636+ }
637+
638+ new SSLParametersConfig (clientAuth, protocols)
639+ }
640+ }
641+
642+ /**
643+ * An SSLEngine can either demand, allow or ignore its peer’s authentication
644+ * (via certificates), where `Need` will fail the handshake if the peer does
645+ * not provide valid credentials, `Want` allows the peer to send credentials
646+ * and verifies them if provided, and `None` disables peer certificate
647+ * verification.
648+ *
649+ * See the documentation for `SSLEngine::setWantClientAuth` for more information.
650+ */
651+ sealed abstract class ClientAuth
652+ object ClientAuth {
653+ case object Default extends ClientAuth
654+ case object None extends ClientAuth
655+ case object Want extends ClientAuth
656+ case object Need extends ClientAuth
657+
658+ def none : ClientAuth = None
659+ def want : ClientAuth = Want
660+ def need : ClientAuth = Need
661+ def defaultAuth : ClientAuth = Default // since `default` is a Java keyword
558662}
0 commit comments