diff --git a/apps/dashboard-app/src/main/scala/net/shrine/dashboard/jwtauth/ShrineJwtAuthenticator.scala b/apps/dashboard-app/src/main/scala/net/shrine/dashboard/jwtauth/ShrineJwtAuthenticator.scala
index 0f58d3b35..3dcb996b1 100644
--- a/apps/dashboard-app/src/main/scala/net/shrine/dashboard/jwtauth/ShrineJwtAuthenticator.scala
+++ b/apps/dashboard-app/src/main/scala/net/shrine/dashboard/jwtauth/ShrineJwtAuthenticator.scala
@@ -1,202 +1,202 @@
package net.shrine.dashboard.jwtauth
import java.io.ByteArrayInputStream
import java.security.{Principal, Key, PrivateKey}
import java.security.cert.{CertificateFactory, X509Certificate}
import java.util.Date
import io.jsonwebtoken.impl.TextCodec
import io.jsonwebtoken.{Jws, ClaimJwtException, Claims, SignatureAlgorithm, Jwts}
import net.shrine.crypto.{KeyStoreDescriptorParser, KeyStoreCertCollection}
import net.shrine.dashboard.DashboardConfigSource
import net.shrine.i2b2.protocol.pm.User
import net.shrine.log.Loggable
import net.shrine.protocol.Credential
import spray.http.HttpHeaders.{Authorization, `WWW-Authenticate`}
import spray.http.{HttpRequest, OAuth2BearerToken, HttpHeader, HttpChallenge}
import spray.routing.AuthenticationFailedRejection.{CredentialsMissing, CredentialsRejected}
import spray.routing.AuthenticationFailedRejection
import spray.routing.authentication._
import scala.concurrent.{Future, ExecutionContext}
import scala.util.{Success, Failure, Try}
/**
* An Authenticator that uses Jwt in a Bearer header to authenticate. See http://jwt.io/introduction/ for what this is all about,
* https://tools.ietf.org/html/rfc7519 for what it might include for claims.
*
* @author david
* @since 12/21/15
*/
object ShrineJwtAuthenticator extends Loggable {
val config = DashboardConfigSource.config
val certCollection: KeyStoreCertCollection = KeyStoreCertCollection.fromFileRecoverWithClassPath(KeyStoreDescriptorParser(config.getConfig("shrine.keystore")))
//from https://groups.google.com/forum/#!topic/spray-user/5DBEZUXbjtw
def authenticate(implicit ec: ExecutionContext): ContextAuthenticator[User] = { ctx =>
Future {
val attempt: Try[Authentication[User]] = for {
header:HttpHeader <- extractAuthorizationHeader(ctx.request)
jwtsString:String <- extractJwtsStringAndCheckScheme(header)
jwtsClaims <- extractJwtsClaims(jwtsString)
cert: X509Certificate <- extractAndCheckCert(jwtsClaims)
jwtsBody:Claims <- Try{jwtsClaims.getBody}
jwtsSubject <- failIfNull(jwtsBody.getSubject,MissingRequiredJwtsClaim("subject",cert.getSubjectDN))
jwtsIssuer <- failIfNull(jwtsBody.getSubject,MissingRequiredJwtsClaim("issuer",cert.getSubjectDN))
} yield {
val user = User(
fullName = cert.getSubjectDN.getName,
username = jwtsSubject,
domain = jwtsIssuer,
credential = Credential(jwtsIssuer, isToken = false),
params = Map(),
rolesByProject = Map()
)
Right(user)
}
//todo use a fold() in Scala 2.12
attempt match {
case Success(rightUser) => rightUser
case Failure(x) => x match
{
case anticipated: ShrineJwtException =>
info(s"Failed to authenticate due to ${anticipated.toString}",anticipated)
anticipated.rejection
case fromJwts: ClaimJwtException =>
info(s"Failed to authenticate due to ${fromJwts.toString} while authenticating ${ctx.request}",fromJwts)
rejectedCredentials
/*
case x: CertificateExpiredException => {
//todo will these even be thrown here? Get some identification here
info(s"Cert expired.", x)
rejectedCredentials
}
case x: CertificateNotYetValidException => {
info(s"Cert not yet valid.", x)
rejectedCredentials
}
*/
case unanticipated =>
warn(s"Unanticipated ${unanticipated.toString} while authenticating ${ctx.request}",unanticipated)
rejectedCredentials
}
}
}
}
def createOAuthCredentials(user:User): OAuth2BearerToken = {
- val base64Cert = new String(TextCodec.BASE64URL.encode(certCollection.myCert.get.getEncoded))
+ val base64Cert:String = TextCodec.BASE64URL.encode(certCollection.myCert.get.getEncoded)
val key: PrivateKey = certCollection.myKeyPair.privateKey
val expiration: Date = new Date(System.currentTimeMillis() + 30 * 1000) //good for 30 seconds
val jwtsString = Jwts.builder().
setHeaderParam("kid", base64Cert).
setSubject(s"${user.username} at ${user.domain}").
setIssuer(java.net.InetAddress.getLocalHost.getHostName). //todo is it OK for me to use issuer this way or should I use my own claim?
setExpiration(expiration).
signWith(SignatureAlgorithm.RS512, key).
compact()
OAuth2BearerToken(jwtsString)
}
def extractAuthorizationHeader(request: HttpRequest):Try[HttpHeader] = Try {
case class NoAuthorizationHeaderException(request: HttpRequest) extends ShrineJwtException(s"No ${Authorization.name} header found in $request",missingCredentials)
//noinspection ComparingUnrelatedTypes
request.headers.find(_.name.equals(Authorization.name)).getOrElse{throw NoAuthorizationHeaderException(request)}
}
def extractJwtsStringAndCheckScheme(httpHeader: HttpHeader) = Try {
val splitHeaderValue: Array[String] = httpHeader.value.trim.split(" ")
if (splitHeaderValue.length != 2) {
case class WrongNumberOfSegmentsException(httpHeader: HttpHeader) extends ShrineJwtException(s"Header had ${splitHeaderValue.length} space-delimited segments, not 2, in $httpHeader.",missingCredentials)
throw new WrongNumberOfSegmentsException(httpHeader)
}
else if(splitHeaderValue(0) != BearerAuthScheme) {
case class NotBearerAuthException(httpHeader: HttpHeader) extends ShrineJwtException(s"Expected $BearerAuthScheme, not ${splitHeaderValue(0)} in $httpHeader.",missingCredentials)
throw new NotBearerAuthException(httpHeader)
}
else splitHeaderValue(1)
}
def extractJwtsClaims(jwtsString:String): Try[Jws[Claims]] = Try {
Jwts.parser().setSigningKeyResolver(new SigningKeyResolverBridge()).parseClaimsJws(jwtsString)
}
def extractAndCheckCert(jwtsClaims:Jws[Claims]): Try[X509Certificate] = Try {
val cert = KeySource.certForString(jwtsClaims.getHeader.getKeyId)
val issuingSite = jwtsClaims.getBody.getIssuer
//todo is this the right way to find a cert in the certCollection?
debug(s"certCollection.caCerts.contains(${cert.getSubjectX500Principal}) is ${certCollection.caCerts.contains(cert.getSubjectX500Principal)}")
certCollection.caCerts.get(cert.getSubjectX500Principal).fold{
//if not in the keystore, check that the issuer is available
val issuer: Principal = cert.getIssuerX500Principal
case class CertIssuerNotInCollectionException(issuingSite:String,issuer: Principal) extends ShrineJwtException(s"Could not find a CA certificate with issuer DN $issuer. Known CA cert aliases are ${certCollection.caCertAliases.mkString(",")}")
val signingCert = certCollection.caCerts.getOrElse(issuer,{throw CertIssuerNotInCollectionException(issuingSite,issuer)})
//verify that the cert was signed using the signingCert
//todo this can throw CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException
cert.verify(signingCert.getPublicKey)
//todo has cert expired?
info(s"${cert.getSubjectX500Principal} verified using $issuer from the KeyStore")
cert
}{ principal => //if the cert is in the certCollection then all is well
info(s"$principal is in the KeyStore")
cert
}
}
def failIfNull[E](e:E,t:Throwable):Try[E] = Try {
if(e == null) throw t
else e
}
case class MissingRequiredJwtsClaim(field:String,principal: Principal) extends ShrineJwtException(s"$field is null from ${principal.getName}")
val BearerAuthScheme = "Bearer"
val challengeHeader: `WWW-Authenticate` = `WWW-Authenticate`(HttpChallenge(BearerAuthScheme, "dashboard-to-dashboard"))
val missingCredentials: Authentication[User] = Left(AuthenticationFailedRejection(CredentialsMissing, List(challengeHeader)))
val rejectedCredentials: Authentication[User] = Left(AuthenticationFailedRejection(CredentialsRejected, List(challengeHeader)))
}
class KeySource {}
object KeySource extends Loggable {
def keyForString(string: String): Key = {
val certificate =certForString(string)
//todo validate cert with something like obtainAndValidateSigningCert
//check date on cert vs time. throws CertificateExpiredException or CertificateNotYetValidException for problems
//todo skip this until you rebuild the certs used for testing certificate.checkValidity(now)
certificate.getPublicKey
}
def certForString(string: String): X509Certificate = {
val certBytes = TextCodec.BASE64URL.decode(string)
val inputStream = new ByteArrayInputStream(certBytes)
val certificate = try {
CertificateFactory.getInstance("X.509").generateCertificate(inputStream).asInstanceOf[X509Certificate]
}
finally {
inputStream.close()
}
certificate
}
}
abstract class ShrineJwtException(message:String,
val rejection:Authentication[User] = ShrineJwtAuthenticator.rejectedCredentials,
cause:Throwable = null) extends RuntimeException(message,cause)
\ No newline at end of file
diff --git a/commons/crypto/pom.xml b/commons/crypto/pom.xml
index 651056024..db4a74ca9 100644
--- a/commons/crypto/pom.xml
+++ b/commons/crypto/pom.xml
@@ -1,52 +1,64 @@
4.0.0SHRINE Cryptoshrine-cryptojarnet.shrineshrine-base1.22.2.0-SNAPSHOT../../pom.xmlnet.shrineshrine-test-commons${project.version}test-jartestnet.shrineshrine-protocol${project.version}net.shrineshrine-config${project.version}
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ ${bouncy-castle-version}
+
+
+
+ org.cryptacular
+ cryptacular
+ ${cryptacular-version}
+ src/main/scalasrc/test/scalanet.alchim31.mavenscala-maven-pluginorg.apache.maven.pluginsmaven-jar-plugintest-jar
diff --git a/commons/crypto/src/main/scala/net/shrine/crypto/DefaultSignerVerifier.scala b/commons/crypto/src/main/scala/net/shrine/crypto/DefaultSignerVerifier.scala
index 33fe4aac0..f4f168b6e 100644
--- a/commons/crypto/src/main/scala/net/shrine/crypto/DefaultSignerVerifier.scala
+++ b/commons/crypto/src/main/scala/net/shrine/crypto/DefaultSignerVerifier.scala
@@ -1,179 +1,172 @@
package net.shrine.crypto
-import java.security.PrivateKey
-import java.security.{ Signature => JSig }
-import java.security.cert.Certificate
+import java.security.{ Signature => JSig, PrivateKey, PublicKey }
+import java.security.cert.{Certificate, X509Certificate}
import javax.xml.datatype.XMLGregorianCalendar
+
+import net.shrine.protocol.{BroadcastMessage, CertId, Signature, CertData}
import net.shrine.log.Loggable
-import net.shrine.protocol.BroadcastMessage
-import net.shrine.protocol.CertId
-import net.shrine.protocol.Signature
import net.shrine.util.{Tries, XmlDateHelper, XmlGcEnrichments}
+
import scala.concurrent.duration.Duration
-import net.shrine.protocol.CertData
-import scala.util.Success
-import scala.util.Failure
-import java.security.cert.X509Certificate
-import scala.util.Try
-import java.security.PublicKey
+import scala.util.{Success, Failure, Try}
/**
* @author clint
* @since Nov 25, 2013
*/
final class DefaultSignerVerifier(certCollection: CertCollection) extends Signer with Verifier with Loggable {
logStartup()
import DefaultSignerVerifier._
import SigningCertStrategy._
override def sign(message: BroadcastMessage, signingCertStrategy: SigningCertStrategy): BroadcastMessage = {
val signatureOption = for {
signedBy <- certCollection.myCertId
KeyPair(_, myPrivateKey) = certCollection.myKeyPair
myCert <- certCollection.myCert
} yield {
val timestamp = XmlDateHelper.now
//TODO: Find way to attach only the public part of the signing cert, plus its signatures
//TODO: Currently the whole signing cert is attached (right?)
val signingCert = {
if (signingCertStrategy.attachSigningCert) { Some(CertData(myCert)) }
else { None }
}
Signature(timestamp, signedBy, signingCert, DefaultSignerVerifier.sign(myPrivateKey, toBytes(message, timestamp)))
}
val sig = signatureOption.getOrElse(throw new Exception(s"Can't sign, no private keys. Known ids: ${certCollection.ids}"))
message.withSignature(sig)
}
import Tries.toTry
private[crypto] def isSignedByTrustedCA(attachedCert: X509Certificate): Try[Boolean] = {
for {
attachedCertSignerCert <- toTry(certCollection.caCerts.get(CertCollection.getIssuer(attachedCert)))(new Exception(s"Couldn't find CA certificate with issuer DN '${attachedCert.getIssuerDN}'; known CA cert aliases: ${certCollection.caCertAliases.mkString(",")}"))
caPublicKey = attachedCertSignerCert.getPublicKey
} yield {
attachedCert.isSignedBy(caPublicKey)
}
}
private[crypto] def obtainAndValidateSigningCert(signature: Signature): Try[X509Certificate] = {
signature.signingCert match {
//If the signing cert was sent with the signature, and the signing cert was signed by a CA we trust
- case Some(signingCertData) => {
+ case Some(signingCertData) =>
for {
signingCert <- Try(signingCertData.toCertificate)
signedByTrustedCA <- isSignedByTrustedCA(signingCert)
} yield {
if (signedByTrustedCA) { signingCert }
else {
throw new Exception(s"Couldn't verify: signing cert with serial '${signingCert.getSerialNumber}' was part of the signature, but was not signed by any CA we trust. Aliases of trusted CAs are: ${certCollection.caCertAliases.map(s => s"'$s'").mkString(",")}")
}
}
- }
+
//Otherwise, look up the signing cert in our keystore by its CertId
- case None => {
+ case None =>
val signerCertOption = certCollection.get(signature.signedBy)
toTry(signerCertOption)(new Exception(s"Couldn't verify: can't find signer key with CertId ${signature.signedBy}"))
- }
}
}
override def verifySig(message: BroadcastMessage, maxSignatureAge: Duration): Boolean = {
def notTooOld(sig: Signature): Boolean = {
import scala.concurrent.duration._
import XmlGcEnrichments._
val sigValidityEndTime = sig.timestamp + maxSignatureAge
sigValidityEndTime > XmlDateHelper.now
}
message.signature match {
case None => false
case Some(signature) => {
val signerCertAttempt: Try[X509Certificate] = obtainAndValidateSigningCert(signature)
val verificationAttempt = for {
signerCert <- signerCertAttempt
if notTooOld(signature)
} yield {
DefaultSignerVerifier.verify(signerCert, toBytes(message, signature.timestamp), signature.value.array)
}
verificationAttempt match {
case Success(result) => result
case Failure(reason) => {
warn(s"Error verifying signature for message with id '${message.requestId}': ", reason)
false
}
}
}
}
}
private def logStartup() {
debug(s"DefaultSignerVerifier using cert collection: ")
debug(s"Private key id: ${certCollection.myCertId}")
debug(s"Known certs: ")
def certIdToMessage(certId: CertId): String = s" ${certId.serial} with name '${certId.name.getOrElse("")}'"
certCollection.ids.foreach { certId =>
debug(certIdToMessage(certId))
}
debug(s"Known CA certs: ")
certCollection.caCerts.values.map(KeyStoreCertCollection.toCertId).foreach { certId =>
debug(certIdToMessage(certId))
}
}
}
object DefaultSignerVerifier {
val signatureAlgorithm = "SHA256withRSA"
private implicit final class HasBooleanSignedBy(val cert: X509Certificate) extends AnyVal {
def isSignedBy(caPubKey: PublicKey): Boolean = Try { cert.verify(caPubKey); true }.getOrElse(false)
}
private def toBytes(message: BroadcastMessage, timestamp: XMLGregorianCalendar): Array[Byte] = {
val messageXml = message.copy(signature = None).toXmlString
val timestampXml = timestamp.toXMLFormat
(messageXml + timestampXml).getBytes("UTF-8")
}
private[crypto] def sign(signingKey: PrivateKey, bytes: Array[Byte]): Array[Byte] = {
val signerVerifier = getSignerVerifier
signerVerifier.initSign(signingKey)
signerVerifier.update(bytes)
signerVerifier.sign
}
private[crypto] def verify(signerCert: Certificate, signedBytes: Array[Byte], signatureBytes: Array[Byte]): Boolean = {
val signerVerifier = getSignerVerifier
signerVerifier.initVerify(signerCert)
signerVerifier.update(signedBytes)
signerVerifier.verify(signatureBytes)
}
private def getSignerVerifier: JSig = JSig.getInstance(signatureAlgorithm)
}
\ No newline at end of file
diff --git a/commons/crypto/src/main/scala/net/shrine/crypto/KeyStoreCertCollection.scala b/commons/crypto/src/main/scala/net/shrine/crypto/KeyStoreCertCollection.scala
index 2b703ad9a..37a793a9a 100644
--- a/commons/crypto/src/main/scala/net/shrine/crypto/KeyStoreCertCollection.scala
+++ b/commons/crypto/src/main/scala/net/shrine/crypto/KeyStoreCertCollection.scala
@@ -1,158 +1,156 @@
package net.shrine.crypto
import java.io.{IOException, FileInputStream, InputStream, File}
-import java.security.KeyStore
-import java.security.PrivateKey
+import java.security.{KeyStore, PrivateKey, Key, Principal}
import java.security.cert.X509Certificate
import javax.naming.ldap.{Rdn, LdapName}
+
import net.shrine.log.Loggable
+import net.shrine.protocol.CertId
import scala.collection.JavaConverters.enumerationAsScalaIteratorConverter
-import net.shrine.protocol.CertId
-import java.security.Key
-import java.security.Principal
/**
* @author clint
* @since Nov 22, 2013
*/
final case class KeyStoreCertCollection(keystore: KeyStore, descriptor: KeyStoreDescriptor) extends CertCollection with Loggable {
override def size: Int = keystore.size
override def get(id: CertId): Option[X509Certificate] = certsById.get(id)
override def iterator: Iterator[X509Certificate] = certsById.valuesIterator ++ caCerts.valuesIterator
override def ids: Iterable[CertId] = certsById.keys
import KeyStoreCertCollection.toCertId
override lazy val caIds: Iterable[CertId] = caCerts.values.map(toCertId)
override def caCertAliases: Seq[String] = descriptor.caCertAliases
override lazy val caCerts: Map[Principal, X509Certificate] = {
caCertAliases.flatMap(getX509Cert).map(cert => CertCollection.getIssuer(cert) -> cert).toMap
}
override lazy val myCert: Option[X509Certificate] = descriptor.privateKeyAlias.flatMap(getX509Cert)
override lazy val myCertId: Option[CertId] = myCert.map(toCertId)
lazy val myCommonName:Option[String] = {
myCert.flatMap{cert:X509Certificate =>
KeyStoreCertCollection.extractCommonName(cert)
}
}
override val myKeyPair: KeyPair = {
val privateKeyAlias: String = descriptor.privateKeyAlias match {
case Some(alias) =>
if(isPrivateKey(alias)) { alias }
else throw new Exception(s"No key, or no private key component, at alias '$alias'")
case _ =>
val privateKeyAliases = keystore.aliases.asScala.filter(isPrivateKey).toIndexedSeq
privateKeyAliases.size match {
case 1 =>
val alias = privateKeyAliases.head
info(s"Found one cert with a private key, with alias '$alias'")
alias
case 0 => throw new Exception(s"No aliases point to certs with private keys. Known aliases are: $privateKeyAliases")
case n => throw new Exception(s"$n aliases point to certs with private keys: $privateKeyAliases; specify the private key to use with the privateKeyAlias option")
}
}
val keyPairOption = for {
cert <- getX509Cert(privateKeyAlias)
privateKey <- getPrivateKey(privateKeyAlias)
} yield KeyPair(cert.getPublicKey, privateKey)
require(keyPairOption.isDefined, "Private key alias must be defined, and identify a cert with a private key component, or exactly one cert with a private key component must be present in the keystore")
keyPairOption.get
}
private def getKey(alias: String): Option[Key] = {
Option(keystore.getKey(alias, descriptor.password.toCharArray))
}
private def isPrivateKey(alias: String): Boolean = {
getKey(alias).exists(_.isInstanceOf[PrivateKey])
}
private def getPrivateKey(alias: String): Option[PrivateKey] = {
getKey(alias).collect { case pk: PrivateKey => pk }
}
private lazy val certsById: Map[CertId, X509Certificate] = {
import scala.collection.JavaConverters._
val nonCaAliases = keystore.aliases.asScala.toSet -- caCertAliases
val certs = nonCaAliases.toSeq.flatMap(getX509Cert)
certs.map(cert => (toCertId(cert), cert)).toMap
}
private[crypto] def getX509Cert(alias: String): Option[X509Certificate] = {
Option(keystore.getCertificate(alias).asInstanceOf[X509Certificate])
}
}
object KeyStoreCertCollection extends Loggable {
/** Try the file system if a keystore file exists, else try the classpath*/
def fromFileRecoverWithClassPath(descriptor: KeyStoreDescriptor): KeyStoreCertCollection = {
if(new File(descriptor.file).exists) fromFile(descriptor)
else fromClassPathResource(descriptor)
}
def fromFile(descriptor: KeyStoreDescriptor): KeyStoreCertCollection = {
require(new File(descriptor.file).exists,s"Keystore file '${descriptor.file}' exists? ${new File(descriptor.file).exists}")
fromStream(descriptor, new FileInputStream(_))
}
def fromClassPathResource(descriptor: KeyStoreDescriptor): KeyStoreCertCollection = {
fromStream(descriptor, getClass.getClassLoader.getResourceAsStream)
}
def fromStream(descriptor: KeyStoreDescriptor, streamFrom: String => InputStream): KeyStoreCertCollection = {
def toString(descriptor: KeyStoreDescriptor) = descriptor.copy(password = "********").toString
debug(s"Loading keystore using descriptor: ${toString(descriptor)}")
val stream = streamFrom(descriptor.file)
require(stream != null,s"null stream for descriptor ${toString(descriptor)}")
val keystore = KeyStore.getInstance(descriptor.keyStoreType.name)
try {
keystore.load(stream, descriptor.password.toCharArray)
} catch {case x:IOException => throw new IOException(s"Unable to load keystore from $descriptor",x)}
import scala.collection.JavaConverters._
debug(s"Keystore aliases: ${keystore.aliases.asScala.mkString(",")}")
debug(s"Keystore ${toString(descriptor)} loaded successfully")
KeyStoreCertCollection(keystore, descriptor)
}
private[crypto] def toCertId(cert: X509Certificate): CertId = {
//TODO: Is getSubjectDN right for a human-readable name?
CertId(cert.getSerialNumber, Option(cert.getSubjectDN.getName))
}
def extractCommonName(cert:X509Certificate):Option[String] = {
val ldapDn = new LdapName(cert.getSubjectX500Principal.getName)
import collection.JavaConverters._
val rdns: Array[Rdn] = ldapDn.getRdns.asScala.toArray
rdns.collectFirst{case rdn:Rdn if rdn.getType == "CN" => rdn.getValue.toString}
}
}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 0c584257c..c9c1d9416 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,376 +1,389 @@
4.0.0SHRINEnet.shrineshrine-basepom1.22.2.0-SNAPSHOTUTF-84.2.5.RELEASE2.6.22.11.82.114.121.7.181.2.171.192.2.63.2.20.9.6-RC41.2.11.4.1913.45.1.382.33.0.10.9.51.3.32.3.142.4.03.3.03.1.13.1.1.1
+ 1.55
+ 1.2.0apps/dashboard-appapps/dashboard-warapps/steward-appapps/steward-warapps/proxyapps/shrine-appapps/warqep/servicehub/broadcaster-aggregatorhub/broadcaster-serviceadapter/adapter-apiadapter/adapter-servicehms-supporttoolscommons/utilcommons/authcommons/protocol-querycommons/data-commonscommons/protocolcommons/cryptocommons/clientcommons/configcommons/ont-supportcommons/test-commonsinstallintegrationshrine-webclientnet.alchim31.mavenscala-maven-plugin${scala-maven-plugin-version}compilecompilecompiletest-compiletestCompiletest-compileprocess-resourcescompileincrementaltrue-XX:+AggressiveOpts-XX:CompileThreshold=500-XX:+UseFastAccessorMethods-XX:+UseStringCache-XX:+OptimizeStringConcat-XX:+TieredCompilation-XX:+UseConcMarkSweepGC-XX:+DoEscapeAnalysis-server-Xms64m-Xmx1024m-XX:MaxPermSize=384m${scala-version}-Xcheckinit-unchecked-deprecation-Xlint:adapted-args,inaccessible,infer-any,missing-interpolator,private-shadow,type-parameter-shadow,unsound-match7maven-compiler-plugin7org.codehaus.mojobuildnumber-maven-plugin1.1org.apache.maven.pluginsmaven-jar-plugin2.4org.apache.maven.pluginsmaven-war-plugin2.1.1org.codehaus.mojobuildnumber-maven-pluginvalidatecreate{0,date,yyyy-MM-dd HH:mm:ss}(not available)org.apache.maven.pluginsmaven-jar-plugintrue${buildNumber}${scmBranch}${timestamp}org.apache.maven.pluginsmaven-war-plugintrue${buildNumber}${scmBranch}${timestamp}org.apache.tomcat.maventomcat7-maven-plugin2.2truetruetruetruehttp://shrine-dev1.catalyst:6060/shrine/rest/happyscm:git:https://open.med.harvard.edu/stash/scm/shrine/shrine.gitCBMI-Nexushttps://repo.open.med.harvard.edu/nexus/content/groups/public/com.typesafeconfig${typesafe-config-version}log4jlog4j${log4j-version}org.springframeworkspring-jdbc${spring.version}testcom.h2databaseh2${h2-version}testorg.easymockeasymock${easymock-version}testorg.slf4jslf4j-log4j12${slf4j-version}testmysqlmysql-connector-java${mysql-version}net.sf.opencsvopencsv${opencsv-version}net.liftweblift-json_${scala-major-version}${lift-version}com.sun.jerseyjersey-server${jersey-version}com.sun.jerseyjersey-servlet${jersey-version}com.sun.jerseyjersey-client${jersey-version}org.squerylsqueryl_${scala-major-version}${squeryl-version}javax.servletjavax.servlet-api${servlet-api-version}provided
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ ${bouncy-castle-version}
+
+
+ org.cryptacular
+ cryptacular
+ ${cryptacular-version}
+ org.scala-langscala-library${scala-version}junitjunit${junit-version}testorg.scalatestscalatest_${scala-major-version}${scalatest-version}testorg.scala-langscala-actorsorg.scala-langscala-reflectorg.scala-langscala-actors${scala-version}testorg.scala-langscala-reflect${scala-version}nexusNexus Repohttps://repo.open.med.harvard.edu/nexus/content/repositories/snapshotsfalsenexusNexus Repohttps://repo.open.med.harvard.edu/nexus/content/repositories/releases-internal