package play.api.libs.ws.ssl
import javax.net.ssl.X509TrustManager
import java.security.cert._
import scala.collection.mutable.ArrayBuffer
import scala.util.control.NonFatal
import java.security.GeneralSecurityException
class CompositeX509TrustManager(trustManagers: Seq[X509TrustManager], algorithmChecker: AlgorithmChecker) extends X509TrustManager {
private val logger = org.slf4j.LoggerFactory.getLogger(getClass)
def getAcceptedIssuers: Array[X509Certificate] = {
logger.debug("getAcceptedIssuers: ")
val certificates = ArrayBuffer[X509Certificate]()
val exceptionList = withTrustManagers {
trustManager =>
certificates.appendAll(trustManager.getAcceptedIssuers)
}
if (!exceptionList.isEmpty) {
val msg = exceptionList(0).getMessage
throw new CompositeCertificateException(msg, exceptionList.toArray)
}
certificates.toArray
}
def checkClientTrusted(chain: Array[X509Certificate], authType: String) {
logger.debug("checkClientTrusted: chain = {}", debugChain(chain))
val anchor: TrustAnchor = new TrustAnchor(chain(chain.length - 1), null)
logger.debug(s"checkClientTrusted: checking key size only on root anchor $anchor")
algorithmChecker.checkKeyAlgorithms(anchor.getTrustedCert)
var trusted = false
val exceptionList = withTrustManagers {
trustManager =>
trustManager.checkClientTrusted(chain, authType)
logger.debug(s"checkClientTrusted: trustManager $trustManager found a match for ${debugChain(chain)}")
trusted = true
}
if (!trusted) {
val msg = "No trust manager was able to validate this certificate chain."
throw new CompositeCertificateException(msg, exceptionList.toArray)
}
}
def checkServerTrusted(chain: Array[X509Certificate], authType: String): Unit = {
logger.debug(s"checkServerTrusted: chain = ${debugChain(chain)}, authType = $authType")
val anchor: TrustAnchor = new TrustAnchor(chain(chain.length - 1), null)
logger.debug(s"checkServerTrusted: checking key size only on root anchor $anchor")
algorithmChecker.checkKeyAlgorithms(anchor.getTrustedCert)
var trusted = false
val exceptionList = withTrustManagers {
trustManager =>
trustManager.checkServerTrusted(chain, authType)
logger.debug(s"checkServerTrusted: trustManager $trustManager using authType $authType found a match for ${debugChain(chain).toSeq}")
trusted = true
}
if (!trusted) {
val msg = s"No trust manager was able to validate this certificate chain: # of exceptions = ${exceptionList.size}"
throw new CompositeCertificateException(msg, exceptionList.toArray)
}
}
private def withTrustManagers(block: (X509TrustManager => Unit)): Seq[Throwable] = {
val exceptionList = ArrayBuffer[Throwable]()
trustManagers.foreach {
trustManager =>
try {
block(trustManager)
} catch {
case e: CertPathBuilderException =>
logger.debug("No path found to certificate: this usually means the CA is not in the trust store", e)
exceptionList.append(e)
case e: GeneralSecurityException =>
logger.debug("General security exception", e)
exceptionList.append(e)
case NonFatal(e) =>
logger.debug("Unexpected exception!", e)
exceptionList.append(e)
}
}
exceptionList
}
override def toString = {
s"CompositeX509TrustManager(trustManagers = [$trustManagers], algorithmChecker = $algorithmChecker)"
}
}