|
Play Framework/Scala example source code file (SSLContextBuilder.scala)
The SSLContextBuilder.scala Play Framework example source code/* * Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com> */ package play.api.libs.ws.ssl import javax.net.ssl._ import java.security._ import java.security.cert._ import java.io._ import java.net.URL import play.api.libs.ws.ssl.AlgorithmConstraint trait SSLContextBuilder { def build(): SSLContext } /** * A simple SSL context builder. If the keyManagers or trustManagers are empty, then null is used in the init method. * Likewise, if secureRandom is None then null is used. */ class SimpleSSLContextBuilder(protocol: String, keyManagers: Seq[KeyManager], trustManagers: Seq[TrustManager], secureRandom: Option[SecureRandom]) extends SSLContextBuilder { def nullIfEmpty[T](array: Array[T]) = { if (array.isEmpty) null else array } /** * Builds the appropriate SSL context manager. * @return a configured SSL context. */ def build(): SSLContext = { // We deliberately do not pass in a provider, on the recommendation of // http://docs.oracle.com/javase/7/docs/technotes/guides/security/SunProviders.html#SunJSSEProvider // // "REMINDER: Cryptographic implementations in the JDK are distributed through several different providers // ("Sun", "SunJSSE", "SunJCE", "SunRsaSign") for both historical reasons and by the types of services provided. // General purpose applications SHOULD NOT request cryptographic services from specific providers." val sslContext = SSLContext.getInstance(protocol) sslContext.init(nullIfEmpty(keyManagers.toArray), nullIfEmpty(trustManagers.toArray), secureRandom.orNull) sslContext } } // the KeyManagerFactory and TrustManagerFactory use final methods and protected abstract constructors that make // mocking tough. Either we provide a wrapper, or we set up our own "mock" provider, or we use Powermock. trait KeyManagerFactoryWrapper { @throws[KeyStoreException] @throws[NoSuchAlgorithmException] @throws[UnrecoverableKeyException] def init(keystore: KeyStore, password: Array[Char]): Unit def getKeyManagers: Array[KeyManager] } trait TrustManagerFactoryWrapper { @throws[InvalidAlgorithmParameterException] def init(spec: ManagerFactoryParameters): Unit def getTrustManagers: Array[TrustManager] } class DefaultKeyManagerFactoryWrapper(keyManagerAlgorithm: String) extends KeyManagerFactoryWrapper { private val instance = KeyManagerFactory.getInstance(keyManagerAlgorithm) def init(keystore: KeyStore, password: Array[Char]) { instance.init(keystore, password) } def getKeyManagers: Array[KeyManager] = instance.getKeyManagers } class DefaultTrustManagerFactoryWrapper(trustManagerAlgorithm: String) extends TrustManagerFactoryWrapper { private val instance = TrustManagerFactory.getInstance(trustManagerAlgorithm) def init(spec: ManagerFactoryParameters) { instance.init(spec) } def getTrustManagers: Array[TrustManager] = instance.getTrustManagers } /** * Creates an SSL context builder from info objects. */ class ConfigSSLContextBuilder(info: SSLConfig, keyManagerFactory: KeyManagerFactoryWrapper, trustManagerFactory: TrustManagerFactoryWrapper) extends SSLContextBuilder { protected val logger = org.slf4j.LoggerFactory.getLogger(getClass) def build: SSLContext = { val protocol = info.protocol.getOrElse(Protocols.recommendedProtocol) val checkRevocation = info.checkRevocation.getOrElse(false) val revocationLists = certificateRevocationList(info) val disabledSignatureAlgorithms = info.disabledSignatureAlgorithms.getOrElse(Algorithms.disabledSignatureAlgorithms) val signatureConstraints = AlgorithmConstraintsParser(disabledSignatureAlgorithms).toSet val disabledKeyAlgorithms = info.disabledKeyAlgorithms.getOrElse(Algorithms.disabledKeyAlgorithms) val keySizeConstraints = AlgorithmConstraintsParser(disabledKeyAlgorithms).toSet val algorithmChecker = new AlgorithmChecker(signatureConstraints, keySizeConstraints) val keyManagers: Seq[KeyManager] = info.keyManagerConfig.map { kmc => Seq(buildCompositeKeyManager(kmc, algorithmChecker)) }.getOrElse(Nil) val trustManagers: Seq[TrustManager] = info.trustManagerConfig.map { tmc => Seq(buildCompositeTrustManager(tmc, checkRevocation, revocationLists, algorithmChecker)) }.getOrElse(Nil) buildSSLContext(protocol, keyManagers, trustManagers, info.secureRandom) } def buildSSLContext(protocol: String, keyManagers: Seq[KeyManager], trustManagers: Seq[TrustManager], secureRandom: Option[SecureRandom]) = { val builder = new SimpleSSLContextBuilder(protocol, keyManagers, trustManagers, secureRandom) builder.build() } def buildCompositeKeyManager(keyManagerConfig: KeyManagerConfig, algorithmChecker: AlgorithmChecker) = { val keyManagers = keyManagerConfig.keyStoreConfigs.map { ksc => buildKeyManager(ksc, algorithmChecker) } new CompositeX509KeyManager(keyManagers) } def buildCompositeTrustManager(trustManagerInfo: TrustManagerConfig, revocationEnabled: Boolean, revocationLists: Option[Seq[CRL]], algorithmChecker: AlgorithmChecker) = { val trustManagers = trustManagerInfo.trustStoreConfigs.map { tsc => buildTrustManager(tsc, revocationEnabled, revocationLists, algorithmChecker) } new CompositeX509TrustManager(trustManagers, algorithmChecker) } // Get either a string or file based keystore builder from config. def keyStoreBuilder(ksc: KeyStoreConfig): KeyStoreBuilder = { val storeType = ksc.storeType.getOrElse(KeyStore.getDefaultType) val password = ksc.password.map(_.toCharArray) ksc.filePath.map { f => fileBuilder(storeType, f, password) }.getOrElse { val data = ksc.data.getOrElse(throw new IllegalStateException("No keystore builder found!")) stringBuilder(data, password) } } def trustStoreBuilder(tsc: TrustStoreConfig): KeyStoreBuilder = { val storeType = tsc.storeType.getOrElse(KeyStore.getDefaultType) tsc.filePath.map { f => fileBuilder(storeType, f, None) }.getOrElse { val data = tsc.data.getOrElse(throw new IllegalStateException("No truststore builder found!")) stringBuilder(data, None) } } def fileBuilder(storeType: String, filePath: String, password: Option[Array[Char]]): KeyStoreBuilder = { new FileBasedKeyStoreBuilder(storeType, filePath, password) } def stringBuilder(data: String, password: Option[Array[Char]]): KeyStoreBuilder = { new StringBasedKeyStoreBuilder(data, password) } /** * Builds a key manager from a keystore, using the KeyManagerFactory. */ def buildKeyManager(ksc: KeyStoreConfig, algorithmChecker: AlgorithmChecker): X509KeyManager = { val keyStore = keyStoreBuilder(ksc).build() validateStore(keyStore, algorithmChecker) val password = ksc.password.map(_.toCharArray) val factory = keyManagerFactory try { factory.init(keyStore, password.orNull) } catch { case e: UnrecoverableKeyException => logger.error(s"Unrecoverable key in keystore $ksc") throw new IllegalStateException(e) } val keyManagers = factory.getKeyManagers if (keyManagers == null) { val msg = s"Cannot create key manager with configuration $ksc" throw new IllegalStateException(msg) } // The JSSE implementation only sends back ONE key manager, X509ExtendedKeyManager keyManagers.head.asInstanceOf[X509KeyManager] } // Should anyone have any interest in implementing this feature at all, they can implement this method and // submit a patch. def certificateRevocationList(sslConfig: SSLConfig): Option[Seq[CRL]] = { sslConfig.revocationLists.map { urls => urls.map(generateCRLFromURL) } } def generateCRL(inputStream: InputStream): CRL = { val cf = CertificateFactory.getInstance("X509") val crl = cf.generateCRL(inputStream).asInstanceOf[X509CRL] crl } def generateCRLFromURL(url: URL): CRL = { val connection = url.openConnection() connection.setDoInput(true) connection.setUseCaches(false) val inStream = new DataInputStream(connection.getInputStream) try { generateCRL(inStream) } finally { inStream.close() } } def generateCRLFromFile(file: File): CRL = { val fileStream = new BufferedInputStream(new FileInputStream(file)) val inStream = new DataInputStream(fileStream) try { generateCRL(inStream) } finally { inStream.close() } } def buildTrustManagerParameters(trustStore: KeyStore, revocationEnabled: Boolean, revocationLists: Option[Seq[CRL]], algorithmChecker: AlgorithmChecker): CertPathTrustManagerParameters = { import scala.collection.JavaConverters._ val certSelect: X509CertSelector = new X509CertSelector val pkixParameters = new PKIXBuilderParameters(trustStore, certSelect) pkixParameters.setRevocationEnabled(revocationEnabled) // For the sake of completeness, set the static revocation list if it exists... revocationLists.map { crlList => import scala.collection.JavaConverters._ pkixParameters.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(crlList.asJavaCollection))) } // Add the algorithm checker in here to check the certification path sequence (not including trust anchor)... val checkers: Seq[PKIXCertPathChecker] = Seq(algorithmChecker) // Use the custom cert path checkers we defined... pkixParameters.setCertPathCheckers(checkers.asJava) new CertPathTrustManagerParameters(pkixParameters) } /** * Builds trust managers, using a TrustManagerFactory internally. */ def buildTrustManager(tsc: TrustStoreConfig, revocationEnabled: Boolean, revocationLists: Option[Seq[CRL]], algorithmChecker: AlgorithmChecker): X509TrustManager = { val factory = trustManagerFactory val trustStore = trustStoreBuilder(tsc).build() validateStore(trustStore, algorithmChecker) val trustManagerParameters = buildTrustManagerParameters( trustStore, revocationEnabled, revocationLists, algorithmChecker) factory.init(trustManagerParameters) val trustManagers = factory.getTrustManagers if (trustManagers == null) { val msg = s"Cannot create trust manager with configuration $tsc" throw new IllegalStateException(msg) } // The JSSE implementation only sends back ONE trust manager, X509TrustManager trustManagers.head.asInstanceOf[X509TrustManager] } /** * Tests each trusted certificate in the store, and warns if the certificate is not valid. Does not throw * exceptions. */ def validateStore(store: KeyStore, algorithmChecker: AlgorithmChecker) { import scala.collection.JavaConverters._ logger.debug(s"validateKeyStore: type = ${store.getType}, size = ${store.size}") store.aliases().asScala.foreach { alias => Option(store.getCertificate(alias)).map { c => try { algorithmChecker.checkKeyAlgorithms(c) } catch { case e: CertPathValidatorException => logger.warn(s"validateKeyStore: Skipping certificate with weak key size in $alias: " + e.getMessage) store.deleteEntry(alias) case e: Exception => logger.warn(s"validateKeyStore: Skipping unknown exception $alias: " + e.getMessage) store.deleteEntry(alias) } } } } } Other Play Framework source code examplesHere is a short list of links related to this Play Framework SSLContextBuilder.scala source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.