Optimus.Bank.Extensions.DownstreamEncryption 1.0.6

The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package Optimus.Bank.Extensions.DownstreamEncryption --version 1.0.6
                    
NuGet\Install-Package Optimus.Bank.Extensions.DownstreamEncryption -Version 1.0.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Optimus.Bank.Extensions.DownstreamEncryption" Version="1.0.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Optimus.Bank.Extensions.DownstreamEncryption" Version="1.0.6" />
                    
Directory.Packages.props
<PackageReference Include="Optimus.Bank.Extensions.DownstreamEncryption" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Optimus.Bank.Extensions.DownstreamEncryption --version 1.0.6
                    
#r "nuget: Optimus.Bank.Extensions.DownstreamEncryption, 1.0.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Optimus.Bank.Extensions.DownstreamEncryption@1.0.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Optimus.Bank.Extensions.DownstreamEncryption&version=1.0.6
                    
Install as a Cake Addin
#tool nuget:?package=Optimus.Bank.Extensions.DownstreamEncryption&version=1.0.6
                    
Install as a Cake Tool

Optimus.Bank.Extensions.DownstreamEncryption - Package Design & Integration Guide

This document covers the reusable NuGet package design for Optimus.Bank.Extensions.DownstreamEncryption and how consuming projects integrate with it. Use it as a reference and architectural guide, not a verbatim copy - your service-specific config shapes, helper classes, and endpoint structures will differ per project.


Overview

The encryption infrastructure described in the OptiNotify Downstream Encryption Design Guide has been extracted into a reusable internal NuGet package. Consuming projects get all the cryptographic plumbing for free and only need to provide their own service-specific config classes and API helper classes.

The package is exclusively concerned with PGP encryption, decryption, and secret management. HTTP client implementation, authentication strategies, and API communication are entirely the consuming project's responsibility.


Prerequisites

Every consuming project must:

  1. Reference OptimusCryptic.dll - this is a required local DLL dependency. Add it via a <Reference> in your .csproj:
<ItemGroup>
  <Reference Include="OptimusCryptic">
    <HintPath>..\libs\OptimusCryptic.dll</HintPath>
  </Reference>
</ItemGroup>
  1. Reference the package from the internal NuGet feed:
<ItemGroup>
  <PackageReference Include="Optimus.Bank.Extensions.DownstreamEncryption" Version="1.0.1" />
</ItemGroup>
  1. Add the appropriate Vault Options - see the Vault NuGet package for more info.

Registration API

Optimus.Bank.Extensions.DownstreamEncryption exposes AddDownstreamEncryption(). Call this once in your Program.cs or Startup.cs, passing in IConfiguration:

builder.Services.AddDownstreamEncryption(builder.Configuration);

This registers the encryption infrastructure only. HTTP clients, authentication, and any other API communication concerns are registered separately by the consuming project.


How Consuming Projects Use the Package

The package handles all encryption and secret management. A consuming project needs to provide three things per downstream service:

1. A config class

The only property required from the package is Credentials. Any additional properties - base URLs, endpoints, headers - are entirely up to the consuming project and are not enforced by the package.

// YourProject/Config/PaymentApiConfigs.cs
public class PaymentApiConfigs
{
    [ValidateObjectMembers, Required] public InternalApiCredentials Credentials { get; set; }  // from package

    // Everything below is optional and up to the consuming project
    public string BaseURL { get; set; } = string.Empty;
    public PaymentApiEndpointsConfig Endpoints { get; set; } = new();
}

public class PaymentApiEndpointsConfig
{
    public string InitiatePayment { get; set; } = string.Empty;
    public string GetPaymentStatus { get; set; } = string.Empty;
}

InternalApiCredentials, EncryptedInternalApiPgpConfigs, and KeyVaultOptions all come from the package - no need to redefine them.

2. An appsettings.json block

"PaymentApiConfigs": {
  "BaseURL": "https://your-host/api/v1",
  "Credentials": {
    "PgpConfigs": {
      "EncryptedClientId": "<OptimusCryptic.aesEncrypt output>",
      "EncryptedApiPublicKeyRelativePath": "<OptimusCryptic.aesEncrypt output>",
      "EncryptedServerPrivateKeyPath": "<OptimusCryptic.aesEncrypt output>",
      "EncryptedServerPassphrase": "<OptimusCryptic.aesEncrypt output>",
      "ApiChannelIdHeader": "ClientKey"
    },
    "KeyVaultOptions": {
      "ServiceName": "payment_<your-service-guid>"
    },
    "UseKeyVault": false,
    "EncryptRequest": true

  },
  "Endpoints": {
    "InitiatePayment": "/Payment/Initiate",
    "GetPaymentStatus": "/Payment/Status/{reference}"
  }
}

3. A helper class

The consuming helper takes IEncryptedInternalApiHelperFactory from DI and passes only the credentials into Create(). The factory resolves the correct secret provider internally - ISecretProviderFactory and the concrete providers never appear in consumer code.

HTTP execution is handled by whatever client the consuming project uses - HttpClient, RestSharp, or any other implementation.

// YourProject/Helpers/PaymentApiHelper.cs
public class PaymentApiHelper : IPaymentApiHelper
{
    private readonly ILogger<PaymentApiHelper> _logger;
    private readonly PaymentApiConfigs _config;
    private readonly IEncryptedInternalApiHelper _crypto;

    public PaymentApiHelper(
        IEncryptedInternalApiHelperFactory cryptoFactory,
        ILogger<PaymentApiHelper> logger,
        IOptionsSnapshot<PaymentApiConfigs> config)
    {
        _logger = logger;
        _config = config.Value;

        // Pass credentials only - factory handles provider resolution internally
        _crypto = cryptoFactory.Create(config.Value.Credentials);
    }

    public async Task<ApiResponse<PaymentResponse?>> InitiatePayment(PaymentRequest request, CancellationToken token)
    {
        // Encrypt outgoing body
        var encryptedPayload = await _crypto.EncryptInternalRequests(request, token);
        var body = new { Data = encryptedPayload };

        // HTTP execution is the consuming project's responsibility
        // Use HttpClient, RestSharp, or any other implementation here
        var rawResponse = await _yourHttpClient.PostAsync(...);

        if (string.IsNullOrWhiteSpace(rawResponse?.Data))
            return ApiResponse<PaymentResponse?>.CreateFailure(ResponseCodeInfo.FailedRequest);

        // Decrypt incoming response
        var decrypted = await _crypto.DecryptInternalResponse(rawResponse.Data, token);

        if (string.IsNullOrWhiteSpace(decrypted))
            return ApiResponse<PaymentResponse?>.CreateFailure(ResponseCodeInfo.FailedRequest);

        return JsonConvert.DeserializeObject<ApiResponse<PaymentResponse?>>(decrypted)
            ?? ApiResponse<PaymentResponse?>.CreateFailure(ResponseCodeInfo.FailedRequest);
    }
}

The package's surface area in consuming code is limited to IEncryptedInternalApiHelperFactory, IEncryptedInternalApiHelper, InternalApiCredentials, and the config model types. Everything else is the consuming project's concern.


Full DI Registration Example (Consuming Project)

// Program.cs

// 1. Register package encryption infrastructure
builder.Services.AddDownstreamEncryption(builder.Configuration);

// 2. Bind your service-specific config
builder.Services.AddOptions<PaymentApiConfigs>()
    .BindConfiguration("PaymentApiConfigs")
    .ValidateDataAnnotations()
    .ValidateOnStart();

// 3. Register your helper
builder.Services.AddScoped<IPaymentApiHelper, PaymentApiHelper>();

// 4. Register your own HTTP client and auth - not the package's concern
// e.g. builder.Services.AddHttpClient<IPaymentApiHelper, PaymentApiHelper>();

IEncryptedInternalApiHelper is not registered in DI. It is produced exclusively by IEncryptedInternalApiHelperFactory.Create() and held as a field by the consuming helper. Registering it in DI would imply a single shared instance, which would break per-service credential isolation.


Switching Key Providers at Runtime

No code changes are needed. The provider is determined entirely by the UseKeyVault flag in appsettings.json for each service independently:

// Service A - file-based keys (dev/staging)
"KeyVaultOptions": { "UseKeyVault": false,"EncryptRequest": "true", "ServiceName": "account_..." }

// Service B - Key Vault (production)
"KeyVaultOptions": { "UseKeyVault": true, "EncryptRequest": "true", "ServiceName": "payment_..." }

// Service C - None (Unencrypted option)
"KeyVaultOptions": { "UseKeyVault": true, "EncryptRequest": "false", "ServiceName": "payment_..." }

You can have two services in the same project using different providers simultaneously. IEncryptedInternalApiHelperFactory.Create(credentials) resolves the correct provider per instance at construction time based on the credentials passed in.


Adding a New Downstream Service (Checklist)

For each new encrypted downstream API a project needs to integrate with:

  • Add a config class with InternalApiCredentials as the only required property from the package
  • Add the appsettings.json block with OptimusCryptic-encrypted values under Credentials
  • Add an AddOptions<YourConfig>() binding in Program.cs
  • Create a helper class that takes IEncryptedInternalApiHelperFactory and calls Create(config.Value.Credentials)
  • Wire up your own HTTP client implementation in the helper
  • Register your helper in DI
  • Place PGP key files in the configured relative path (file mode) or register secrets in Key Vault (KV mode)

No changes to the package are needed.


Handling Encrypted vs Unencrypted Requests

A new configuration property, EncryptRequest, has been introduced within the credentials. This boolean flag determines whether a request should be encrypted before being sent.

The IEncryptedInternalApiHelper interface provides a method that exposes the value of this property, allowing consumers to dynamically decide how to process outgoing requests.

Implementation Guidance

Before sending a request, you should:

  1. Check the value of EncryptRequest via the helper.
  2. Based on the result:
    • If true: Proceed with encrypting the request payload.
    • If false: Send the request without encryption.

This approach ensures flexibility and allows the system to support both encrypted and unencrypted request flows seamlessly, depending on the configured credentials.

Example (Pseudo-Logic)


if (_crypto.UseEncryption)
{
    // Encrypt and send request
}
else
{
    // Send request as-is without encryption and return a response
}

Package Versioning & Ownership

Concern Recommendation
Breaking interface changes Increment major version (2.0.0)
New providers (e.g. HashiCorp Vault) New package Optimus.Bank.Extensions.DownstreamEncryption.Vault following the same pattern
OptimusCryptic updates Consuming projects update their local DLL reference independently; no package change needed unless the call signature changes
IVaultService updates Update the vault package reference and re-publish

Security Notes

  • All Encrypted* config values are produced by OptimusCryptic.aesEncrypt and decrypted at runtime by OptimusCryptic.aesDecrypt. The AES key underpinning OptimusCryptic must be protected separately (environment variable or secrets manager) - it is the root secret for all config-level encryption.
  • SecretLoader reads from disk on every call and caches.
  • KeyVaultSecretProvider caches in IMemoryCache only - never Redis or any distributed store.
  • Cache keys in KeyVaultSecretProvider are namespaced by serviceName, so multiple services in the same process remain isolated.
  • IEncryptedInternalApiHelper is not registered in DI intentionally - see the note in the DI Registration section above.
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated