Skip to content

Conversation

@guptapratykshh
Copy link
Contributor

Fixes #3611

Removed the static key.pem and cert.pem files from io/shared/src/test/resources to verify we are no longer relying on committed private keys , implemented ephemeral certificate generation for Scala Native using the openssl CLI. This generates a proper Root CA and signs a Server Certificate with strict RFC 5280 extensions (SKI, AKI, KeyUsage) to satisfy s2n-tls validation requirements and implemented ephemeral certificate generation for Scala.js using openssl (via fs2.io.process), ensuring we can run TLS tests on Node.js without the deleted static files.

@guptapratykshh guptapratykshh changed the title fix: ensure Topic#publish1 returns accurate result during concurrent closure fixed Private key and certificates on repo Jan 18, 2026
@guptapratykshh guptapratykshh force-pushed the fix/ephemeral-certificates-issue-3611 branch 3 times, most recently from c718b27 to be002a5 Compare January 19, 2026 05:10
@guptapratykshh guptapratykshh force-pushed the fix/ephemeral-certificates-issue-3611 branch from be002a5 to 00b9144 Compare January 19, 2026 05:39
@guptapratykshh
Copy link
Contributor Author

@mpilquist can you have a look at this PR , Thanks.

@@ -0,0 +1,94 @@
/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note this file is JVM specific but name still has Js in it.

*/
def createProvider: IO[TestCertificateProvider] =
// This will be implemented differently for each platform
PlatformSpecificCertificateProvider.create
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the implementations all shell out to openssl, why do they need to be platform specific? If instead we used, e.g., JCA on the JVM, node-forge on JS, and libopenssl on native, then I could see doing the platform independent approach.

/** Cached certificate provider to avoid regenerating for each test
*/
private lazy val cachedProvider: IO[TestCertificateProvider] =
Ref.of[IO, Option[TestCertificateProvider]](None).flatMap { ref =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will create a new Ref for each use of cachedProvider, defeating the attempt at caching.

}

test("mTLS client verification fails if client cannot authenticate") {
test("mTLS client verification fails if client cannot authenticate".ignore) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover from debugging?


def getCertificatePair: IO[TestCertificateProvider.CertificatePair] =
Files[IO].tempDirectory.use { tempDir =>
val caKey = tempDir / "ca_key.pem"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to create a CA key/certificate. The generated certificate can be self signed. That will simplify this whole function. The self-signed cert + private key should be used as a client/server identity and the self-signed cert should be used in trust stores.


/** Represents a certificate-key pair for testing
*/
case class CertificatePair(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only need to return a single self-signed certificate and private key. Should also document the format that's returned (e.g. PEM, etc)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also rename this to be CertificateAndPrivateKey


/** Creates a provider using OpenSSL (available on all supported platforms).
*/
def createProvider: IO[TestCertificateProvider] =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd remove this provider concept and just do this:

object TestCertificateProvider {
  private val cachedValue: AtomicCell[IO, Option[CertificateAndPrivateKey]] = AtomicCell.of(None).unsafeRunSync()

  def getCertificateAndPrivateKey: IO[CertificateAndPrivateKey] = {
    cachedValue.evalUpdate {
      case Some(cached) => IO.pure(cached)
      case None => generateCertificateAndPrivateKey
    }
  }

  private def generateCertificateAndPrivateKey: IO[CertificateAndPrivateKey] = ???
}

@guptapratykshh guptapratykshh force-pushed the fix/ephemeral-certificates-issue-3611 branch 3 times, most recently from 82b6cb1 to 20a2ca7 Compare January 25, 2026 15:11
@guptapratykshh guptapratykshh force-pushed the fix/ephemeral-certificates-issue-3611 branch from 20a2ca7 to d7ae073 Compare January 25, 2026 15:13
privateKeyString: String
)

private val cachedValue: AtomicReference[Option[CertificateAndPrivateKey]] =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my other comment about using cats.effect.std.AtomicCell which simplifies this caching logic.

@guptapratykshh guptapratykshh force-pushed the fix/ephemeral-certificates-issue-3611 branch from 5052da6 to fa569f9 Compare January 25, 2026 16:05
@guptapratykshh
Copy link
Contributor Author

i have tested locally all these tests are passing , but cli si failing

@mpilquist
Copy link
Member

Looks like the -addext switch was causing openssl to segfault sometimes.

@mpilquist
Copy link
Member

@guptapratykshh Take a look at the 2 commits from today for some further simplifications. AtomicCell is missing both an unsafe constructor (like Ref has) and an in constructor (like Mutex has) so I replaced it with a Ref+Mutex to avoid the complicated initialization. I also added an API to S2nConfig to allow specifying a cert+key with a PEM string instead of only via binary, which removed the need for carrying both formats in the value returned from TestCertificateProvider.

@mpilquist mpilquist merged commit cd22180 into typelevel:main Jan 28, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Private key and certificates on repo

2 participants