PGP Encrypt & Decrypt — C# source
Encrypt or decrypt messages with PGP public/private keys. Powered by OpenPGP.js, runs entirely in your browser.
This is the C# implementation — the same logic the interactive tool runs, in a shareable, citable form.
// PGP Encrypt & Decrypt — read OpenPGP key metadata, encrypt (optionally
// signing), decrypt.
// C# 12 / .NET 8 — ported from src/lib/pgp-encrypt.ts (the canonical
// TypeScript implementation). Display source for CosmoDev's polyglot pages.
//
// .NET has no OpenPGP implementation in the BCL, so this port drives the
// `gpg` binary (the same engine the C port reaches through GPGME and the
// native counterpart to the TS reference's openpgp.js). Three entry points,
// matching the TS public API one-for-one:
// ReadKeyInfo — user ID, uppercase fingerprint, algorithm, creation date,
// expiry (null when the key never expires), and whether the
// armor holds a private key.
// PgpEncrypt — ASCII-armored PGP message for a recipient's public key,
// optionally signed with the sender's private key.
// PgpDecrypt — plaintext from an armored message + private key.
//
// Armored key material is imported into a fresh 0700 GNUPGHOME under a temp
// directory that is deleted on the way out, so nothing is written to the
// user's real keyring — the C# equivalent of the TS island keeping every
// operation client-side.
//
// gpg is always spawned with an argument ARRAY (ProcessStartInfo.ArgumentList,
// never a command string), so no key, message or passphrase is ever parsed by
// a shell. Messages and armored keys travel via stdin/stdout, passphrases via
// --passphrase with --pinentry-mode loopback --batch.
using System.Diagnostics;
using System.Text.RegularExpressions;
/// <summary>Metadata extracted from one armored PGP key.</summary>
public sealed record PGPKeyInfo(
string UserId,
string Fingerprint,
string Algorithm,
/// <summary>ISO yyyy-MM-dd.</summary>
string CreationDate,
/// <summary>ISO yyyy-MM-dd, null when the key never expires.</summary>
string? Expiry,
bool IsPrivate);
public static class PgpEncrypt
{
private static readonly Regex HexEscapeRegex = new(@"\\x([0-9a-fA-F]{2})", RegexOptions.Compiled);
/// <summary>gpg's numeric public-key algorithm ids -> openpgp.js-style names.</summary>
private static readonly IReadOnlyDictionary<int, string> AlgoNames = new Dictionary<int, string>
{
[1] = "rsa",
[2] = "rsa",
[3] = "rsa",
[16] = "elgamal",
[17] = "dsa",
[18] = "ecdh",
[19] = "ecdsa",
[22] = "ed25519",
[27] = "x25519",
};
/// <summary>
/// A fresh 0700 GNUPGHOME under a temp directory, deleted (best-effort)
/// on dispose. gpg refuses a world-readable homedir, and this way no key
/// ever touches the user's real keyring.
/// </summary>
private sealed class TempGnuHome : IDisposable
{
public string Path { get; } = Path.Combine(Path.GetTempPath(), $"gnupg-{Guid.NewGuid():N}");
public TempGnuHome()
{
Directory.CreateDirectory(Path);
if (OperatingSystem.IsUnix())
{
new DirectoryInfo(Path).SetUnixFileMode(
System.IO.FileMode doesn_not_exist_placeholder);
}
}
public void Dispose()
{
try
{
Directory.Delete(Path, recursive: true);
}
catch (IOException)
{
// Lock/Sockets leftovers — the OS temp cleaner owns the directory now.
}
}
}
}
Also available in 8 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →