diff --git a/bazel-java-deps.bzl b/bazel-java-deps.bzl index 443f81209f1..251170c4e64 100644 --- a/bazel-java-deps.bzl +++ b/bazel-java-deps.bzl @@ -151,6 +151,8 @@ def install_java_deps(): "junit:junit:4.12", "junit:junit-dep:4.10", "net.logstash.logback:logstash-logback-encoder:6.6", + "org.bouncycastle:bcpkix-jdk15on:1.70", + "org.bouncycastle:bcprov-jdk15on:1.70", "org.codehaus.janino:janino:3.1.4", "org.apache.commons:commons-lang3:3.9", "org.apache.commons:commons-text:1.4", diff --git a/ledger/cli-opts/BUILD.bazel b/ledger/cli-opts/BUILD.bazel index fb3474a2a20..8c58a6f4bbc 100644 --- a/ledger/cli-opts/BUILD.bazel +++ b/ledger/cli-opts/BUILD.bazel @@ -4,6 +4,7 @@ load( "//bazel_tools:scala.bzl", "da_scala_library", + "da_scala_test_suite", "lf_scalacopts", ) @@ -23,3 +24,30 @@ da_scala_library( "@maven//:com_auth0_java_jwt", ], ) + +da_scala_test_suite( + name = "cli-opts-tests", + srcs = glob(["src/test/suite/scala/**/*.scala"]), + scala_deps = [ + "@maven//:com_github_scopt_scopt", + "@maven//:org_scalactic_scalactic", + "@maven//:org_scalatest_scalatest_core", + "@maven//:org_scalatest_scalatest_matchers_core", + "@maven//:org_scalatest_scalatest_shouldmatchers", + "@maven//:org_scalatest_scalatest_wordspec", + "@maven//:org_scalaz_scalaz_core", + ], + scalacopts = lf_scalacopts, + deps = [ + ":cli-opts", + "//ledger-service/jwt", + "//ledger/ledger-api-auth", + "//ledger/test-common", + "//libs-scala/fs-utils", + "//libs-scala/resources", + "@maven//:com_auth0_java_jwt", + "@maven//:io_grpc_grpc_api", + "@maven//:org_bouncycastle_bcpkix_jdk15on", + "@maven//:org_bouncycastle_bcprov_jdk15on", + ], +) diff --git a/ledger/cli-opts/src/main/scala/com/daml/jwt/JwtVerifierConfigurationCli.scala b/ledger/cli-opts/src/main/scala/com/daml/jwt/JwtVerifierConfigurationCli.scala index dea212bc68e..e49412a52d8 100644 --- a/ledger/cli-opts/src/main/scala/com/daml/jwt/JwtVerifierConfigurationCli.scala +++ b/ledger/cli-opts/src/main/scala/com/daml/jwt/JwtVerifierConfigurationCli.scala @@ -18,7 +18,7 @@ object JwtVerifierConfigurationCli { opt[String]("auth-jwt-hs256-unsafe") .optional() .hidden() - .validate(v => Either.cond(v.length > 0, (), "HMAC secret must be a non-empty string")) + .validate(v => Either.cond(v.nonEmpty, (), "HMAC secret must be a non-empty string")) .text( "[UNSAFE] Enables JWT-based authorization with shared secret HMAC256 signing: USE THIS EXCLUSIVELY FOR TESTING" ) diff --git a/ledger/cli-opts/src/test/suite/scala/com/daml/jwt/JwtVerifierConfigurationCliSpec.scala b/ledger/cli-opts/src/test/suite/scala/com/daml/jwt/JwtVerifierConfigurationCliSpec.scala new file mode 100644 index 00000000000..3c690342205 --- /dev/null +++ b/ledger/cli-opts/src/test/suite/scala/com/daml/jwt/JwtVerifierConfigurationCliSpec.scala @@ -0,0 +1,180 @@ +// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.daml.jwt + +import java.math.BigInteger +import java.nio.file.{Files, Path} +import java.security.interfaces.{ECPrivateKey, ECPublicKey, RSAPrivateKey, RSAPublicKey} +import java.security.{KeyPair, KeyPairGenerator, PrivateKey, PublicKey, Security} +import java.time.Instant +import java.util.concurrent.atomic.AtomicReference + +import com.auth0.jwt.JWT +import com.auth0.jwt.algorithms.Algorithm +import com.daml.fs.TemporaryDirectory +import com.daml.jwt.JwtVerifierConfigurationCliSpec._ +import com.daml.ledger.api.auth.ClaimSet.Claims +import com.daml.ledger.api.auth.{AuthService, AuthServiceJWT, AuthServiceWildcard, ClaimPublic} +import com.daml.testing.SimpleHttpServer +import io.grpc.Metadata +import org.bouncycastle.asn1.x500.X500Name +import org.bouncycastle.cert.jcajce.{JcaX509CertificateConverter, JcaX509v3CertificateBuilder} +import org.bouncycastle.jce.provider.BouncyCastleProvider +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder +import org.scalatest.Assertion +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AsyncWordSpec +import scopt.OptionParser + +import scala.concurrent.{ExecutionContext, Future} +import scala.jdk.FutureConverters._ + +class JwtVerifierConfigurationCliSpec extends AsyncWordSpec with Matchers { + Security.addProvider(new BouncyCastleProvider) + + "auth command-line parsers" should { + "parse and configure the authorisation mechanism correctly when `--auth-jwt-hs256-unsafe ` is passed" in { + val secret = "someSecret" + val authService = parseConfig(Array("--auth-jwt-hs256-unsafe", secret)) + val token = JWT.create().sign(Algorithm.HMAC256(secret)) + val metadata = createAuthMetadata(authService, token) + decodeAndCheckMetadata(authService, metadata) + } + + "parse and configure the authorisation mechanism correctly when `--auth-jwt-rs256-crt ` is passed" in + new TemporaryDirectory(getClass.getSimpleName).use { directory => + val (publicKey, privateKey) = newRsaKeyPair() + val certificatePath = newCertificate("SHA256WithRSA", directory, publicKey, privateKey) + val token = JWT.create().sign(Algorithm.RSA256(publicKey, privateKey)) + + val authService = parseConfig(Array("--auth-jwt-rs256-crt", certificatePath.toString)) + val metadata = createAuthMetadata(authService, token) + decodeAndCheckMetadata(authService, metadata) + } + + "parse and configure the authorisation mechanism correctly when `--auth-jwt-es256-crt ` is passed" in + new TemporaryDirectory(getClass.getSimpleName).use { directory => + val (publicKey, privateKey) = newEcdsaKeyPair() + val certificatePath = newCertificate("SHA256WithECDSA", directory, publicKey, privateKey) + val token = JWT.create().sign(Algorithm.ECDSA256(publicKey, privateKey)) + + val authService = parseConfig(Array("--auth-jwt-es256-crt", certificatePath.toString)) + val metadata = createAuthMetadata(authService, token) + decodeAndCheckMetadata(authService, metadata) + } + + "parse and configure the authorisation mechanism correctly when `--auth-jwt-es512-crt ` is passed" in + new TemporaryDirectory(getClass.getSimpleName).use { directory => + val (publicKey, privateKey) = newEcdsaKeyPair() + val certificatePath = newCertificate("SHA512WithECDSA", directory, publicKey, privateKey) + val token = JWT.create().sign(Algorithm.ECDSA512(publicKey, privateKey)) + + val authService = parseConfig(Array("--auth-jwt-es512-crt", certificatePath.toString)) + val metadata = createAuthMetadata(authService, token) + decodeAndCheckMetadata(authService, metadata) + } + + "parse and configure the authorisation mechanism correctly when `--auth-jwt-rs256-jwks ` is passed" in { + val (publicKey, privateKey) = newRsaKeyPair() + val keyId = "test-key-1" + val token = JWT.create().withKeyId(keyId).sign(Algorithm.RSA256(publicKey, privateKey)) + + // Start a JWKS server and create a verifier using the JWKS server + val jwks = KeyUtils.generateJwks( + Map( + keyId -> publicKey + ) + ) + + val server = SimpleHttpServer.start(jwks) + Future { + val url = SimpleHttpServer.responseUrl(server) + val authService = parseConfig(Array("--auth-jwt-rs256-jwks", url)) + val metadata = createAuthMetadata(authService, token) + (authService, metadata) + }.flatMap { case (authService, metadata) => + decodeAndCheckMetadata(authService, metadata) + }.andThen { case _ => + SimpleHttpServer.stop(server) + } + } + } +} + +object JwtVerifierConfigurationCliSpec { + private def parseConfig(args: Array[String]): AuthService = { + val parser = new OptionParser[AtomicReference[AuthService]]("test") {} + JwtVerifierConfigurationCli.parse(parser) { (verifier, config) => + config.set(AuthServiceJWT(verifier)) + config + } + parser.parse(args, new AtomicReference[AuthService](AuthServiceWildcard)).get.get() + } + + private def createAuthMetadata(authService: AuthService, token: String) = { + val metadata = new Metadata() + metadata.put(authService.AUTHORIZATION_KEY, s"Bearer $token") + metadata + } + + private def decodeAndCheckMetadata( + authService: AuthService, + metadata: Metadata, + )(implicit executionContext: ExecutionContext): Future[Assertion] = { + import org.scalatest.Inside._ + import org.scalatest.matchers.should.Matchers._ + + authService.decodeMetadata(metadata).asScala.map { auth => + inside(auth) { case claims: Claims => + claims.claims should be(List(ClaimPublic)) + } + } + } + + private def newRsaKeyPair(): (RSAPublicKey, RSAPrivateKey) = { + val keyPair = newKeyPair("RSA", 2048) + val publicKey = keyPair.getPublic.asInstanceOf[RSAPublicKey] + val privateKey = keyPair.getPrivate.asInstanceOf[RSAPrivateKey] + (publicKey, privateKey) + } + + private def newEcdsaKeyPair(): (ECPublicKey, ECPrivateKey) = { + val keyPair = newKeyPair("ECDSA", 256) + val publicKey = keyPair.getPublic.asInstanceOf[ECPublicKey] + val privateKey = keyPair.getPrivate.asInstanceOf[ECPrivateKey] + (publicKey, privateKey) + } + + private def newKeyPair(algorithm: String, keySize: Int): KeyPair = { + val generator = KeyPairGenerator.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME) + generator.initialize(keySize) + generator.generateKeyPair() + } + + private def newCertificate( + signatureAlgorithm: String, + directory: Path, + publicKey: PublicKey, + privateKey: PrivateKey, + ): Path = { + val now = Instant.now() + val dnName = new X500Name(s"CN=${getClass.getSimpleName}") + val contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(privateKey) + val certBuilder = new JcaX509v3CertificateBuilder( + dnName, + BigInteger.valueOf(now.toEpochMilli), + java.util.Date.from(now), + java.util.Date.from(now.plusSeconds(60)), + dnName, + publicKey, + ) + val certificate = + new JcaX509CertificateConverter() + .setProvider(BouncyCastleProvider.PROVIDER_NAME) + .getCertificate(certBuilder.build(contentSigner)) + val certificatePath = directory.resolve("certificate") + Files.write(certificatePath, certificate.getEncoded) + certificatePath + } +} diff --git a/libs-scala/fs-utils/BUILD.bazel b/libs-scala/fs-utils/BUILD.bazel index b5a25e7ee73..16c38bc0faa 100644 --- a/libs-scala/fs-utils/BUILD.bazel +++ b/libs-scala/fs-utils/BUILD.bazel @@ -11,4 +11,7 @@ da_scala_library( srcs = glob(["src/main/scala/**/*.scala"]), tags = ["maven_coordinates=com.daml:fs-utils:__VERSION__"], visibility = ["//visibility:public"], + deps = [ + "//libs-scala/resources", + ], ) diff --git a/libs-scala/fs-utils/src/main/scala/com/daml/fs/TemporaryDirectory.scala b/libs-scala/fs-utils/src/main/scala/com/daml/fs/TemporaryDirectory.scala new file mode 100644 index 00000000000..6bff1956c67 --- /dev/null +++ b/libs-scala/fs-utils/src/main/scala/com/daml/fs/TemporaryDirectory.scala @@ -0,0 +1,22 @@ +// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.daml.fs + +import java.nio.file.{Files, Path} + +import com.daml.resources.{AbstractResourceOwner, HasExecutionContext, ReleasableResource, Resource} + +import scala.concurrent.Future + +final class TemporaryDirectory[Context: HasExecutionContext](prefix: String) + extends AbstractResourceOwner[Context, Path] { + override def acquire()(implicit context: Context): Resource[Context, Path] = + ReleasableResource(Future { + Files.createTempDirectory(prefix) + })(directory => + Future { + Utils.deleteRecursively(directory) + } + ) +} diff --git a/maven_install_2.13.json b/maven_install_2.13.json index b8edb1dacfa..46c333ef634 100644 --- a/maven_install_2.13.json +++ b/maven_install_2.13.json @@ -1,6 +1,6 @@ { "dependency_tree": { - "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": 1118382420, + "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": -1779008797, "conflict_resolution": {}, "dependencies": [ { @@ -3411,6 +3411,7 @@ "org.unbescape:unbescape:1.1.6.RELEASE", "com.github.scopt:scopt_2.13:4.0.0", "io.gatling:gatling-graphite:3.5.1", + "org.bouncycastle:bcprov-jdk15on:jar:1.70", "net.sf.saxon:Saxon-HE:10.3", "io.netty:netty-tcnative-classes:2.0.46.Final", "org.scala-lang.modules:scala-swing_2.13:3.0.0", @@ -3437,6 +3438,7 @@ "org.jodd:jodd-util:6.0.0", "org.simpleflatmapper:lightning-csv:8.2.3", "org.jodd:jodd-lagarto:6.0.3", + "org.bouncycastle:bcpkix-jdk15on:1.70", "io.netty:netty-handler:4.1.72.Final", "io.netty:netty-resolver:4.1.72.Final", "ch.qos.logback:logback-core:1.2.8", @@ -3444,7 +3446,6 @@ "io.netty:netty-tcnative-boringssl-static:2.0.46.Final", "org.scala-lang:scala-library:2.13.8", "com.fasterxml.jackson.core:jackson-annotations:2.12.0", - "org.bouncycastle:bcprov-jdk15on:jar:1.68", "io.netty:netty-buffer:4.1.72.Final", "io.netty:netty-codec-dns:4.1.58.Final", "io.netty:netty-codec-http2:4.1.72.Final", @@ -3454,10 +3455,10 @@ "com.github.ben-manes.caffeine:caffeine:2.8.0", "org.apache.commons:commons-pool2:2.8.0", "io.netty:netty-codec-http:4.1.72.Final", - "org.bouncycastle:bcpkix-jdk15on:1.68", "io.netty:netty-handler-proxy:4.1.72.Final", "io.netty:netty-codec-socks:4.1.72.Final", "io.gatling:gatling-http:3.5.1", + "org.bouncycastle:bcutil-jdk15on:jar:1.70", "io.netty:netty-codec:4.1.72.Final" ], "directDependencies": [ @@ -3494,7 +3495,10 @@ "com.eatthepath:fast-uuid:jar:sources:0.1", "com.github.scopt:scopt_2.13:jar:sources:4.0.0", "io.netty:netty-codec:jar:sources:4.1.72.Final", + "org.bouncycastle:bcpkix-jdk15on:jar:sources:1.70", + "org.bouncycastle:bcutil-jdk15on:jar:sources:1.70", "org.jodd:jodd-util:jar:sources:6.0.0", + "org.bouncycastle:bcprov-jdk15on:jar:sources:1.70", "org.simpleflatmapper:sfm-util:jar:sources:8.2.3", "com.typesafe.akka:akka-actor_2.13:jar:sources:2.6.18", "io.pebbletemplates:pebble:jar:sources:3.1.4", @@ -3529,11 +3533,9 @@ "net.sf.saxon:Saxon-HE:jar:sources:10.3", "io.netty:netty-codec-http:jar:sources:4.1.72.Final", "io.gatling:gatling-commons-shared:jar:sources:3.5.1", - "org.bouncycastle:bcprov-jdk15on:jar:sources:1.68", "com.google.errorprone:error_prone_annotations:jar:sources:2.9.0", "io.gatling:gatling-commons-shared-unstable:jar:sources:3.5.1", "io.gatling:gatling-charts:jar:sources:3.5.1", - "org.bouncycastle:bcpkix-jdk15on:jar:sources:1.68", "com.typesafe.akka:akka-slf4j_2.13:jar:sources:2.6.18", "io.netty:netty-resolver:jar:sources:4.1.72.Final", "org.scala-lang.modules:scala-java8-compat_2.13:jar:sources:1.0.0", @@ -4902,6 +4904,7 @@ "io.gatling:gatling-commons-shared-unstable:3.5.1", "org.unbescape:unbescape:1.1.6.RELEASE", "com.github.scopt:scopt_2.13:4.0.0", + "org.bouncycastle:bcprov-jdk15on:jar:1.70", "net.sf.saxon:Saxon-HE:10.3", "io.netty:netty-tcnative-classes:2.0.46.Final", "org.scala-lang.modules:scala-swing_2.13:3.0.0", @@ -4924,6 +4927,7 @@ "org.jodd:jodd-util:6.0.0", "org.simpleflatmapper:lightning-csv:8.2.3", "org.jodd:jodd-lagarto:6.0.3", + "org.bouncycastle:bcpkix-jdk15on:1.70", "io.netty:netty-handler:4.1.72.Final", "io.netty:netty-resolver:4.1.72.Final", "ch.qos.logback:logback-core:1.2.8", @@ -4931,16 +4935,15 @@ "io.netty:netty-tcnative-boringssl-static:2.0.46.Final", "org.scala-lang:scala-library:2.13.8", "com.fasterxml.jackson.core:jackson-annotations:2.12.0", - "org.bouncycastle:bcprov-jdk15on:jar:1.68", "io.netty:netty-buffer:4.1.72.Final", "io.netty:netty-codec-dns:4.1.58.Final", "io.netty:netty-codec-http2:4.1.72.Final", "com.github.ben-manes.caffeine:caffeine:2.8.0", "io.netty:netty-codec-http:4.1.72.Final", - "org.bouncycastle:bcpkix-jdk15on:1.68", "io.netty:netty-handler-proxy:4.1.72.Final", "io.netty:netty-codec-socks:4.1.72.Final", "io.gatling:gatling-http:3.5.1", + "org.bouncycastle:bcutil-jdk15on:jar:1.70", "io.netty:netty-codec:4.1.72.Final" ], "directDependencies": [ @@ -4948,9 +4951,9 @@ "com.fasterxml.jackson.core:jackson-databind:2.12.0", "io.gatling:gatling-core:3.5.1", "com.typesafe.akka:akka-actor_2.13:2.6.18", + "org.bouncycastle:bcpkix-jdk15on:1.70", "org.scala-lang:scala-library:2.13.8", "io.netty:netty-codec-http:4.1.72.Final", - "org.bouncycastle:bcpkix-jdk15on:1.68", "io.gatling:gatling-http:3.5.1" ], "file": "v1/https/repo1.maven.org/maven2/io/gatling/gatling-recorder/3.5.1/gatling-recorder-3.5.1.jar", @@ -4980,7 +4983,10 @@ "org.simpleflatmapper:lightning-csv:jar:sources:8.2.3", "com.github.scopt:scopt_2.13:jar:sources:4.0.0", "io.netty:netty-codec:jar:sources:4.1.72.Final", + "org.bouncycastle:bcpkix-jdk15on:jar:sources:1.70", + "org.bouncycastle:bcutil-jdk15on:jar:sources:1.70", "org.jodd:jodd-util:jar:sources:6.0.0", + "org.bouncycastle:bcprov-jdk15on:jar:sources:1.70", "org.simpleflatmapper:sfm-util:jar:sources:8.2.3", "com.typesafe.akka:akka-actor_2.13:jar:sources:2.6.18", "io.pebbletemplates:pebble:jar:sources:3.1.4", @@ -5008,10 +5014,8 @@ "net.sf.saxon:Saxon-HE:jar:sources:10.3", "io.netty:netty-codec-http:jar:sources:4.1.72.Final", "io.gatling:gatling-commons-shared:jar:sources:3.5.1", - "org.bouncycastle:bcprov-jdk15on:jar:sources:1.68", "com.google.errorprone:error_prone_annotations:jar:sources:2.9.0", "io.gatling:gatling-commons-shared-unstable:jar:sources:3.5.1", - "org.bouncycastle:bcpkix-jdk15on:jar:sources:1.68", "com.typesafe.akka:akka-slf4j_2.13:jar:sources:2.6.18", "io.netty:netty-resolver:jar:sources:4.1.72.Final", "org.scala-lang.modules:scala-java8-compat_2.13:jar:sources:1.0.0", @@ -5022,11 +5026,11 @@ "io.netty:netty-resolver-dns:jar:sources:4.1.58.Final" ], "directDependencies": [ + "org.bouncycastle:bcpkix-jdk15on:jar:sources:1.70", "com.typesafe.akka:akka-actor_2.13:jar:sources:2.6.18", "org.scala-lang.modules:scala-swing_2.13:jar:sources:3.0.0", "com.fasterxml.jackson.core:jackson-databind:jar:sources:2.12.0", "io.netty:netty-codec-http:jar:sources:4.1.72.Final", - "org.bouncycastle:bcpkix-jdk15on:jar:sources:1.68", "org.scala-lang:scala-library:jar:sources:2.13.8", "io.gatling:gatling-core:jar:sources:3.5.1", "io.gatling:gatling-http:jar:sources:3.5.1" @@ -7687,56 +7691,90 @@ "url": "https://repo1.maven.org/maven2/org/beanshell/bsh/2.0b4/bsh-2.0b4.jar" }, { - "coord": "org.bouncycastle:bcpkix-jdk15on:1.68", + "coord": "org.bouncycastle:bcpkix-jdk15on:1.70", "dependencies": [ - "org.bouncycastle:bcprov-jdk15on:jar:1.68" + "org.bouncycastle:bcprov-jdk15on:jar:1.70", + "org.bouncycastle:bcutil-jdk15on:jar:1.70" ], "directDependencies": [ - "org.bouncycastle:bcprov-jdk15on:jar:1.68" + "org.bouncycastle:bcprov-jdk15on:jar:1.70", + "org.bouncycastle:bcutil-jdk15on:jar:1.70" ], - "file": "v1/https/repo1.maven.org/maven2/org/bouncycastle/bcpkix-jdk15on/1.68/bcpkix-jdk15on-1.68.jar", + "file": "v1/https/repo1.maven.org/maven2/org/bouncycastle/bcpkix-jdk15on/1.70/bcpkix-jdk15on-1.70.jar", "mirror_urls": [ - "https://repo1.maven.org/maven2/org/bouncycastle/bcpkix-jdk15on/1.68/bcpkix-jdk15on-1.68.jar" + "https://repo1.maven.org/maven2/org/bouncycastle/bcpkix-jdk15on/1.70/bcpkix-jdk15on-1.70.jar" ], - "sha256": "fb8d0f8f673ad6e16c604732093d7aa31b26ff4e0bd9cae1d7f99984c06b8a0f", - "url": "https://repo1.maven.org/maven2/org/bouncycastle/bcpkix-jdk15on/1.68/bcpkix-jdk15on-1.68.jar" + "sha256": "e5b9cb821df57f70b0593358e89c0e8d7266515da9d088af6c646f63d433c07c", + "url": "https://repo1.maven.org/maven2/org/bouncycastle/bcpkix-jdk15on/1.70/bcpkix-jdk15on-1.70.jar" }, { - "coord": "org.bouncycastle:bcpkix-jdk15on:jar:sources:1.68", + "coord": "org.bouncycastle:bcpkix-jdk15on:jar:sources:1.70", "dependencies": [ - "org.bouncycastle:bcprov-jdk15on:jar:sources:1.68" + "org.bouncycastle:bcutil-jdk15on:jar:sources:1.70", + "org.bouncycastle:bcprov-jdk15on:jar:sources:1.70" ], "directDependencies": [ - "org.bouncycastle:bcprov-jdk15on:jar:sources:1.68" + "org.bouncycastle:bcprov-jdk15on:jar:sources:1.70", + "org.bouncycastle:bcutil-jdk15on:jar:sources:1.70" ], - "file": "v1/https/repo1.maven.org/maven2/org/bouncycastle/bcpkix-jdk15on/1.68/bcpkix-jdk15on-1.68-sources.jar", + "file": "v1/https/repo1.maven.org/maven2/org/bouncycastle/bcpkix-jdk15on/1.70/bcpkix-jdk15on-1.70-sources.jar", "mirror_urls": [ - "https://repo1.maven.org/maven2/org/bouncycastle/bcpkix-jdk15on/1.68/bcpkix-jdk15on-1.68-sources.jar" + "https://repo1.maven.org/maven2/org/bouncycastle/bcpkix-jdk15on/1.70/bcpkix-jdk15on-1.70-sources.jar" ], - "sha256": "37f4525a1c26fb73a7ddce148723773f8102b372aaccb4509bd3928a7d8aa877", - "url": "https://repo1.maven.org/maven2/org/bouncycastle/bcpkix-jdk15on/1.68/bcpkix-jdk15on-1.68-sources.jar" + "sha256": "0c9a75e4c10ec82bb1d410fd0787fd15acc718ea59b5dc160278fcee887c0f26", + "url": "https://repo1.maven.org/maven2/org/bouncycastle/bcpkix-jdk15on/1.70/bcpkix-jdk15on-1.70-sources.jar" }, { - "coord": "org.bouncycastle:bcprov-jdk15on:jar:1.68", + "coord": "org.bouncycastle:bcprov-jdk15on:1.70", "dependencies": [], "directDependencies": [], - "file": "v1/https/repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.68/bcprov-jdk15on-1.68.jar", + "file": "v1/https/repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.70/bcprov-jdk15on-1.70.jar", "mirror_urls": [ - "https://repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.68/bcprov-jdk15on-1.68.jar" + "https://repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.70/bcprov-jdk15on-1.70.jar" ], - "sha256": "f732a46c8de7e2232f2007c682a21d1f4cc8a8a0149b6b7bd6aa1afdc65a0f8d", - "url": "https://repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.68/bcprov-jdk15on-1.68.jar" + "sha256": "8f3c20e3e2d565d26f33e8d4857a37d0d7f8ac39b62a7026496fcab1bdac30d4", + "url": "https://repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.70/bcprov-jdk15on-1.70.jar" }, { - "coord": "org.bouncycastle:bcprov-jdk15on:jar:sources:1.68", + "coord": "org.bouncycastle:bcprov-jdk15on:jar:sources:1.70", "dependencies": [], "directDependencies": [], - "file": "v1/https/repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.68/bcprov-jdk15on-1.68-sources.jar", + "file": "v1/https/repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.70/bcprov-jdk15on-1.70-sources.jar", "mirror_urls": [ - "https://repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.68/bcprov-jdk15on-1.68-sources.jar" + "https://repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.70/bcprov-jdk15on-1.70-sources.jar" ], - "sha256": "d9bb57dd73ae7ae3a3b37fcbee6e91ca87156343123d6d3079712928088fb370", - "url": "https://repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.68/bcprov-jdk15on-1.68-sources.jar" + "sha256": "0252e39814e4403b5d91a7386c3a5ac3e1fe65d43c2d25fed8d45e8eebab2696", + "url": "https://repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.70/bcprov-jdk15on-1.70-sources.jar" + }, + { + "coord": "org.bouncycastle:bcutil-jdk15on:jar:1.70", + "dependencies": [ + "org.bouncycastle:bcprov-jdk15on:jar:1.70" + ], + "directDependencies": [ + "org.bouncycastle:bcprov-jdk15on:jar:1.70" + ], + "file": "v1/https/repo1.maven.org/maven2/org/bouncycastle/bcutil-jdk15on/1.70/bcutil-jdk15on-1.70.jar", + "mirror_urls": [ + "https://repo1.maven.org/maven2/org/bouncycastle/bcutil-jdk15on/1.70/bcutil-jdk15on-1.70.jar" + ], + "sha256": "52dc5551b0257666526c5095424567fed7dc7b00d2b1ba7bd52298411112b1d0", + "url": "https://repo1.maven.org/maven2/org/bouncycastle/bcutil-jdk15on/1.70/bcutil-jdk15on-1.70.jar" + }, + { + "coord": "org.bouncycastle:bcutil-jdk15on:jar:sources:1.70", + "dependencies": [ + "org.bouncycastle:bcprov-jdk15on:jar:sources:1.70" + ], + "directDependencies": [ + "org.bouncycastle:bcprov-jdk15on:jar:sources:1.70" + ], + "file": "v1/https/repo1.maven.org/maven2/org/bouncycastle/bcutil-jdk15on/1.70/bcutil-jdk15on-1.70-sources.jar", + "mirror_urls": [ + "https://repo1.maven.org/maven2/org/bouncycastle/bcutil-jdk15on/1.70/bcutil-jdk15on-1.70-sources.jar" + ], + "sha256": "5bc99f2da0436d62ab22a374a77a913f33c91f9c23f06f1fca4d62a66b24d303", + "url": "https://repo1.maven.org/maven2/org/bouncycastle/bcutil-jdk15on/1.70/bcutil-jdk15on-1.70-sources.jar" }, { "coord": "org.brotli:dec:0.1.2",