Skip to content
English
  • There are no suggestions because the search field is empty.

Securing SurePassID Configuration Files

 Encrypting web.config Secrets (PKCS12 and DPAPI)

This guide explains how to encrypt sensitive sections of SurePassIdp\web.config (appSettings, connectionStrings, and SMTP credentials) so that secrets are never stored in clear text on disk.

Two providers are covered:

Provider Single server Server farm When to use
PKCS12 (certificate based) Yes Yes On-prem / VM deployments, especially farms. The same encrypted web.config works on every node that has the certificate installed.
DPAPI (machine based) Yes No Quick single-server deployments only. Keys are bound to the machine, so an encrypted file cannot be moved to another server.

Azure-hosted deployments should continue to use the existing Azure Key Vault configuration path instead of either provider below. Unless you are in an air-gapped environment you can always user an Azure Key Vault in addition the the options below.

At runtime no application code changes are required — ASP.NET transparently decrypts the protected sections when the app reads them via ConfigurationManager.AppSettings[...] / ConnectionStrings[...].

The sections you should encrypt in this application:

  • appSettings — contains Connection.Password, System.Key, System.IV, System.Salt, EAM client secrets, etc.
  • appSecrets — in production a dedicated section that holds only the sensitive keys (see "Choosing what to encrypt" below).
  • connectionStrings — if/when a <connectionStrings> section is added.
  • system.net/mailSettings/smtp — contains the automailer SMTP password.

Choosing What to Encrypt: Whole appSettings vs. an appSecrets-Only Section

You have two options for how much of the configuration to encrypt. Both use the exact same providers and aspnet_regiis commands described later — the only difference is the name of the section you pass on the command line.

Option A — Encrypt the entire appSettings section

Encrypt appSettings as-is, leaving all keys where they are.

  • Pros: Simplest; nothing to reorganize. Every value is protected.
  • Cons: Once encrypted, the whole section is an opaque <EncryptedData> blob. To change any value — even a non-sensitive one like a support URL, a template name, or a trace flag — an operator must decrypt → edit → re-encrypt → redeploy. That is slow, error-prone, and requires the certificate/keys to be present just to tweak a harmless setting.

Option B — Move secrets into a dedicated appSecrets section and encrypt only that (recommended)

Keep everyday, non-sensitive settings in the normal appSettings section (in clear text, freely editable), and move only the sensitive keys into a separate custom <appSecrets> section. Then encrypt only appSecrets.

  • Pros: Operators can edit non-sensitive settings in appSettings instantly — no decrypt/re-encrypt/redeploy cycle, no certificate needed just to change a URL or flag. The blast radius of encryption is limited to true secrets.
  • Cons: One-time setup to declare the appSecrets section and relocate the sensitive keys.

Any setting can be moved between appSettings and appSecrets freely — the choice of which keys are "sensitive" is yours. Typical candidates for appSecrets: Connection.Password, System.Key, System.IV, System.Salt, Eam.*.ClientSecret, and the SMTP password. Everything else (support URLs, template names, trace flags, cache settings, feature toggles) can stay in appSettings.

In this repository's production web.config, the <appSecrets> section already exists and holds the sensitive keys; this dev sample keeps everything in appSettings.

Setting up an appSecrets section (one-time, for Option B)

appSecrets is a custom section, so unlike appSettings it must be declared in <configSections>. Reuse the built-in AppSettingsSection type so it behaves exactly like appSettings (same <add key="" value="" /> syntax) and is readable the same way:

<configSections>
<section name="appSecrets"
type="System.Configuration.AppSettingsSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
requirePermission="false" />
</configSections>

<!-- Non-sensitive, freely editable without re-encryption -->
<appSettings>
<add key="Support.HelpWebSiteURL" value="https://www.surepassid.com" />
<add key="Server.Trace" value="1" />
<!-- ...other non-sensitive settings... -->
</appSettings>

<!-- Sensitive; this is the ONLY section you encrypt -->
<appSecrets>
<add key="Connection.Password" value="..." />
<add key="System.Key" value="..." />
<add key="System.IV" value="..." />
<add key="System.Salt" value="..." />
</appSecrets>

Reading appSecrets in code (only relevant if you introduce the section here): because it uses AppSettingsSection, values are retrieved with ((System.Collections.Specialized.NameValueCollection)ConfigurationManager.GetSection("appSecrets"))["System.Key"]. The application's existing configuration accessor should fall back across both sections; confirm the production accessor already does this before relocating keys.

Which section name to pass to aspnet_regiis

Everything else in this guide is identical — just substitute the section name:

You chose Section name to encrypt
Option A (whole appSettings) appSettings
Option B (secrets only) appSecrets

For example, with the PKCS12 provider:

# Option A
aspnet_regiis -pef "appSettings" "<folder>" -prov "Pkcs12Provider"

# Option B (recommended)
aspnet_regiis -pef "appSecrets" "<folder>" -prov "Pkcs12Provider"

1. PKCS12 (Certificate-Based) Encryption — Recommended for Farms

This uses the Pkcs12ProtectedConfigurationProvider included in this solution (Submodules\SurePassIdDotNetLibs\PKCS12ProtectedConfigurationProvider). Encryption is performed with an X.509 certificate's RSA key pair; any node holding the certificate (and whose app pool identity can read its private key) can decrypt the file.

1.1 Important certificate requirement (read first)

The provider casts the certificate key to RSACryptoServiceProvider (a legacy CSP key). A certificate created with the default modern CNG key storage provider (for example a plain New-SelfSignedCertificate) will throw an InvalidCastException at decrypt time. You must create the certificate with a legacy CSP:

# Run in an elevated PowerShell prompt.
$cert = New-SelfSignedCertificate `
-Subject "CN=SurePassConfig" `
-CertStoreLocation "Cert:\LocalMachine\My" `
-KeyExportPolicy Exportable `
-KeySpec KeyExchange `
-Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" `
-KeyLength 2048 `
-NotAfter (Get-Date).AddYears(10)

$cert.Thumbprint # note this value; you need it for web.config

-Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" forces a legacy CSP key that is compatible with RSACryptoServiceProvider. -KeySpec KeyExchange is required so the key can be used for encryption/decryption.

1.2 Register the provider in web.config

Add a <configProtectedData> section (see the ready-to-use block already added to web.config, described in section 3). Set thumbprint to the value from step 1.1. Remove any spaces from the thumbprint and use it as-is (case-insensitive):

<configProtectedData>
<providers>
<add name="Pkcs12Provider"
thumbprint="REPLACE_WITH_CERT_THUMBPRINT"
storeLocation="LocalMachine"
type="Pkcs12ProtectedConfigurationProvider.Pkcs12ProtectedConfigurationProvider, Pkcs12ProtectedConfigurationProvider" />
</providers>
</configProtectedData>
  • storeLocation accepts LocalMachine (default, recommended for IIS) or CurrentUser.
  • The type uses the assembly name Pkcs12ProtectedConfigurationProvider. Ensure that DLL is deployed to the app's bin folder (it is referenced by the IdP project).

1.3 Grant the app pool identity access to the private key

The IIS application pool account must be able to read the certificate's private key:

  1. Open certlm.msc (Local Machine certificates).
  2. Navigate to Personal → Certificates, select the CN=SurePassConfig cert.
  3. Right-click → All Tasks → Manage Private Keys…
  4. Add the app pool identity (for example IIS AppPool\SurePassIdp) with Read permission.

1.4 Encrypt the sections

Run from an elevated prompt. -pef takes a section name and the folder that contains web.config (not the file itself). Use appSecrets instead of appSettings if you followed Option B (secrets-only section) above:

cd C:\Windows\Microsoft.NET\Framework64\v4.0.30319\

# Option A: encrypt the whole appSettings section
.\aspnet_regiis.exe -pef "appSettings" "C:\Path\To\SurePassIdp" -prov "Pkcs12Provider"

# Option B (recommended): encrypt only the secrets section, leaving appSettings editable
.\aspnet_regiis.exe -pef "appSecrets" "C:\Path\To\SurePassIdp" -prov "Pkcs12Provider"

.\aspnet_regiis.exe -pef "connectionStrings" "C:\Path\To\SurePassIdp" -prov "Pkcs12Provider"
.\aspnet_regiis.exe -pef "system.net/mailSettings/smtp" "C:\Path\To\SurePassIdp" -prov "Pkcs12Provider"

1.5 Decrypt (to edit later)

.\aspnet_regiis.exe -pdf "appSettings" "C:\Path\To\SurePassIdp"
.\aspnet_regiis.exe -pdf "connectionStrings" "C:\Path\To\SurePassIdp"
.\aspnet_regiis.exe -pdf "system.net/mailSettings/smtp" "C:\Path\To\SurePassIdp"

The provider name is not required when decrypting — it is read from the encrypted section.

1.6 Farm rollout

  1. Export the certificate including the private key to a password-protected .pfx:
    Export-PfxCertificate -Cert $cert `
    -FilePath "C:\keys\surepass-config.pfx" `
    -Password (Read-Host -AsSecureString)
  2. Import the same .pfx into Cert:\LocalMachine\My on every farm node (preserving the legacy CSP key):
    Import-PfxCertificate -FilePath "C:\keys\surepass-config.pfx" `
    -CertStoreLocation "Cert:\LocalMachine\My" `
    -Password (Read-Host -AsSecureString)
  3. Grant the app pool identity Read access to the private key on each node (section 1.3).
  4. Encrypt web.config once (section 1.4) and deploy the same encrypted file to all nodes. Because every node shares the certificate, they can all decrypt it.

Store the .pfx and its password in a secure secrets store, and delete the local copy after import. Anyone with the .pfx can decrypt the configuration.


2. DPAPI (Machine-Based) Encryption — Single Server Only

DPAPI uses the built-in DataProtectionConfigurationProvider. Keys are derived from the local machine, so this is the simplest option but the encrypted file only works on the machine where it was encrypted. Do not use DPAPI for a farm or when the same config file is copied between servers.

2.1 Encrypt

Run on the target server itself, from an elevated prompt:

cd C:\Windows\Microsoft.NET\Framework64\v4.0.30319\

# Option A: whole appSettings, or Option B: substitute "appSecrets" for "appSettings"
.\aspnet_regiis.exe -pef "appSettings" "C:\Path\To\SurePassIdp" -prov "DataProtectionConfigurationProvider"
.\aspnet_regiis.exe -pef "connectionStrings" "C:\Path\To\SurePassIdp" -prov "DataProtectionConfigurationProvider"
.\aspnet_regiis.exe -pef "system.net/mailSettings/smtp" "C:\Path\To\SurePassIdp" -prov "DataProtectionConfigurationProvider"

DataProtectionConfigurationProvider is registered by the .NET Framework by default and uses the machine key store (any account on that machine can decrypt), so no app pool permission change is required.

2.2 Decrypt

.\aspnet_regiis.exe -pdf "appSettings" "C:\Path\To\SurePassIdp"

2.3 DPAPI limitations

  • Not portable. A config file encrypted on Server A cannot be decrypted on Server B. Deploy the plaintext (or transformed) config, then run -pef on each server.
  • Not farm-safe. Every node would need its own separate encryption pass, and shared deployment of a single encrypted file will fail on all but the originating machine.
  • Backups/disaster recovery must re-encrypt on the restored machine.

3. web.config Changes Made to Support This

A commented <configProtectedData> provider block for the PKCS12 provider has been added to SurePassIdp\web.config. It is commented out by default so the app builds and runs unchanged until you are ready to encrypt. To enable PKCS12 encryption:

  1. Create the certificate (section 1.1) and note the thumbprint.
  2. Uncomment the <configProtectedData> block and set the thumbprint attribute.
  3. Grant the app pool Read access to the private key (section 1.3).
  4. Encrypt the sections (section 1.4).

For DPAPI you do not need the provider block at all — reference the built-in DataProtectionConfigurationProvider directly on the aspnet_regiis command line (section 2.1).


4. Verifying Encryption

After encrypting, the protected section in web.config looks similar to:

<appSettings configProtectionProvider="Pkcs12Provider">
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" ...>
<CipherData>
<CipherValue>...base64...</CipherValue>
</CipherData>
</EncryptedData>
</appSettings>

Confirm the application still starts and can read settings (log in, send a test email). If startup fails with a decryption error, check:

  • The certificate is present in the configured store/location and thumbprint matches.
  • The certificate was created with a legacy CSP key (section 1.1), not CNG.
  • The app pool identity has Read on the private key.

5. Quick Reference
Task Command
Encrypt whole appSettings (PKCS12) aspnet_regiis -pef "appSettings" "<folder>" -prov "Pkcs12Provider"
Encrypt secrets-only section (PKCS12) aspnet_regiis -pef "appSecrets" "<folder>" -prov "Pkcs12Provider"
Encrypt (DPAPI) aspnet_regiis -pef "appSettings" "<folder>" -prov "DataProtectionConfigurationProvider"
Decrypt (either) aspnet_regiis -pdf "appSettings" "<folder>" (or "appSecrets")
Create legacy-CSP cert New-SelfSignedCertificate ... -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" -KeySpec KeyExchange
Export cert (farm) Export-PfxCertificate -Cert $cert -FilePath <pfx> -Password <secure>
Import cert (farm) Import-PfxCertificate -FilePath <pfx> -CertStoreLocation Cert:\LocalMachine\My -Password <secure>

aspn