PgpCore 8.1.0
dotnet add package PgpCore --version 8.1.0
NuGet\Install-Package PgpCore -Version 8.1.0
<PackageReference Include="PgpCore" Version="8.1.0" />
<PackageVersion Include="PgpCore" Version="8.1.0" />
<PackageReference Include="PgpCore" />
paket add PgpCore --version 8.1.0
#r "nuget: PgpCore, 8.1.0"
#:package PgpCore@8.1.0
#addin nuget:?package=PgpCore&version=8.1.0
#tool nuget:?package=PgpCore&version=8.1.0
PgpCore
A .NET Core class library for using PGP.
This is based on <a href="https://github.com/Cinchoo/ChoPGP" alt="ChoPGP">ChoPGP</a> but updated to .NET Standard and to add in a missing utilities class.
Installation
To use PgpCore in your C# project download it from NuGet.
Once you have the PgpCore libraries properly referenced in your project, you can include calls to them in your code.
Add the following namespaces to use the library:
using PgpCore;
Dependencies
- BouncyCastle.Cryptography (>= 2.4.0)
Usage
PgpCore targets both .NET Standard 2.0 (for broad compatibility with .NET Framework and older .NET Core) and .NET 10.
Upgrading to v8.0
Version 8.0 hardens the cryptographic defaults and removes long-deprecated members. These are breaking changes — review the following before upgrading:
Changed defaults (only affects code that relied on the old defaults without setting them explicitly):
| Setting | Old default | New default |
|---|---|---|
SymmetricKeyAlgorithm |
TripleDes |
Aes256 |
HashAlgorithmTag |
Sha1 |
Sha256 |
CompressionAlgorithm |
Uncompressed |
Zip |
GenerateKey strength |
1024 |
3072 |
GenerateKey certainty |
8 |
24 |
To reproduce the old behaviour, set the properties explicitly, e.g. new PGP(keys) { SymmetricKeyAlgorithm = SymmetricKeyAlgorithmTag.TripleDes, HashAlgorithmTag = HashAlgorithmTag.Sha1, CompressionAlgorithm = CompressionAlgorithmTag.Uncompressed }. Data produced with the new defaults remains readable by any modern OpenPGP implementation (including GnuPG).
Removed members:
IEncryptionKeys.PublicKey/EncryptionKeys.PublicKey→ useMasterKeyorEncryptKeys.FirstOrDefault().IEncryptionKeys.PublicKeys/EncryptionKeys.PublicKeys→ useEncryptKeys.VerifyClear(string input, string output)→ useVerifyAndReadClearArmoredString(string input)(the removed overload could never return its output).PGP.Instancestatic singleton → construct aPGPinstance directly (the mutable singleton with settable algorithm properties was not thread-safe).
Exceptions: operations now throw specific exception types deriving from PgpCoreException (e.g. IncorrectPassphraseException, NoDecryptionKeyException, NotEncryptedDataException, MessageIntegrityException) instead of generic ArgumentException / BouncyCastle PgpException. PgpCoreException now derives from System.Exception (in v7.x it derived from BouncyCastle's PgpException); update any catch (PgpException) blocks that relied on the old hierarchy.
Generated key structure: GenerateKey now produces a certify/sign master key plus a separate
encryption subkey, matching what GnuPG and other mainstream implementations emit. Previously a single
master key carried every capability, which meant the signature-only algorithms (EdDsa_Legacy, ECDsa, Dsa)
produced a key that could not encrypt at all (#285).
See PublicKeyAlgorithm for the algorithm pairings. Two consequences worth noting:
- Messages are now encrypted to the subkey's key id, so
GetRecipientsreturns the subkey id rather than the master key id for keys generated by v8.1 and later. - Keys generated by earlier versions continue to work unchanged for both encryption and decryption; only newly generated keys have the new structure.
Behaviour changes since v8.0.0:
- Expired and revoked keys are no longer selected for encryption automatically. Previously an
expired public key was encrypted to without any warning (#71);
now
NoEncryptionKeyExceptionis thrown when the only candidates are expired or revoked, matching gpg's behaviour. Among valid keys, the newest is preferred, so a newly issued subkey takes over from the one it replaces (#210). To encrypt to an expired key deliberately, select it explicitly with UseEncryptionKey. Verifythrows for encrypted input regardless ofthrowIfEncrypted. Previously, with the parameter at its default offalse,Verifyreturnedtruewhen the message was merely encrypted to a known key id — a result any sender can produce, unrelated to any signature. UseDecryptAndVerifyfor encrypted-and-signed messages.- Keys generated without a passphrase are stored unencrypted, as gpg stores them. Previously the secret key material was encrypted with the empty string, which made gpg and Kleopatra demand a non-empty passphrase before the key could be used (#308).
Azure Function Example
If you want a (basic) example of how you can use an Azure Function to encrypt/decrypt from Azure Blob Storage I've created a sample project here.
Performance
By default encrypted files are armoured. It is suggested that for larger files this is disabled as it can significantly increase the file size and processing time. To disable armouring set the armour property to false.
Methods
- Generate Key
- Inspect
- Encrypt
- Sign
- Clear Sign
- Detached Sign
- Encrypt and Sign
- Decrypt
- Verify
- Verify Clear
- Decrypt and Verify
- Key Management
Settings
- Compression Algorithm
- Symmetric Key Algorithm
- Pgp Signature Type
- Public Key Algorithm
- File Type
- Hash Algorithm Tag
- Ignore Integrity Check Failure
- Text Encoding
Exceptions
See Exceptions for the exception types thrown by operations.
Generate Key
Generate a new public and private key for the provided username and password.
GenerateKey
using (PGP pgp = new PGP())
{
// Generate keys
pgp.GenerateKey(new FileInfo(@"C:\TEMP\Keys\public.asc"), new FileInfo(@"C:\TEMP\Keys\private.asc"), "email@email.com", "password");
}
Inspect
Inspect the provided file, stream or string and return a PGPInspectResult object that contains details on the messages encryption and sign status as well as additional information on filename, headers, etc. where available.
gpg --list-packets "C:\TEMP\Content\encrypted.pgp"
Inspect File
// Load keys
FileInfo publicKey = new FileInfo(@"C:\TEMP\Keys\public.asc");
FileInfo privateKey = new FileInfo(@"C:\TEMP\Keys\private.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey, privateKey, "password");
// Reference input file
FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\encrypted.pgp");
// Inspect
PGP pgp = new PGP();
PgpInspectResult result = await pgp.InspectAsync(inputFile);
Inspect Stream
// Load keys
EncryptionKeys encryptionKeys;
using (Stream publicKeyStream = new FileStream(@"C:\TEMP\Keys\public.asc", FileMode.Open))
using (Stream privateKeyStream = new FileStream(@"C:\TEMP\Keys\private.asc", FileMode.Open))
encryptionKeys = new EncryptionKeys(publicKeyStream, privateKeyStream, "password");
PGP pgp = new PGP(encryptionKeys);
// Reference input stream
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\Content\encrypted.pgp", FileMode.Open))
// Inspect
PgpInspectResult result = await pgp.InspectAsync(inputFileStream);
Inspect String
// Load keys
string publicKey = File.ReadAllText(@"C:\TEMP\Keys\public.asc");
string privatyeKey = File.ReadAllText(@"C:\TEMP\Keys\private.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey, privateKey, "password");
// Inspect
PGP pgp = new PGP(encryptionKeys);
PgpInspectResult result = await pgp.InspectAsync("String to inspect");
Encrypt
Encrypt the provided file, stream or string using a public key.
Optional headers can be provided to include in the encrypted file. These can be set by providing a Dictionary<string, string> to the headers parameter. The key of the dictionary will be the header name and the value will be the header value.
gpg --output "C:\TEMP\Content\encrypted.pgp" --encrypt "C:\TEMP\Content\content.txt"
Encrypt File
// Load keys
FileInfo publicKey = new FileInfo(@"C:\TEMP\Keys\public.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey);
// Reference input/output files
FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\content.txt");
FileInfo encryptedFile = new FileInfo(@"C:\TEMP\Content\encrypted.pgp");
// Encrypt
PGP pgp = new PGP(encryptionKeys);
await pgp.EncryptAsync(inputFile, encryptedFile);
Encrypt Stream
// Load keys
EncryptionKeys encryptionKeys;
using (Stream publicKeyStream = new FileStream(@"C:\TEMP\Keys\public.asc", FileMode.Open))
encryptionKeys = new EncryptionKeys(publicKeyStream);
PGP pgp = new PGP(encryptionKeys);
// Reference input/output files
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\Content\content.txt", FileMode.Open))
using (Stream outputFileStream = File.Create(@"C:\TEMP\Content\encrypted.pgp"))
// Encrypt
await pgp.EncryptAsync(inputFileStream, outputFileStream);
Encrypt String
// Load keys
string publicKey = File.ReadAllText(@"C:\TEMP\Keys\public.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey);
// Encrypt
PGP pgp = new PGP(encryptionKeys);
string encryptedContent = await pgp.EncryptAsync("String to encrypt");
Sign
Sign the provided file or stream using a private key.
Optional headers can be provided to include in the signed file. These can be set by providing a Dictionary<string, string> to the headers parameter. The key of the dictionary will be the header name and the value will be the header value.
gpg --output "C:\TEMP\Content\content.txt" --sign "C:\TEMP\Content\signed.pgp"
Sign File
// Load keys
FileInfo privateKey = new FileInfo(@"C:\TEMP\Keys\private.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(privateKey, "password");
// Reference input/output files
FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\content.txt");
FileInfo signedFile = new FileInfo(@"C:\TEMP\Content\signed.pgp");
// Sign
PGP pgp = new PGP(encryptionKeys);
await pgp.SignAsync(inputFile, signedFile);
Sign Stream
// Load keys
EncryptionKeys encryptionKeys;
using (Stream privateKeyStream = new FileStream(@"C:\TEMP\Keys\private.asc", FileMode.Open))
encryptionKeys = new EncryptionKeys(privateKeyStream, "password");
PGP pgp = new PGP(encryptionKeys);
// Reference input/output files
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\Content\content.txt", FileMode.Open))
using (Stream outputFileStream = File.Create(@"C:\TEMP\Content\signed.pgp"))
// Sign
await pgp.SignAsync(inputFileStream, outputFileStream);
Sign String
// Load keys
string privateKey = File.ReadAllText(@"C:\TEMP\Keys\private.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(privateKey, "password");
PGP pgp = new PGP(encryptionKeys);
// Sign
string signedContent = await pgp.SignAsync("String to sign");
Clear Sign
Clear signing keeps the message readable and appends an armored signature. The signature hashes canonical CRLF line endings and ignores trailing spaces and tabs on each line. The separator immediately before the signature armor is excluded from the hash. Clear-signed messages are not compressed or encrypted.
gpg --output "C:\TEMP\Content\content.txt" --clearsign "C:\TEMP\Content\clearSigned.pgp"
Clear Sign File
// Load keys
FileInfo privateKey = new FileInfo(@"C:\TEMP\Keys\private.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(privateKey, "password");
// Reference input/output files
FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\content.txt");
FileInfo signedFile = new FileInfo(@"C:\TEMP\Content\signed.pgp");
// Sign
PGP pgp = new PGP(encryptionKeys);
await pgp.ClearSignAsync(inputFile, signedFile);
Clear Sign Stream
// Load keys
EncryptionKeys encryptionKeys;
using (Stream privateKeyStream = new FileStream(@"C:\TEMP\Keys\private.asc", FileMode.Open))
encryptionKeys = new EncryptionKeys(privateKeyStream, "password");
PGP pgp = new PGP(encryptionKeys);
// Reference input/output files
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\Content\content.txt", FileMode.Open))
using (Stream outputFileStream = File.Create(@"C:\TEMP\Content\signed.pgp"))
// Sign
await pgp.ClearSignAsync(inputFileStream, outputFileStream);
Clear Sign String
// Load keys
string privateKey = File.ReadAllText(@"C:\TEMP\Keys\private.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(privateKey, "password");
PGP pgp = new PGP(encryptionKeys);
// Sign
string signedContent = await pgp.ClearSignAsync("String to sign");
Detached Sign
Produce a signature for the provided file, stream or string as a separate file, leaving the original content untouched, or verify a detached signature against the original content. Verification is cryptographic: the signature must have been made over exactly the supplied content by one of the supplied verification keys.
gpg --detach-sign "C:\TEMP\Content\content.txt"
Detached Sign File
// Load keys
FileInfo privateKey = new FileInfo(@"C:\TEMP\Keys\private.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(privateKey, "password");
// Reference input/output files
FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\content.txt");
FileInfo signatureFile = new FileInfo(@"C:\TEMP\Content\content.txt.sig");
// Sign
PGP pgp = new PGP(encryptionKeys);
await pgp.SignDetachedAsync(inputFile, signatureFile);
Verify Detached Signature
// Load keys
FileInfo publicKey = new FileInfo(@"C:\TEMP\Keys\public.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey);
// Reference the original content and the signature
FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\content.txt");
FileInfo signatureFile = new FileInfo(@"C:\TEMP\Content\content.txt.sig");
// Verify
PGP pgp = new PGP(encryptionKeys);
bool verified = await pgp.VerifyDetachedAsync(inputFile, signatureFile);
Encrypt and Sign
Encrypt the provided file, stream or string using a public key and sign using your private key. You usually encrypt with the public key of your counterparty so they can decrypt with their private key and sign with your private key so they can verify with your public key.
Although this method is called EncryptAndSign the signature will actually be included within the encrypted message rather than being appended to the encrypted message. This ensures that the original message was composed by the holder of the private key.
gpg --encrypt --sign --recipient 'some user ID value' "C:\TEMP\keys\content.txt"
Encrypt File And Sign
// Load keys
FileInfo publicKey = new FileInfo(@"C:\TEMP\Keys\public.asc");
FileInfo privateKey = new FileInfo(@"C:\TEMP\Keys\private.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey, privateKey, "password");
// Reference input/output files
FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\content.txt");
FileInfo encryptedSignedFile = new FileInfo(@"C:\TEMP\Content\encryptedSigned.pgp");
// Encrypt and Sign
PGP pgp = new PGP(encryptionKeys);
await pgp.EncryptAndSignAsync(inputFile, encryptedSignedFile);
Encrypt Stream And Sign
// Load keys
EncryptionKeys encryptionKeys;
using (Stream publicKeyStream = new FileStream(@"C:\TEMP\Keys\public.asc", FileMode.Open))
using (Stream privateKeyStream = new FileStream(@"C:\TEMP\Keys\private.asc", FileMode.Open))
encryptionKeys = new EncryptionKeys(publicKeyStream, privateKeyStream, "password");
PGP pgp = new PGP(encryptionKeys);
// Reference input/output files
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\Content\content.txt", FileMode.Open))
using (Stream outputFileStream = File.Create(@"C:\TEMP\Content\signed.pgp"))
// Encrypt and Sign
await pgp.EncryptAndSignAsync(inputFileStream, outputFileStream);
Encrypt String And Sign
// Load keys
string publicKey = File.ReadAllText(@"C:\TEMP\Keys\public.asc");
string privateKey = File.ReadAllText(@"C:\TEMP\Keys\private.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey, privateKey, "password");
PGP pgp = new PGP(encryptionKeys);
// Encrypt and Sign
string encryptedSignedContent = await pgp.EncryptAndSignAsync("String to encrypt and sign");
Decrypt
Decrypt the provided file, stream or string using the matching private key and passphrase.
gpg --output "C:\TEMP\Content\decrypted.txt" --decrypt "C:\TEMP\Content\encrypted.pgp"
Decrypt File
// Load keys
FileInfo privateKey = new FileInfo(@"C:\TEMP\Keys\private.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(privateKey, "password");
// Reference input/output files
FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\encryptedContent.pgp");
FileInfo decryptedFile = new FileInfo(@"C:\TEMP\Content\decrypted.txt");
// Decrypt
PGP pgp = new PGP(encryptionKeys);
await pgp.DecryptAsync(inputFile, decryptedFile);
Decrypt Stream
// Load keys
EncryptionKeys encryptionKeys;
using (Stream privateKeyStream = new FileStream(@"C:\TEMP\Keys\private.asc", FileMode.Open))
encryptionKeys = new EncryptionKeys(privateKeyStream, "password");
PGP pgp = new PGP(encryptionKeys);
// Reference input/output files
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\Content\encryptedContent.pgp", FileMode.Open))
using (Stream outputFileStream = File.Create(@"C:\TEMP\Content\decrypted.txt"))
// Decrypt
await pgp.DecryptAsync(inputFileStream, outputFileStream);
Decrypt String
// Load keys
string privateKey = File.ReadAllText(@"C:\TEMP\Keys\private.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(privateKey, "password");
PGP pgp = new PGP(encryptionKeys);
// Decrypt
string decryptedContent = await pgp.DecryptAsync("String to decrypt");
Verify
Verify that the file, stream or string was signed by the matching private key of the counterparty.
gpg --verify "C:\TEMP\Content\signed.pgp"
Verify File
// Load keys
FileInfo publicKey = new FileInfo(@"C:\TEMP\Keys\public.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey);
// Reference input
FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\signedContent.pgp");
// Verify
PGP pgp = new PGP(encryptionKeys);
bool verified = await pgp.VerifyAsync(inputFile);
Verify Stream
// Load keys
EncryptionKeys encryptionKeys;
using (Stream publicKeyStream = new FileStream(@"C:\TEMP\Keys\public.asc", FileMode.Open))
encryptionKeys = new EncryptionKeys(publicKeyStream);
PGP pgp = new PGP(encryptionKeys);
// Reference input file
bool verified;
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\Content\encryptedContent.pgp", FileMode.Open))
// Verify
verified = await pgp.VerifyAsync(inputFileStream);
Verify String
// Load keys
string publicKey = File.ReadAllText(@"C:\TEMP\Keys\public.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey);
PGP pgp = new PGP(encryptionKeys);
// Verify
bool verified = await pgp.VerifyAsync("String to verify");
Verify and Read
Verify that the file, stream was signed by the matching private key of the counterparty. This is an overload of the Verify method that takes an additional output argument. Please note that this is not available for the string based method.
Verify And Read File
// Load keys
FileInfo publicKey = new FileInfo(@"C:\TEMP\Keys\public.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey);
// Reference input
FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\signedContent.pgp");
FileInfo outputFile = new FileInfo(@"C:\TEMP\Content\decryptedContent.txt");
// Verify and read
PGP pgp = new PGP(encryptionKeys);
bool verified = await pgp.VerifyAsync(inputFile, outputFile);
Verify And Read Stream
// Load keys
EncryptionKeys encryptionKeys;
using (Stream publicKeyStream = new FileStream(@"C:\TEMP\Keys\public.asc", FileMode.Open))
encryptionKeys = new EncryptionKeys(publicKeyStream);
PGP pgp = new PGP(encryptionKeys);
// Reference input file
bool verified;
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\Content\encryptedContent.pgp", FileMode.Open))
using (FileStream outputFileStream = new FileStream(@"C:\TEMP\Content\decryptedContent.pgp", FileMode.Open))
// Verify and read
verified = await pgp.VerifyAsync(inputFileStream);
Verify And Read String
// Load keys
string publicKey = File.ReadAllText(@"C:\TEMP\Keys\public.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey);
PGP pgp = new PGP(encryptionKeys);
// Verify and read
string output = string.Empty;
bool verified = await pgp.VerifyAsync("String to verify", output);
Verify Clear
Verify that the clear signed file or stream was signed by the matching private key of the counterparty.
gpg --verify "C:\TEMP\Content\clearSigned.pgp"
Verify Clear File
// Load keys
FileInfo publicKey = new FileInfo(@"C:\TEMP\Keys\public.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey);
// Reference input
FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\signedContent.pgp");
// Verify
PGP pgp = new PGP(encryptionKeys);
bool verified = await pgp.VerifyClearAsync(inputFile);
Verify Clear Stream
// Load keys
EncryptionKeys encryptionKeys;
using (Stream publicKeyStream = new FileStream(@"C:\TEMP\Keys\public.asc", FileMode.Open))
encryptionKeys = new EncryptionKeys(publicKeyStream);
PGP pgp = new PGP(encryptionKeys);
// Reference input file
bool verified;
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\Content\encryptedContent.pgp", FileMode.Open))
// Verify
verified = await pgp.VerifyClearAsync(inputFileStream);
Verify Clear String
// Load keys
string publicKey = File.ReadAllText(@"C:\TEMP\Keys\public.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey);
PGP pgp = new PGP(encryptionKeys);
// Verify
bool verified = await pgp.VerifyClearAsync("String to verify");
Verify and Read Clear
Verify that the clear signed file or stream was signed by the matching private key of the counterparty. This is an overload of the VerifyClear method that takes an additional output argument.
Verify And Read Clear File
// Load keys
FileInfo publicKey = new FileInfo(@"C:\TEMP\Keys\public.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey);
// Reference input
FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\signedContent.pgp");
FileInfo outputFile = new FileInfo(@"C:\TEMP\Content\decryptedContent.txt");
// Verify and read
PGP pgp = new PGP(encryptionKeys);
bool verified = await pgp.VerifyClearAsync(inputFile, outputFile);
Verify And Read Clear Stream
// Load keys
EncryptionKeys encryptionKeys;
using (Stream publicKeyStream = new FileStream(@"C:\TEMP\Keys\public.asc", FileMode.Open))
encryptionKeys = new EncryptionKeys(publicKeyStream);
PGP pgp = new PGP(encryptionKeys);
// Reference input file
bool verified;
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\Content\encryptedContent.pgp", FileMode.Open))
using (FileStream outputFileStream = new FileStream(@"C:\TEMP\Content\decryptedContent.pgp", FileMode.Open))
// Verify and read
verified = await pgp.VerifyClearAsync(inputFileStream);
Verify And Read Clear String
// Load keys
string publicKey = File.ReadAllText(@"C:\TEMP\Keys\public.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey);
PGP pgp = new PGP(encryptionKeys);
// Verify and read
string output = string.Empty;
bool verified = await pgp.VerifyClearAsync("String to verify", output);
Decrypt and Verify
Decrypt and then verify the provided encrypted and signed file, stream or string. Usually your counterparty will encrypt with your public key and sign with their private key so you can decrypt with your private key and verify with their public key.
The DecryptAndVerify methods will only work with files that have been encrypted and signed using the EncryptAndSign methods. This is because the signature is included within the encrypted message rather than being appended to the encrypted message. If a file is first encrypted using an Encrypt method and then signed using a Sign method then the signature will be appended to the encrypted message rather than embedded within it and the DecryptAndVerify methods will not be able to verify the signature.
Decrypt File And Verify
// Load keys
FileInfo publicKey = new FileInfo(@"C:\TEMP\Keys\public.asc");
FileInfo privateKey = new FileInfo(@"C:\TEMP\Keys\private.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey, privateKey, "password");
// Reference input/output files
FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\encryptedSigned.pgp");
FileInfo outputFile = new FileInfo(@"C:\TEMP\Content\content.txt");
// Decrypt and Verify
PGP pgp = new PGP(encryptionKeys);
await pgp.DecryptAndVerifyAsync(inputFile, outputFile);
Decrypt Stream And Verify
// Load keys
EncryptionKeys encryptionKeys;
using (Stream publicKeyStream = new FileStream(@"C:\TEMP\Keys\public.asc", FileMode.Open))
using (Stream privateKeyStream = new FileStream(@"C:\TEMP\Keys\private.asc", FileMode.Open))
encryptionKeys = new EncryptionKeys(publicKeyStream, privateKeyStream, "password");
PGP pgp = new PGP(encryptionKeys);
// Reference input/output files
using (FileStream inputFileStream = new FileStream(@"C:\TEMP\Content\encryptedSigned.pgp", FileMode.Open))
using (Stream outputFileStream = File.Create(@"C:\TEMP\Content\content.txtp"))
// Decrypt and Verify
await pgp.DecryptAndVerifyAsync(inputFileStream, outputFileStream);
Decrypt String And Verify
// Load keys
string publicKey = File.ReadAllText(@"C:\TEMP\Keys\public.asc");
string privateKey = File.ReadAllText(@"C:\TEMP\Keys\private.asc");
EncryptionKeys encryptionKeys = new EncryptionKeys(publicKey, privateKey, "password");
PGP pgp = new PGP(encryptionKeys);
// Decrypt and Verify
string encryptedSignedContent = await pgp.DecryptAndVerifyAsync("String to decrypt and verify");
Key Management
EncryptionKeys picks the best key for each job automatically: the strongest encryption-capable,
unexpired, unrevoked key from each supplied key ring (newest first among equals), and every key and
subkey is available for signature verification. Two tools change that behaviour when the automatic
choice is not what you need.
EncryptionKeysBuilder
A fluent alternative to the EncryptionKeys constructors. Its main advantage is combining multiple
private keys, each with its own passphrase — useful when decrypting messages that may be encrypted to
any of several keys you hold.
EncryptionKeys encryptionKeys = new EncryptionKeysBuilder()
.WithPublicKey(new FileInfo(@"C:\TEMP\Keys\recipient.asc"))
.WithPrivateKey(new FileInfo(@"C:\TEMP\Keys\private1.asc"), "passphrase1")
.WithPrivateKey(new FileInfo(@"C:\TEMP\Keys\private2.asc"), "passphrase2")
.Build();
PGP pgp = new PGP(encryptionKeys);
UseEncryptionKey
Selects a specific key (by key id) as the encryption key, instead of the automatically chosen one.
This is also the override for encrypting to an expired or revoked key, which the automatic selection
refuses. Throws MissingKeyException when no supplied key ring contains an encryption key with that id.
EncryptionKeys encryptionKeys = new EncryptionKeys(new FileInfo(@"C:\TEMP\Keys\public.asc"));
encryptionKeys.UseEncryptionKey(0x123456789ABCDEF0);
PGP pgp = new PGP(encryptionKeys);
The same selection is available at construction via the builder's WithPreferredEncryptionKeyId(keyId).
Settings
The PGP object contains a variety of settings properties that can be used to determine how files are encrypted.
CompressionAlgorithm
The compression algorithm to be used on the message. This is applied prior to encryption, either to the message or the signed message.
- Uncompressed
- Zip - Default
- ZLib
- BZip2
SymmetricKeyAlgorithm
The private key encryption algorithm.
- Null
- Idea
- TripleDes
- Cast5
- Blowfish
- Safer
- Des
- Aes128
- Aes192
- Aes256 - Default
- Twofish
- Camellia128
- Camellia192
- Camellia256
PgpSignatureType
The type of signature to be used for file signing.
- BinaryDocument
- CanonicalTextDocument
- StandAlone
- DefaultCertification - Default
- NoCertification
- CasualCertification
- PositiveCertification
- SubkeyBinding
- PrimaryKeyBinding
- DirectKey
- KeyRevocation
- SubkeyRevocation
- CertificationRevocation
- Timestamp
PublicKeyAlgorithm
The algorithm used for the master key created by GenerateKey.
GenerateKey produces a certify/sign master key plus a matching encryption subkey, so the master key
algorithm only needs to be capable of signing. The encryption subkey algorithm is chosen automatically:
| PublicKeyAlgorithm | Master key | Encryption subkey |
|---|---|---|
| RsaGeneral - Default | RSA | RSA |
| RsaEncrypt | RSA | RSA |
| RsaSign | RSA | RSA |
| EdDsa_Legacy | Ed25519 | X25519 ECDH |
| ECDsa | NIST P-256 | NIST P-256 ECDH |
| Dsa | DSA | RSA |
Any other value (ECDH, ElGamalEncrypt, ElGamalGeneral, DiffieHellman) throws
NotSupportedException, as those algorithms cannot sign or certify and so cannot be used for a master
key. ECDH is still used as a subkey algorithm via the pairings above.
Two notes on Dsa:
- DSA is paired with an RSA encryption subkey rather than the traditional ElGamal one. BouncyCastle's ElGamal parameter generation takes over two minutes at 2048 bits and considerably longer at the default strength, which would make key generation appear to hang. The subkey algorithm does not have to relate to the master's, and gpg accepts an RSA encryption subkey under a DSA primary key.
- DSA strengths of 512-1024 bits must be a multiple of 64 (a BouncyCastle constraint); larger strengths
are generated with a 256-bit subgroup per FIPS 186-3. An unusable strength now raises a descriptive
ArgumentOutOfRangeExceptionrather than the opaque BouncyCastle "size must be from 512 - 1024 and a multiple of 64" error reported in #285.
FileType
Encoding to be used for the output file.
- Binary - Default
- Text
- UTF8
HashAlgorithmTag
The hash algorithm to be used by the signature.
- MD5
- Sha1
- RipeMD160
- DoubleSha
- MD2
- Tiger192
- Haval5pass160
- Sha256 - Default
- Sha384
- Sha512
- Sha224
During GenerateKey this value is also used for the key's self-certification, where some algorithms
require a minimum digest size: 256 bits for EdDsa_Legacy and ECDsa, and at least the subgroup size for Dsa
(256 bits above 1024-bit keys, otherwise 160). If the requested hash is shorter than the key algorithm
requires, SHA-256 is used for the certification instead — a shorter digest would produce a
self-certification that other implementations may reject, and some combinations (MD5 with ECDsa or
Dsa) cannot be signed by BouncyCastle at all. RSA keys have no such requirement and always use the
requested hash.
IgnoreIntegrityCheckFailure
When true, a failed modification detection (MDC) integrity check during Decrypt or
DecryptAndVerify is tolerated instead of throwing MessageIntegrityException. Equivalent to
gpg --ignore-mdc-error. Defaults to false; only enable it to recover data from a message you have
other reasons to trust, since a failed check means the ciphertext was modified.
PGP pgp = new PGP(encryptionKeys) { IgnoreIntegrityCheckFailure = true };
TextEncoding
TextEncoding controls plaintext stored in literal packets by string encryption and signing, and
plaintext decoded by the read helpers. It defaults to UTF-8 without a byte order mark. Set it when a
counterparty uses another payload encoding, such as Windows-1253 or UTF-16.
Armored string inputs and outputs use UTF-8 independently of the payload encoding. Clear-signing and
verifying clear-signed strings also use UTF-8 for their cleartext. For a clear-signed document in another
encoding, use the stream or file APIs and set TextEncoding when reading the verified plaintext.
Stream and file operations retain their byte-oriented input and output.
PGP pgp = new PGP(encryptionKeys) { TextEncoding = Encoding.GetEncoding(1253) };
Exceptions
Operations throw specific exception types, all deriving from PgpCoreException (which derives from
System.Exception):
| Exception | Thrown when |
|---|---|
NotEncryptedDataException |
Decrypt input is not PGP encrypted data — plain text, signed-only, or clear-signed content. |
UnsupportedAeadException |
The message uses an AEAD encryption format that PgpCore cannot decrypt (#219). |
IncorrectPassphraseException |
The supplied passphrase does not unlock the private key. |
InvalidKeyMaterialException |
Key material could not be parsed, or contained no usable keys. |
MessageIntegrityException |
The message failed its modification detection (MDC) check — see IgnoreIntegrityCheckFailure. |
MissingKeyException |
An operation needs a key that was not supplied (e.g. UseEncryptionKey with an unknown key id). |
NoEncryptionKeyException |
No usable encryption key was found — including when the only candidates are expired or revoked. |
NoSigningKeyException |
No key suitable for signing was found in the supplied private key material. |
NoDecryptionKeyException |
None of the supplied private keys match any key the message is encrypted to (the message's key ids are listed). |
PgpException (BouncyCastle's type) is still thrown for signature verification failures, e.g.
"Failed to verify file." from DecryptAndVerify.
Certificate validation and safe output
Key selection authenticates primary self-signatures, subkey bindings, key flags, expiration metadata, and primary-issued revocations. Signing subkeys require an embedded primary-key binding signature. Subkeys without a verifiable binding are excluded from key selection and inspection; they do not disable the authentic keys in the same certificate. A primary key without a valid self-signature remains invalid. New encryption and signing reject expired or revoked primary keys and subkeys. Creation times allow up to five minutes of clock skew; expiration and revocation checks still apply. Explicit encryption-key selection still permits historical keys, but requires an authentic binding and encryption capability.
Verification uses authenticated signing keys, including expired or revoked keys for cryptographic verification of older data. It does not establish a trusted identity or prove that a message predates revocation. Compare the complete primary fingerprint against an independently trusted value.
PGP.InspectKeys(Stream) returns every primary and subkey, authenticated user IDs, fingerprints, capabilities, and current usability. PGP.ExportPublicKeys validates public certificates before exporting them; it rejects secret-key packets and can require an expected complete primary fingerprint. Its file overload stages the validated export before replacing the destination.
File-writing overloads stage output beside the destination and commit only after successful completion. Failed integrity or signature validation preserves an existing destination and does not publish a new plaintext file. Key generation produces both key files before committing either; failure to commit the second restores the first when possible. Public and private key destinations must differ by more than letter case, including on case-sensitive volumes. Two files are not a crash-atomic transaction. If restoration itself fails, the exception identifies the retained recovery backup.
Concatenated encrypted messages must all parse and decrypt successfully before file output is committed. A damaged later message fails the operation instead of replacing the destination with only the earlier plaintext chunks.
AEAD data packets (tag 20), including OCB and EAX, are unsupported by the current BouncyCastle OpenPGP decryption engine. Decryption and inspection reject them with UnsupportedAeadException before attempting to process their payload, whether recipients use public keys or passphrases. Ask the sender for non-AEAD, MDC-protected output; changing key preferences cannot repair existing ciphertext. See issue #219.
Unix staging directories are owner-only while an operation is in progress. New non-secret outputs use normal creation permissions under the process umask; replacements preserve the existing destination's read, write, and execute permission bits. Generated secret-key files are owner-only, including replacements. Atomic file output requires create, rename, and delete access in the destination directory, even when the existing destination file is writable. There is no in-place fallback: callers needing their own output or permission policy can use a stream overload. Stream overloads can emit data before final validation, so callers must discard stream output when verification returns false or decryption throws. Borrowed streams remain open.
Verification without extraction hashes and discards the payload. Non-seekable verification and inspection spool input to an owned temporary file instead of allocating memory proportional to message size. Clear-sign verification canonicalizes and hashes even long lines with fixed-size buffers and private temporary storage; these operations require writable temporary storage.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. 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 is compatible. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- BouncyCastle.Cryptography (>= 2.7.0)
-
net10.0
- BouncyCastle.Cryptography (>= 2.7.0)
NuGet packages (21)
Showing the top 5 NuGet packages that depend on PgpCore:
| Package | Downloads |
|---|---|
|
DTF.Services.Common.V2
DTF common services. |
|
|
APF.Core
Package Description |
|
|
PasswordManagerAccess
Package Description |
|
|
PTRPlus.Framework
PTR Plus Framework |
|
|
Sonar.Lib.SFTP
Package Description |
GitHub repositories (5)
Showing the top 5 popular GitHub repositories that depend on PgpCore:
| Repository | Stars |
|---|---|
|
ClassIsland/ClassIsland
一款功能强、可定制、跨平台,适用于班级多媒体屏幕的课表信息显示工具,可以一目了然地显示各种信息。
|
|
|
paillave/Etl.Net
Mass processing data with a complete ETL for .net developers
|
|
|
axzxs2001/Asp.NetCoreExperiment
原来所有项目都移动到**OleVersion**目录下进行保留。新的案例装以.net 5.0为主,一部分对以前案例进行升级,一部分将以前的工作经验总结出来,以供大家参考!
|
|
|
automuteus/amonguscapture
Capture of the local Among Us executable state
|
|
|
UiPath/Community.Activities
Repository of Windows Workflow Foundation Activities for UiPath Community
|
| Version | Downloads | Last Updated |
|---|---|---|
| 8.1.0 | 0 | 10/9/2026 |
| 8.0.0 | 194,222 | 7/16/2026 |
| 7.2.0 | 48,860 | 7/6/2026 |
| 7.1.0 | 294,648 | 6/1/2026 |
| 7.0.0 | 481,611 | 2/24/2026 |
| 6.5.5 | 74,562 | 2/23/2026 |
| 6.5.4 | 258,705 | 1/13/2026 |
| 6.5.3 | 385,959 | 10/20/2025 |
| 6.5.2 | 1,179,944 | 6/2/2025 |
| 6.5.1 | 2,898,015 | 9/16/2024 |
| 6.5.0 | 684,196 | 6/13/2024 |
| 6.4.1 | 77,655 | 6/3/2024 |
| 6.3.1 | 1,621,941 | 12/19/2023 |
| 5.13.1 | 209,186 | 11/15/2023 |
| 5.13.0 | 274,965 | 10/21/2023 |
| 5.10.0 | 1,508,599 | 3/23/2023 |
v8.1.0 - Various bug fixes, improvements and package updates.