Skip to content

Add support for pkcs12 format certificate trust bundles to enable trust in Java service scenarios#14756

Open
danegsta wants to merge 4 commits intorelease/13.2from
danegsta/pkcs12
Open

Add support for pkcs12 format certificate trust bundles to enable trust in Java service scenarios#14756
danegsta wants to merge 4 commits intorelease/13.2from
danegsta/pkcs12

Conversation

@danegsta
Copy link
Member

@danegsta danegsta commented Feb 27, 2026

Description

Adds support for generating PKCS#12 format certificate trust store bundles, enabling Java-based container resources (e.g., Kafka, Elasticsearch) to consume trusted CA certificates via javax.net.ssl.trustStore.

Certificate trust currently only produces PEM artifacts (individual files and bundles). Java uses PKCS#12 (or JKS) format files for its trust stores. This change adds PKCS#12 as an additional output format, following the tracked-reference pattern already used for HTTPS termination certificates — the PKCS#12 trust store is only generated if a resource's WithCertificateTrustConfiguration callback actually references the Pkcs12BundlePath path.

Changes

  • CertificateTrustExecutionConfigurationContext: Added Pkcs12BundlePath and Pkcs12BundlePassword properties so the DCP executor can specify where the PKCS#12 trust store will be written
  • CertificateTrustExecutionConfigurationData: Added a TrackedReference inner class (same pattern as HttpsCertificateExecutionConfigurationData) with Pkcs12BundlePathReference, IsPkcs12BundlePathReferenced, and Pkcs12BundlePassword — enabling conditional generation
  • CertificateTrustConfigurationCallbackAnnotationContext: Added Pkcs12BundlePath and Pkcs12BundlePassword so resource integration callbacks can reference the trust store path and password
  • DcpExecutor: Added CreatePkcs12TrustStore helper; updated both the executable and container certificate flows to provide and conditionally generate truststore.p12 when the PKCS#12 path is referenced
  • Tests: Added tests for PKCS#12 tracking behavior and trust store generation

Example usage

builder.AddContainer("kafka", "confluentinc/cp-kafka:latest")
    .WithCertificateTrustConfiguration(ctx =>
    {
        ctx.EnvironmentVariables["KAFKA_SSL_TRUSTSTORE_LOCATION"] = ctx.Pkcs12BundlePath;
        ctx.EnvironmentVariables["KAFKA_SSL_TRUSTSTORE_PASSWORD"] = ctx.Pkcs12BundlePassword;
        ctx.EnvironmentVariables["KAFKA_SSL_TRUSTSTORE_TYPE"] = "PKCS12";
        return Task.CompletedTask;
    });

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • The PKCS#12 trust store contains only public certificates (no private keys). The default password is an empty string. This is consistent with trust-only stores and follows the same security model as the existing PEM trust bundle.
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
  • Does the change require an update in our Aspire docs?

@danegsta danegsta requested a review from mitchdenny as a code owner February 27, 2026 00:44
Copilot AI review requested due to automatic review settings February 27, 2026 00:44
@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 14756

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 14756"

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request adds support for generating PKCS#12 format certificate trust store bundles to enable Java-based services (e.g., Kafka, Elasticsearch) to consume trusted CA certificates via javax.net.ssl.trustStore. The implementation follows the existing tracked-reference pattern used for HTTPS termination certificates, ensuring the PKCS#12 trust store is only generated when a resource's configuration callback actually references the bundle path.

Changes:

  • Added PKCS#12 bundle path and password properties to certificate trust configuration contexts, enabling Java services to reference trust stores in their preferred format
  • Implemented lazy generation of PKCS#12 trust stores using the tracked reference pattern, ensuring bundles are only created when actually needed
  • Extended both executable and container resource flows in DcpExecutor to conditionally generate and deploy truststore.p12 files when referenced
  • Added comprehensive tests covering tracked reference behavior, PKCS#12 generation, and password handling

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/Aspire.Hosting/ApplicationModel/CertificateTrustConfigurationCallbackAnnotation.cs Added public API properties Pkcs12BundlePath and Pkcs12BundlePassword to callback context for Java service integration
src/Aspire.Hosting/ApplicationModel/CertificateTrustExecutionConfigurationGatherer.cs Implemented tracked reference pattern for PKCS#12 bundle path and added password property to execution configuration data and context
src/Aspire.Hosting/Dcp/DcpExecutor.cs Added CreatePkcs12TrustStore helper method and integrated conditional PKCS#12 generation in both executable and container resource creation flows
tests/Aspire.Hosting.Tests/ExecutionConfigurationGathererTests.cs Added comprehensive test coverage for PKCS#12 tracking behavior, password handling, and trust store generation
Comments suppressed due to low confidence (4)

src/Aspire.Hosting/ApplicationModel/CertificateTrustConfigurationCallbackAnnotation.cs:92

  • The new public API properties Pkcs12BundlePath and Pkcs12BundlePassword need more comprehensive documentation. According to the XML documentation standards for public APIs in Aspire, you should:
  1. Add a <remarks> section explaining when and how to use these properties, particularly noting that the PKCS#12 bundle is only generated if the path is referenced
  2. Add an <example> section showing practical usage, similar to the existing examples for Arguments and EnvironmentVariables properties above (lines 37-70)
  3. For Pkcs12BundlePassword, consider documenting the default value and security considerations

Compare with existing properties like Arguments (lines 33-49) and EnvironmentVariables (lines 51-71) which include both <remarks> and <example> tags demonstrating proper public API documentation.

    /// <summary>
    /// A value provider that will resolve to a path to a PKCS#12 trust store bundle.
    /// Referencing this path in a callback will trigger generation of the PKCS#12 trust store.
    /// </summary>
    public required ReferenceExpression Pkcs12BundlePath { get; init; }

    /// <summary>
    /// The password for the PKCS#12 trust store bundle. Defaults to an empty string.
    /// </summary>
    public required string Pkcs12BundlePassword { get; init; }

src/Aspire.Hosting/ApplicationModel/CertificateTrustExecutionConfigurationGatherer.cs:223

  • The documentation for the new Pkcs12BundlePath property is too brief for a public API. According to Aspire's XML documentation standards, public APIs require:
  1. A more detailed <summary> explaining what the PKCS#12 trust store contains and its purpose (e.g., "contains trusted CA certificates in PKCS#12 format for Java-based services")
  2. A <remarks> section explaining the lazy generation behavior (only generated when referenced) and providing usage context
  3. An <example> section showing how to use this with Java services (e.g., setting javax.net.ssl.trustStore environment variables)

For reference, see the existing CertificateBundlePath property documentation pattern, but note that this is a new format that requires more explanation than the PEM bundle which is more commonly known.

    /// <summary>
    /// The path to the PKCS#12 trust store bundle file in the resource context (e.g., container filesystem).
    /// Only generated if a resource's certificate trust configuration callback references this path.
    /// </summary>
    public required ReferenceExpression Pkcs12BundlePath { get; init; }

src/Aspire.Hosting/ApplicationModel/CertificateTrustExecutionConfigurationGatherer.cs:228

  • The Pkcs12BundlePassword property documentation should be more comprehensive for a public API. Consider:
  1. Explaining why the default is an empty string (common for trust-only stores)
  2. Clarifying that this password protects the file but the certificates inside are public (no private keys)
  3. Adding a <remarks> section with security context

Compare with line 315 in ContainerFileSystemCallbackAnnotation.cs which provides a <summary> that explains when the password might be null. This property should similarly explain the default behavior and security implications.

    /// <summary>
    /// The password for the PKCS#12 trust store bundle. Defaults to an empty string.
    /// </summary>
    public string Pkcs12BundlePassword { get; init; } = string.Empty;

src/Aspire.Hosting/Dcp/DcpExecutor.cs:2919

  • While this is an internal method, the documentation is overly verbose according to Aspire's documentation standards. Internal APIs should have brief, concise <summary> tags only. The current documentation includes details that would be appropriate for a public API but are unnecessary for internal code.

Consider simplifying to something like:

/// <summary>
/// Creates a PKCS#12 trust store from the specified certificates (public keys only).
/// </summary>

The detailed explanation about Java compatibility and javax.net.ssl.trustStore usage is implementation detail that doesn't need to be in the XML docs for an internal method.

    /// <summary>
    /// Creates a PKCS#12 trust store containing the specified certificates (public keys only, no private keys).
    /// The resulting trust store is compatible with Java's keytool and can be used as a javax.net.ssl.trustStore.
    /// </summary>
    internal static byte[] CreatePkcs12TrustStore(X509Certificate2Collection certificates, string password)

@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

🎬 CLI E2E Test Recordings

The following terminal recordings are available for commit 244d828:

Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View Recording
AddPackageWhileAppHostRunningDetached ▶️ View Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
AgentInitCommand_WithMalformedMcpJson_ShowsErrorAndExitsNonZero ▶️ View Recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View Recording
Banner_DisplayedOnFirstRun ▶️ View Recording
Banner_DisplayedWithExplicitFlag ▶️ View Recording
CreateAndDeployToDockerCompose ▶️ View Recording
CreateAndDeployToDockerComposeInteractive ▶️ View Recording
CreateAndPublishToKubernetes ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateEmptyAppHostProject ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateStartWaitAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DescribeCommandResolvesReplicaNames ▶️ View Recording
DescribeCommandShowsRunningResources ▶️ View Recording
DetachFormatJsonProducesValidJson ▶️ View Recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
LogsCommandShowsResourceLogs ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View Recording
SecretCrudOnDotNetAppHost ▶️ View Recording
SecretCrudOnTypeScriptAppHost ▶️ View Recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View Recording
StopAllAppHostsFromAppHostDirectory ▶️ View Recording
StopAllAppHostsFromUnrelatedDirectory ▶️ View Recording
StopNonInteractiveMultipleAppHostsShowsError ❌ Upload failed
StopNonInteractiveSingleAppHost ▶️ View Recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View Recording

📹 Recordings uploaded automatically from CI run #22505836544

@danegsta
Copy link
Member Author

@marshalhayes helped verify against a real live Java project; this should allow for enabling certificate trust for Java services in the community toolkit as part of 13.2.

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.

2 participants