jstedfast / MimeKit

A .NET MIME creation and parser library with support for S/MIME, PGP, DKIM, TNEF and Unix mbox spools.
http://www.mimekit.net
MIT License
1.83k stars 372 forks source link

Support for "gpg modern" #362

Closed Compufreak345 closed 6 years ago

Compufreak345 commented 6 years ago

Hi,

Short: I got told in the GPG4Win-Forums that "GPG Modern" does not use the "pubring.gpg" and "secring.pgp" files anymore, that are used in the current implementation of GnuPGContext and that it instead uses a folder structure and separate files for each key.

Gnupg-folder with "gpg classic": image

Gnupg-folder with "gpg modern": image

Long : I got a working code for encrypting PGP-Mails using MailKit and GPG4Win 2.3.4 - regarding to https://gpg4win.org/change-history.html this is based on GnuPG 2.0.30. So, the steps taken to make it working were these:

public class AclGnuPGContext : GnuPGContext
  {
    public static string PgpKeyPassword;

    protected override string GetPasswordForKey(PgpSecretKey key)
    {
      return PgpKeyPassword;
    }
  }

if (AclGnuPGContext.PgpKeyPassword == null)
                AclGnuPGContext.PgpKeyPassword = pgpKeyPassword;
            CryptographyContext.Register(typeof(AclGnuPGContext));

            var message = new MimeMessage();
            message.From.Add(new MailboxAddress(Config.SenderName, Config.SenderAddress));
            foreach (var address in recipientAddress.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries))
                message.To.Add(new MailboxAddress(address));
            message.Subject = subject;

            // create our message body (perhaps a multipart/mixed with the message text and some
            // image attachments, for example)
            var body = new TextPart("text/plain")
            {
                Text = text
            };

            // now to encrypt our message body using our custom PGP/MIME cryptography context
            using (var ctx = new AclGnuPGContext())
            {
                // Note: this assumes that "Alice" has a public PGP key that matches her email
                // address. If she doesn't, try using a SecureMailboxAddress which allows you
                // to specify the fingerprint of her public PGP key to use for lookups.
                message.Body = MultipartEncrypted.SignAndEncrypt(new MailboxAddress(Config.SenderAddress),DigestAlgorithm.Sha1, message.To.Mailboxes, body);
            }

When doing exactly the same steps using GPG4Win 3.0.1, what's based on GnuPG 2.2.3, I get the following error: image

Do you have any plans to support that seemingly new standard - or is it already and I missed something?

Thanks & best regards, Compu

jstedfast commented 6 years ago

As you've noticed, GnuPG >= 2.2 changed their file formats for keyrings. You said they they are now using a directory structure but my understanding was that they were using a new file format (.kbx instead of .gpg) that allowed them to mix OpenPGP keys and X.509 keys/certificates.

I could not, however, find any documentation on this new format (directory structure or otherwise) and so I haven't begun implementing support for it. Trying to reverse engineer it would be like bashing my face into a brick wall, so I'm not very enthusiastic about trying.

Compufreak345 commented 6 years ago

After reading a bit more on the topic, you are right - it seems to be a quite undocumented format :/ So we keep using the older version for now. I think a hint in the docs about this would be useful.

jstedfast commented 6 years ago

Did a bit of looking and playing, and it seems like we might be both right :)

From the GnuPG FAQ (What's new in 2.1.x?):

Removal of the secret keyring

gpg used to keep the public key pairs in two files: pubring.gpg and secring.gpg. The only difference is that secring stored in addition to the public part also the private part of the key pair. The secret keyring thus contained only the keys for which a private key is available, that is the user’s key. It required a lot of code to keep both versions of the key in sync and led to sometimes surprising inconsistencies.

The design of GnuPG-2 demands that only the gpg-agent has control over the private parts of the keys and the actual encryption engine (gpg or gpgsm) does not know about the private key but care only about session keys and keys for symmetric encryption. This has been implemented about 10 years ago for gpgsm (the S/MIME part of GnuPG). However, gpg (the OpenPGP part) used the gpg-agent only as passphrase entry and cache device but handles the private key itself.

With GnuPG 2.1 this changed and gpg now also delegates all private key operations to the gpg- agent. Thus there is no more code in the gpg binary for handling private keys. En passant this allows the long time requested “merging of secret keys” and several other advanced key management techniques.

To ease the migration to the no-secring method, gpg detects the presence of a secring.gpg and converts the keys on-the-fly to the the key store of gpg-agent (this is the private-keys-v1.d directory below the GnuPG home directory (~/.gnupg)). This is done only once and an existing secring.gpg is then not anymore touched by gpg. This allows co-existence of older GnuPG versions with GnuPG 2.1. However, any change to the private keys using the new gpg will not show up when using pre-2.1 versions of GnuPG and vice versa.

Note that the command --export-secret-keys still creates an OpenPGP compliant file with the secret keys. This is achieved by asking gpg-agent to convert a key and return it in the OpenPGP protected format. The export operation requires that the passphrase for the key is entered so that gpg-agent is able to change the protection from its internal format to the OpenPGP required format.

After using the latest gpg (2.2.3) to generate a new key, what I found was this:

ls ~/.gnupg/private-keys-v1.d/
107CEC1F80EC1BE3618AECB8958B495AAE3479B8.key DD98A07A5A38529EA809C9CA18F6CED15F4A9D32.key
gpg --list-keys
/Users/fejj/.gnupg/pubring.gpg
------------------------------
pub   rsa4096 2007-06-02 [SC] [expires: 2017-12-21]
      0EE5BE979282D80B9F7540F1CCD2ED94D21739E9
uid           [ unknown] Daniel Kahn Gillmor <dkg@fifthhorseman.net>
uid           [ unknown] Daniel Kahn Gillmor <dkg@aclu.org>
uid           [ unknown] Daniel Kahn Gillmor <dkg@debian.org>
uid           [ unknown] Daniel Kahn Gillmor <dkg@openflows.com>
uid           [ unknown] [jpeg image of size 3515]
sub   rsa2048 2008-06-19 [A] [expires: 2017-12-21]
sub   rsa4096 2016-12-21 [E] [expires: 2017-12-21]
sub   rsa4096 2016-12-21 [S] [expires: 2017-12-21]

pub   rsa2048 2014-10-19 [SC] [expires: 2020-12-31]
      D238EA65D64C67ED4C3073F28A861B1C7EFD60D9
uid           [ unknown] Werner Koch (Release Signing Key)

pub   dsa2048 2007-12-31 [SC] [expires: 2018-12-31]
      80615870F5BAD690333686D0F2AD85AC1E42B367
uid           [ unknown] Werner Koch <wk@gnupg.org>
uid           [ unknown] Werner Koch <wk@g10code.com>
uid           [ unknown] Werner Koch <werner@eifzilla.de>
sub   ed25519 2017-01-01 [S]
sub   cv25519 2017-01-01 [E] [expires: 2018-12-30]
sub   rsa2048 2017-01-01 [E] [expires: 2018-12-30]
sub   dsa1024 2011-11-02 [S]

pub   rsa2048 2017-12-07 [SC] [expires: 2019-12-07]
      35D5139FB87EB9CAF739F50ACA34B5B1F960CFBD
uid           [ultimate] Jeffrey Stedfast <jestedfa@microsoft.com>
sub   rsa2048 2017-12-07 [E] [expires: 2019-12-07]

and

gpg --list-secret-keys
/Users/fejj/.gnupg/pubring.gpg
------------------------------
sec   rsa2048 2017-12-07 [SC] [expires: 2019-12-07]
      35D5139FB87EB9CAF739F50ACA34B5B1F960CFBD
uid           [ultimate] Jeffrey Stedfast <jestedfa@microsoft.com>
ssb   rsa2048 2017-12-07 [E] [expires: 2019-12-07]

I was expecting that the file names in the private-keys-v1.d directory would match the fingerprints/key ids of my secret key(s), but that does not appear to be the case? Are they just random file names?

Opening the files in a text editor, they did not appear to be standard OpenPGP keyring bundle files. I'm not sure what format they were in.

Also, as you may have noticed, gpg --list-keys and --list-secret-keys is still opening pubring.gpg???

Compufreak345 commented 6 years ago

Also, as you may have noticed, gpg --list-keys and --list-secret-keys is stilling opening pubring.gpg???

This can be explained by this:

If no pubring.gpg is found, gpg defaults to the new keybox format and creates a pubring.kbx keybox file. If such a keybox file already exists, for example due to the use of gpgsm, it will also be used for OpenPGP keys. However, if a pubring.gpg is found and no keybox file with OpenPGP keys exists, the old pubring.gpg will be used.

https://www.gnupg.org/faq/whats-new-in-2.1.html Unfortunately this is a solution/workaround for pubring, but not for secring:

To ease the migration to the no-secring method, gpg detects the presence of a secring.gpg and converts the keys on-the-fly to the the key store of gpg-agent (this is the private-keys-v1.d directory below the GnuPG home directory (~/.gnupg)). This is done only once and an existing secring.gpg is then not anymore touched by gpg. This allows co-existence of older GnuPG versions with GnuPG 2.1. However, any change to the private keys using the new gpg will not show up when using pre-2.1 versions of GnuPG and vice versa.

Regarding those filenames and contents, I have no clue :0

jstedfast commented 6 years ago

Ah, thanks, I missed that.

jstedfast commented 6 years ago

Here's what Werner had to say:

Hi Jeff,

On Thu,  7 Dec 2017 16:52, gnupg-devel@gnupg.org said:

1. What is the file format of the *.key files in the private-keys-v1.d directory?

See gnupg/agent/keyformat.txt (also copied below). But note that this is
a private property of GnuPG and you should take care when accessing them
directky.

2. How are these files named? Does the file name correspond with a
fingerprint / key id?

this is the the keygrip with the suffix ".key".  To view the keygrip,
use

  gpg --with-colons -K

and grep for the "grp" records.  Such records follow after "sec", "ssb",
"pub", or "sub" records.  To get them in a huma readable format use

  gpg --with-keygrip -K

3. Does gpg still use pubring.gpg?

As long as there is no pubring.kbx and existing pubring.gpg will be
used.  If you have a pubring.kbx from gnupg 2.0 (gpgsm always used this)
but that one has no OpenPGP key and existing pubring.gpg is also used.

  kbxutil pubring.kbx | head

shows a flag line which indicates whether an openPGP key exists.

I'm asking these questions because I have a C# email library that uses Bouncy Castle for OpenPGP support that I'm trying to make interoperable with GnuPG as much as I can.

Hmmm.  I can't give an offical guarantee that the format will always be
the same.  The keygrip is computed by a function in Libgcrypt.

Shalom-Salam,

   Werner

==========
Some notes on the format of the secret keys used with gpg-agent.

* Location of keys

The secret keys[1] are stored on a per file basis in a directory below
the ~/.gnupg home directory.  This directory is named

   private-keys-v1.d

and should have permissions 700.

The secret keys are stored in files with a name matching the
hexadecimal representation of the keygrip[2] and suffixed with ".key".

* Extended Private Key Format

GnuPG 2.3+ will use a new format to store private keys that is both
more flexible and easier to read and edit by human beings.  The new
format stores name,value-pairs using the common mail and http header
convention.  Example (here indented with two spaces):

  Description: Key to sign all GnuPG released tarballs.
    The key is actually stored on a smart card.
  Use-for-ssh: yes
  OpenSSH-cert: long base64 encoded string wrapped so that this
    key file can be easily edited with a standard editor.
  Key: (shadowed-private-key
    (rsa
    (n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900
    2961D8AEA153424DC851EF13B83AC64FBE365C59DC1BD3E83017C90D4365B4
    83E02859FC13DB5842A00E969480DB96CE6F7D1C03600392B8E08EF0C01FC7
    19F9F9086B25AD39B4F1C2A2DF3E2BE317110CFFF21D4A11455508FE407997
    601260816C8422297C0637BB291C3A079B9CB38A92CE9E551F80AA0EBF4F0E
    72C3F250461E4D31F23A7087857FC8438324A013634563D34EFDDCBF2EA80D
    F9662C9CCD4BEF2522D8BDFED24CEF78DC6B309317407EAC576D889F88ADA0
    8C4FFB480981FB68C5C6CA27503381D41018E6CDC52AAAE46B166BDC10637A
    E186A02BA2497FDC5D1221#)
    (e #00010001#)
    (shadowed t1-v1
     (#D2760001240102000005000011730000# OPENPGP.1)
    )))

GnuPG 2.2 is able to read and update keys using the new format, but
will not create new files using the new format.  Furthermore, it only
makes use of the value stored under the name 'Key:'.

Keys in the extended format can be recognized by looking at the first
byte of the file.  If it starts with a '(' it is a naked S-expression,
otherwise it is a key in extended format.

** Names

A name must start with a letter and end with a colon.  Valid
characters are all ASCII letters, numbers and the hyphen.  Comparison
of names is done case insensitively.  Names may be used several times
to represent an array of values.

The name "Key:" is special in that it may occur only once and the
associated value holds the actual S-expression with the cryptographic
key.  The S-expression is formatted using the 'Advanced Format'
(GCRYSEXP_FMT_ADVANCED) that avoids non-printable characters so that
the file can be easily inspected and edited.  See section 'Private Key
Format' below for details.

** Values

Values are UTF-8 encoded strings.  Values can be wrapped at any point,
and continued in the next line indicated by leading whitespace.  A
continuation line with one leading space does not introduce a blank so
that the lines can be effectively concatenated.  A blank line as part
of a continuation line encodes a newline.

** Comments

Lines containing only whitespace, and lines starting with whitespace
followed by '#' are considered to be comments and are ignored.

* Private Key Format
** Unprotected Private Key Format

The content of the file is an S-Expression like the ones used with
Libgcrypt.  Here is an example of an unprotected file:

(private-key
(rsa
  (n #00e0ce9..[some bytes not shown]..51#)
  (e #010001#)
  (d #046129F..[some bytes not shown]..81#)
  (p #00e861b..[some bytes not shown]..f1#)
  (q #00f7a7c..[some bytes not shown]..61#)
  (u #304559a..[some bytes not shown]..9b#)
)
(created-at timestamp)
(uri http://foo.bar x-foo:whatever_you_want)
(comment whatever)
)

"comment", "created-at" and "uri" are optional.  "comment" is
currently used to keep track of ssh key comments. "created-at" is used
to keep track of the creation time stamp used with OpenPGP keys; it is
optional but required for some operations to calculate the fingerprint
of the key.  This timestamp should be a string with the number of
seconds since Epoch or an ISO time string (yyyymmddThhmmss).

** Protected Private Key Format

A protected key is like this:

(protected-private-key
   (rsa
    (n #00e0ce9..[some bytes not shown]..51#)
    (e #010001#)
    (protected mode (parms) encrypted_octet_string)
    (protected-at <isotimestamp>)
   )
   (uri http://foo.bar x-foo:whatever_you_want)
   (comment whatever)
)

In this scheme the encrypted_octet_string is encrypted according to
the algorithm described after the keyword protected; most protection
algorithms need some parameters, which are given in a list before the
encrypted_octet_string.  The result of the decryption process is a
list of the secret key parameters.  The protected-at expression is
optional; the isotimestamp is 15 bytes long (e.g. "19610711T172000").

The currently defined protection modes are:

*** openpgp-s2k3-sha1-aes-cbc

  This describes an algorithm using AES in CBC mode for
  encryption, SHA-1 for integrity protection and the String to Key
  algorithm 3 from OpenPGP (rfc4880).

  Example:

  (protected openpgp-s2k3-sha1-aes-cbc
    ((sha1 16byte_salt no_of_iterations) 16byte_iv)
    encrypted_octet_string
  )

  The encrypted_octet string should yield this S-Exp (in canonical
  representation) after decryption:

  (
   (
    (d #046129F..[some bytes not shown]..81#)
    (p #00e861b..[some bytes not shown]..f1#)
    (q #00f7a7c..[some bytes not shown]..61#)
    (u #304559a..[some bytes not shown]..9b#)
   )
   (hash sha1 #...[hashvalue]...#)
  )

  For padding reasons, random bytes are appended to this list - they can
  easily be stripped by looking for the end of the list.

  The hash is calculated on the concatenation of the public key and
  secret key parameter lists: i.e. it is required to hash the
  concatenation of these 6 canonical encoded lists for RSA, including
  the parenthesis, the algorithm keyword and (if used) the protected-at
  list.

  (rsa
   (n #00e0ce9..[some bytes not shown]..51#)
   (e #010001#)
   (d #046129F..[some bytes not shown]..81#)
   (p #00e861b..[some bytes not shown]..f1#)
   (q #00f7a7c..[some bytes not shown]..61#)
   (u #304559a..[some bytes not shown]..9b#)
   (protected-at "18950523T000000")
  )

  After decryption the hash must be recalculated and compared against
  the stored one - If they don't match the integrity of the key is not
  given.

*** openpgp-s2k3-ocb-aes

  This describes an algorithm using AES-128 in OCB mode, a nonce
  of 96 bit, a taglen of 128 bit, and the String to Key algorithm 3
  from OpenPGP (rfc4880).

  Example:

  (protected openpgp-s2k3-ocb-aes
    ((sha1 16byte_salt no_of_iterations) 12byte_nonce)
    encrypted_octet_string
  )

  The encrypted_octet string should yield this S-Exp (in canonical
  representation) after decryption:

  (
   (
    (d #046129F..[some bytes not shown]..81#)
    (p #00e861b..[some bytes not shown]..f1#)
    (q #00f7a7c..[some bytes not shown]..61#)
    (u #304559a..[some bytes not shown]..9b#)
   )
  )

  For padding reasons, random bytes may be appended to this list -
  they can easily be stripped by looking for the end of the list.

  The associated data required for this protection mode is the list
  formiing the public key parameters.  For the above example this is
  is this canonical encoded S-expression:

  (rsa
   (n #00e0ce9..[some bytes not shown]..51#)
   (e #010001#)
   (protected-at "18950523T000000")
  )

*** openpgp-native

  This is a wrapper around the OpenPGP Private Key Transport format
  which resembles the standard OpenPGP format and allows the use of an
  existing key without re-encrypting to the default protection format.

  Example:

  (protected openpgp-native
    (openpgp-private-key
     (version V)
     (algo PUBKEYALGO)
     (skey _ P1 _ P2 _ P3 ... e PN)
     (csum n)
     (protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT)))

  Note that the public key paramaters in SKEY are duplicated and
  should be identical to their copies in the standard parameter
  elements.  Here is an example of an entire protected private key
  using this format:

  (protected-private-key
   (rsa
    (n #00e0ce9..[some bytes not shown]..51#)
    (e #010001#)
    (protected openpgp-native
     (openpgp-private-key
      (version 4)
      (algo rsa)
      (skey _ #00e0ce9..[some bytes not shown]..51#
            _ #010001#
            e #.........................#)
      (protection sha1 aes #aabbccddeeff00112233445566778899#
                  3 sha1 #2596f93e85f41e53# 3:190))))
   (uri http://foo.bar x-foo:whatever_you_want)
   (comment whatever))

** Shadowed Private Key Format

To keep track of keys stored on IC cards we use a third format for
private kyes which are called shadow keys as they are only a reference
to keys stored on a token:

(shadowed-private-key
   (rsa
    (n #00e0ce9..[some bytes not shown]..51#)
    (e #010001#)
    (shadowed protocol (info))
   )
   (uri http://foo.bar x-foo:whatever_you_want)
   (comment whatever)
)

The currently used protocol is "ti-v1" (token info version 1).  The
second list with the information has this layout:

(card_serial_number id_string_of_key fixed_pin_length)

FIXED_PIN_LENGTH is optional.  It can be used to store the length of
the PIN; a value of 0 indicates that this information is not
available.  The rationale for this field is that some pinpad equipped
readers don't allow passing a variable length PIN.

More items may be added to the list.

** OpenPGP Private Key Transfer Format

This format is used to transfer keys between gpg and gpg-agent.

(openpgp-private-key
  (version V)
  (algo PUBKEYALGO)
  (curve CURVENAME)
  (skey _ P1 _ P2 _ P3 ... e PN)
  (csum n)
  (protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT))

* V is the packet version number (3 or 4).
* PUBKEYALGO is a Libgcrypt algo name
* CURVENAME is the name of the curve - only used with ECC.
* P1 .. PN are the parameters; the public parameters are never encrypted
   the secrect key parameters are encrypted if the "protection" list is
   given.  To make this more explicit each parameter is preceded by a
   flag "_" for cleartext or "e" for encrypted text.
* CSUM is the deprecated 16 bit checksum as defined by OpenPGP.  This
   is an optional element.
* If PROTTYPE is "sha1" the new style SHA1 checksum is used if it is "sum"
   the old 16 bit checksum (above) is used and if it is "none" no
   protection at all is used.
* PROTALGO is a Libgcrypt style cipher algorithm name
* IV is the initialization verctor.
* S2KMODE is the value from RFC-4880.
* S2KHASH is a libgcrypt style hash algorithm identifier.
* S2KSALT is the 8 byte salt
* S2KCOUNT is the count value from RFC-4880.

** Persistent Passphrase Format

Note: That this has not yet been implemented.

To allow persistent storage of cached passphrases we use a scheme
similar to the private-key storage format.  This is a master
passphrase format where each file may protect several secrets under
one master passphrase.  It is possible to have several of those files
each protected by a dedicated master passphrase.  Clear text keywords
allow listing the available protected passphrases.

The name of the files with these protected secrets have this form:
pw-<string>.dat.  STRING may be an arbitrary string, as a default name
for the passphrase storage the name "pw-default.dat" is suggested.

(protected-shared-secret
   ((desc descriptive_text)
    (key [key_1] (keyword_1 keyword_2 keyword_n))
    (key [key_2] (keyword_21 keyword_22 keyword_2n))
    (key [key_n] (keyword_n1 keyword_n2 keyword_nn))
    (protected mode (parms) encrypted_octet_string)
    (protected-at <isotimestamp>)
   )
)

After decryption the encrypted_octet_string yields this S-expression:

(
(
  (value key_1 value_1)
  (value key_2 value_2)
  (value key_n value_n)
)
(hash sha1 #...[hashvalue]...#)
)

The "descriptive_text" is displayed with the prompt to enter the
unprotection passphrase.

KEY_1 to KEY_N are unique identifiers for the shared secret, for
example an URI.  In case this information should be kept confidential
as well, they may not appear in the unprotected part; however they are
mandatory in the encrypted_octet_string.  The list of keywords is
optional.  The oder of the "key" lists and the order of the "value"
lists mut match, that is the first "key"-list is associated with the
first "value" list in the encrypted_octet_string.

The protection mode etc. is identical to the protection mode as
described for the private key format.

list of the secret key parameters.  The protected-at expression is
optional; the isotimestamp is 15 bytes long (e.g. "19610711T172000").

The "hash" in the encrypted_octet_string is calculated on the
concatenation of the key list and value lists: i.e it is required to
hash the concatenation of all these lists, including the
parenthesis and (if used) the protected-at list.

Example:

(protected-shared-secret
   ((desc "List of system passphrases")
    (key "uid-1002" ("Knuth" "Donald Ervin Knuth"))
    (key "uid-1001" ("Dijkstra" "Edsgar Wybe Dijkstra"))
    (key)
    (protected mode (parms) encrypted_octet_string)
    (protected-at "20100915T111722")
   )
)

with "encrypted_octet_string" decoding to:

(
(
  (value 4:1002 "signal flags at the lock")
  (value 4:1001 "taocp")
  (value 1:0    "premature optimization is the root of all evil")
)
(hash sha1 #0102030405060708091011121314151617181920#)
)

To compute the hash this S-expression (in canoncical format) was
hashed:

   ((desc "List of system passphrases")
    (key "uid-1002" ("Knuth" "Donald Ervin Knuth"))
    (key "uid-1001" ("Dijkstra" "Edsgar Wybe Dijkstra"))
    (key)
    (value 4:1002 "signal flags at the lock")
    (value 4:1001 "taocp")
    (value 1:0    "premature optimization is the root of all evil")
    (protected-at "20100915T111722")
   )

* Notes

[1] I usually use the terms private and secret key exchangeable but prefer the
term secret key because it can be visually be better distinguished
From the term public key.

[2] The keygrip is a unique identifier for a key pair, it is
independent of any protocol, so that the same key can be used with
different protocols.  PKCS-15 calls this a subjectKeyHash; it can be
calculated using Libgcrypt's gcry_pk_get_keygrip ().

[3] Even when canonical representation are required we will show the
S-expression here in a more readable representation.

--
Die Gedanken sind frei.  Ausnahmen regelt ein Bundesgesetz.
jstedfast commented 6 years ago

I find this the most worrying:

I'm asking these questions because I have a C# email library that uses Bouncy Castle for OpenPGP support that I'm trying to make interoperable with GnuPG as much as I can.

Hmmm. I can't give an offical guarantee that the format will always be the same. The keygrip is computed by a function in Libgcrypt.

jstedfast commented 6 years ago

I'm just going to close this because I don't think this is feasible.

DanielHabenicht commented 1 year ago

Should the README be updated to state that newer Versions of GnuPG won't work with MimeKit anymore?

jstedfast commented 1 year ago

Yea, I probably need to find a way to present this info in the documentation. Any suggestions?

DanielHabenicht commented 1 year ago

Probably its best to just move the documentation part for gnupg to another document and link it from the readme. Stating that it was supported until PGP4Win<=2.3.4 or GnuPG<=2.0.30.

I will update this thread with a Migration Guide

DanielHabenicht commented 1 year ago

On that note there are two Documentation references, one on the website and one in this repo? Which one is the "leading" data source?

jstedfast commented 1 year ago

The docs on github are always more up-to-date than mimekit.net/docs