PgpCore

repository·master·Indexed 43 days ago

https://github.com/mattosaurus/pgpcore

A .NET Core class library for PGP encryption, signing, and decryption. A modernized, .NET Standard-compatible evolution of ChoPGP that utilizes BouncyCastle.Cryptography. It provides functionality for generating key pairs, encrypting/decrypting files, streams, and strings, as well as managing detached and clear signatures.

Tokens
4.1K
Snippets
14
Records
20
Agent score
4%

What's inside PgpCore

  1. Configure PublicKeyAlgorithm for GenerateKey

    master

    When calling GenerateKey, the PublicKeyAlgorithm determines the algorithm for the master key (which must be capable of signing/certifying). PgpCore automatically selects a matching encryption subkey algorithm based on your choice.

    PublicKeyAlgorithmMaster keyEncryption subkey
    RsaGeneral (Default)RSARSA
    RsaEncryptRSARSA
    RsaSignRSARSA
    EdDsaEd25519X25519 ECDH
    ECDsaNIST P-256NIST P-256 ECDH
    DsaDSARSA

    Important Notes:

    • Algorithms like ECDH, ElGamalEncrypt, ElGamalGeneral, or DiffieHellman will throw a NotSupportedException because they cannot be used for a master key.
    • For Dsa, PgpCore uses an RSA encryption subkey instead of ElGamal to avoid long generation times.
    • Dsa strengths must be a multiple of 64 (e.g., 512, 1024) if using BouncyCastle constraints.
  2. Understand v8.0+ Key Generation and Encryption Behavior

    master

    New Key Structure

    GenerateKey now produces a certify/sign master key and a separate encryption subkey. This matches GnuPG behavior and allows for better algorithm pairing (e.g., using EdDsa for signing and a different algorithm for encryption).

    Consequences:

    • Recipient IDs: Messages are encrypted to the subkey's key ID. GetRecipients will return the subkey ID rather than the master key ID for keys generated with v8.1 or later.
    • Compatibility: Keys generated by earlier versions remain compatible for both encryption and decryption.

    Encryption Selection Logic

    • Expired/Revoked Keys: PgpCore no longer automatically selects expired or revoked keys for encryption. If only expired or revoked keys are available, a NoEncryptionKeyException is thrown. To use an expired key, you must select it explicitly using the UseEncryptionKey mechanism.
    • Key Preference: Among valid keys, the newest is preferred (e.g., a new subkey will take precedence over an old one).

    Verification and Passphrases

    • Verify behavior: Verify now throws an error for encrypted input regardless of the throwIfEncrypted setting. For messages that are both encrypted and signed, use DecryptAndVerify.
    • Unencrypted Secret Keys: Keys generated without a passphrase are now stored unencrypted, matching GnuPG behavior. This ensures compatibility with tools like Kleopatra which previously required a non-empty passphrase to use keys encrypted with an empty string.
  3. Handle integrity check failures with IgnoreIntegrityCheckFailure

    master

    By default, if a message fails its modification detection (MDC) integrity check during Decrypt or DecryptAndVerify, a MessageIntegrityException is thrown.

    If you need to recover data from a message you otherwise trust (equivalent to gpg --ignore-mdc-error), set IgnoreIntegrityCheckFailure = true.

    Warning: A failed check means the ciphertext was modified. Only enable this if you have a specific reason to trust the source.

    PGP pgp = new PGP(encryptionKeys) { IgnoreIntegrityCheckFailure = true };
  4. Optimize Performance for Large Files

    master
    By default, PgpCore produces armored files. For large files, this can significantly increase file size and processing time. To improve performance, disable armouring by setting the armour property to false.
  5. Migrate to PgpCore v8.0

    master

    Version 8.0 introduced breaking changes by hardening cryptographic defaults and removing deprecated members.

    Changed Defaults

    If your code relies on implicit defaults, you must now set them explicitly to maintain old behavior:

    SettingOld defaultNew default
    SymmetricKeyAlgorithmTripleDesAes256
    HashAlgorithmTagSha1Sha256
    CompressionAlgorithmUncompressedZip
    GenerateKey strength10243072
    GenerateKey certainty824

    To reproduce old behavior, use:

    new PGP(keys) {
        SymmetricKeyAlgorithm = SymmetricKeyAlgorithmTag.TripleDes, 
        HashAlgorithmTag = HashAlgorithmTag.Sha1, 
        CompressionAlgorithm = CompressionAlgorithmTag.Uncompressed 
    }

    Removed Members

    • Replace IEncryptionKeys.PublicKey or EncryptionKeys.PublicKey with MasterKey or EncryptKeys.FirstOrDefault().
    • Replace IEncryptionKeys.PublicKeys or EncryptionKeys.PublicKeys with EncryptKeys.
    • Replace VerifyClear(string input, string output) with VerifyAndReadClearArmoredString(string input).
    • Do not use PGP.Instance: The static singleton was removed because it was not thread-safe. Construct a PGP instance directly.

    Exception Handling Changes

    Operations now throw specific exceptions deriving from PgpCoreException instead of generic ArgumentException or BouncyCastle PgpException.

    New specific exceptions include:

    • IncorrectPassphraseException
    • NoDecryptionKeyException
    • NotEncryptedDataException
    • MessageIntegrityException

    Note: PgpCoreException now derives from System.Exception (it previously derived from BouncyCastle's PgpException). Update any catch (PgpException) blocks accordingly.

  6. Configure PGP encryption settings

    master

    The PGP object allows you to customize encryption behavior via several properties. These settings determine how files are compressed, which algorithms are used for symmetric encryption, signature types, and more.

    Available Settings

    CompressionAlgorithm

    Applied prior to encryption. Options include:

    • Uncompressed
    • Zip (Default)
    • ZLib
    • BZip2

    SymmetricKeyAlgorithm

    Used for private key encryption. Options include:

    • Aes256 (Default)
    • Aes128, Aes192
    • Twofish
    • Camellia128, Camellia192, Camellia256
    • Idea, TripleDes, Cast5, Blowfish, Safer, Des
    • Null

    PgpSignatureType

    Determines the type of signature used for file signing. The default is DefaultCertification. Options include: BinaryDocument, CanonicalTextDocument, StandAlone, DefaultCertification, NoCertification, CasualCertification, PositiveCertification, SubkeyBinding, PrimaryKeyBinding, DirectKey, KeyRevocation, SubkeyRevocation, CertificationRevocation, Timestamp.

    FileType

    Encoding for the output file:

    • Binary (Default)
    • Text
    • UTF8

    HashAlgorithmTag

    Hash algorithm used by the signature. The default is Sha256. Options include: MD5, Sha1, RipeMD160, DoubleSha, MD2, Tiger192, Haval5pass160, Sha256, Sha384, Sha512, Sha224.

    TextEncoding

    Used for string-based overloads. Defaults to UTF-8. Ensure this matches on both encrypting and decrypting sides when working with legacy encodings.

  7. Generate a new PGP key pair

    master

    Use the GenerateKey method on a PGP instance to create a new public and private key pair. You must provide the file paths for the output files, a username (typically an email address), and a passphrase for the private key.

    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");
    }
  8. Verify PGP signatures

    master

    Use VerifyAsync to check if a file, stream, or string was signed by the expected private key. For clear-signed messages, use VerifyClearAsync. Some overloads allow you to verify and read the content simultaneously into an output file or string.

    // Verify and read a string
    string output = string.Empty;
    bool verified = await pgp.VerifyAsync("String to verify", output);
  9. Decrypt and Verify combined operation

    master

    The DecryptAndVerifyAsync method is specifically designed for messages created via EncryptAndSignAsync. It decrypts the message and verifies the embedded signature. Note: This will not work with messages that were encrypted first and then signed separately (as the signature would be appended rather than embedded).

    // Decrypt and Verify a file
    await pgp.DecryptAndVerifyAsync(inputFile, outputFile);
  10. Use detached signatures

    master

    Detached signatures allow you to produce a signature as a separate file, leaving the original content untouched. Use SignDetachedAsync to create the signature and VerifyDetachedAsync to verify it against the original content.

    // Sign detached
    await pgp.SignDetachedAsync(inputFile, signatureFile);
    
    // Verify detached
    bool verified = await pgp.VerifyDetachedAsync(inputFile, signatureFile);
  11. Inspect PGP files, streams, or strings

    master

    The InspectAsync method returns a PGPInspectResult object containing details about a PGP message, such as encryption status, signing status, filename, and headers. You can inspect FileInfo, Stream, or string inputs.

    // Inspect a file
    FileInfo inputFile = new FileInfo(@"C:\TEMP\Content\encrypted.pgp");
    PGP pgp = new PGP();
    PgpInspectResult result = await pgp.InspectAsync(inputFile);