Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

gpgme: selecting a private key when signing data

Tags:

gnupg

gpgme

Using gpgme (the development library for gpg/gnupg), I'm trying to sign some data. In the key ring I have more then 1 private key so I want to select the correct one. This fails with: "Unusable secret key (117440566)". The key was generated with gnupg2 itself. When using gnupg the problem also occurs.

sec   1024R/14B7E8E6 2015-05-27
      Key fingerprint = 95C7 6C5E F839 43DA 2F32  2CF4 D2C2 5144 14B7 E8E6
uid                  testkey2 (testkey2) <[email protected]>
ssb   1024R/ED8059EA 2015-05-27

pub  rsa1024/14B7E8E6
     created: 2015-05-27  expires: never       usage: SC
     trust: ultimate      validity: ultimate
sub  rsa1024/ED8059EA
     created: 2015-05-27  expires: never       usage: E
sub  rsa1024/74D6F5C6
     created: 2015-05-31  expires: never       usage: S

First I check if there's a private key for the key selected:

gpgme_op_keylist_start(..., ..., 1);
if (gpgme_op_keylist_nex() == GPG_ERR_NO_ERROR) { proceed }

do the signing:

gpgme_new()
gpgme_set_pinentry_mode(GPGME_PINENTRY_MODE_LOOPBACK) // yes i installed v2.1
gpgme_set_passphrase_cb()
/* ...binary to gpgme_data_t... */
gpgme_data_set_encoding(GPGME_DATA_ENCODING_BINARY)
gpgme_signers_clear()
gpgme_signers_add()   // <- that key that I checked for existance earlier
if (gpgme_signers_count() != 1) { fail(); } // sanity check
gpgme_op_encrypt_sign(ctx, recipient, GPGME_ENCRYPT_ALWAYS_TRUST /* FIXME */, data_in, sig);

Now that gpgme_op_encrypt_sign always fails with that "Unusable secret key (117440566)" error. Any tips/hints?

Software versions:

gnupg    1.4.18-7
gnupg-agent      2.1.4-1
gnupg2   2.1.4-1
libgpgme++2      4:4.14.2-2+b1
libgpgme11:amd64         1.5.1-6
libgpgme11-dev   1.5.1-6
python-gnupginterface    0.3.2-9.1

I enabled debug-tracing but it doesn't help me much:

<0x1927>  gpgme_debug: level=4
<0x1927>  gpgme_check_version: call: 0=(nil), req_version=(null), VERSION=1.5.1
<0x1927>  gpgme_check_version_internal: call: 0=(nil), req_version=(null), offset_sig_validity=60
<0x1927>  gpgme_set_locale: enter: ctx=(nil), category=0, value=C
<0x1927>  gpgme_set_locale: leave
<0x1927>  gpgme_set_locale: enter: ctx=(nil), category=5, value=C
<0x1927>  gpgme_set_locale: leave
<0x1927>  gpgme-dinfo: gpgconf='/usr/bin/gpgconf'
<0x1927>  gpgme-dinfo:     gpg='/usr/bin/gpg2'
<0x1927>  gpgme-dinfo:   gpgsm='/usr/bin/gpgsm'
<0x1927>  gpgme-dinfo: homedir='/home/folkert/.gnupg'
<0x1927>  gpgme-dinfo:   agent='/home/folkert/.gnupg/S.gpg-agent'
<0x1927>  gpgme-dinfo:   uisrv='/home/folkert/.gnupg/S.uiserver'
<0x1927>  gpgme_new: enter: r_ctx=0x7fff5afd07a8
<0x1927>  gpgme_new: leave: ctx=0x20c0810
<0x1927>  gpgme_op_keylist_start: enter: ctx=0x20c0810, pattern=0BF38589, secret_only=1
<0x1927>    _gpgme_add_io_cb: call: ctx=0x20c0810, fd 4, dir=1 -> tag=0x20c26b0
<0x1927>    _gpgme_add_io_cb: call: ctx=0x20c0810, fd 6, dir=1 -> tag=0x20c2800
<0x1927>    gpgme:gpg_io_event: call: gpg=0x20c0c10, event 0x7fd8b1a20ad0, type 0, type_data (nil)
<0x1927>  gpgme_op_keylist_start: leave
<0x1927>  gpgme_op_keylist_next: enter: ctx=0x20c0810
<0x1927>    _gpgme_run_io_cb: call: item=0x20c2820, need to check
<0x1927>    _gpgme_run_io_cb: call: item=0x20c2820, handler (0x20c0c10, 6)
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c0810, key = (nil), line = sec:u:2048:1:CC73A8A60BF38589:1433443717:::u:::scESC::::::
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c0810, key = 0x20c2850, line = fpr:::::::::20CD3FF80DA6C1E46CD9F135CC73A8A60BF38589:
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c0810, key = 0x20c2850, line = uid:u::::1433443717::9963CFDE0C8920AD077B06A281992C4008E67E4F::testkey3 (testkey3) <[email protected]>:
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c0810, key = 0x20c2850, line = ssb:u:2048:1:22317805D48C1491:1433443717::::::e::::::
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c0810, key = 0x20c2850, line = fpr:::::::::FB6FFB7D8BEC710A745DE86C22317805D48C1491:
<0x1927>    _gpgme_run_io_cb: call: item=0x20c26d0, need to check
<0x1927>    _gpgme_run_io_cb: call: item=0x20c26d0, handler (0x20c0c10, 4)
<0x1927>      _gpgme_remove_io_cb: call: data=0x20c26b0, setting fd 0x4 (item=0x20c26d0) done
<0x1927>    _gpgme_run_io_cb: call: item=0x20c2820, need to check
<0x1927>    _gpgme_run_io_cb: call: item=0x20c2820, handler (0x20c0c10, 6)
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c0810, key = 0x20c2850, line = (null)
<0x1927>    gpgme:gpg_io_event: call: gpg=0x20c0c10, event 0x7fd8b1a20ad0, type 2, type_data 0x20c2850
<0x1927>      _gpgme_remove_io_cb: call: data=0x20c2800, setting fd 0x6 (item=0x20c2820) done
<0x1927>    gpgme:gpg_io_event: call: gpg=0x20c0c10, event 0x7fd8b1a20ad0, type 1, type_data 0x7fff5afd0700
<0x1927>  gpgme_op_keylist_next: leave: key=0x20c2850 (20CD3FF80DA6C1E46CD9F135CC73A8A60BF38589)
<0x1927>  gpgme_release: call: ctx=0x20c0810
<0x1927>  gpgme_new: enter: r_ctx=0x7fff5afd0818
<0x1927>  gpgme_new: leave: ctx=0x20c2210
<0x1927>  gpgme_op_keylist_start: enter: ctx=0x20c2210, pattern=4BE78BDCF3F5352CF624A6DF3AD6F8118300CC02, secret_only=0
<0x1927>    _gpgme_add_io_cb: call: ctx=0x20c2210, fd 4, dir=1 -> tag=0x20c1f50
<0x1927>    _gpgme_add_io_cb: call: ctx=0x20c2210, fd 6, dir=1 -> tag=0x20c1fa0
<0x1927>    gpgme:gpg_io_event: call: gpg=0x20c0c10, event 0x7fd8b1a20ad0, type 0, type_data (nil)
<0x1927>  gpgme_op_keylist_start: leave
<0x1927>  gpgme_op_keylist_next: enter: ctx=0x20c2210
<0x1927>    _gpgme_run_io_cb: call: item=0x20c1fc0, need to check
<0x1927>    _gpgme_run_io_cb: call: item=0x20c1fc0, handler (0x20c0c10, 6)
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c2210, key = (nil), line = tru::0:1433443869:2410285847:3:1:5
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c2210, key = (nil), line = pub:-:1024:17:3AD6F8118300CC02:1039074767:::-:::scESC::::::
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c2210, key = 0x20c2b70, line = fpr:::::::::4BE78BDCF3F5352CF624A6DF3AD6F8118300CC02:
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c2210, key = 0x20c2b70, line = uid:-::::1203999932::275AAD3E991F1962AD510CC96760907BE70FE668::Bla <bla@com>:
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c2210, key = 0x20c2b70, line = uid:-::::1203999938::59689891229F1817EF66BFC63D9D0BB2F45F5209::Bla <bla@com>:
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c2210, key = 0x20c2b70, line = uid:r::::::8A709552E7AB85B53DDAE18A48C0978E5EBF5547::Bla <bla@com>:
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c2210, key = 0x20c2b70, line = sub:-:2048:16:942E547C12A6B1C2:1039075030::::::e::::::
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c2210, key = 0x20c2b70, line = fpr:::::::::E69EF5226BBF7EC14F1D7D96942E547C12A6B1C2:
<0x1927>    _gpgme_run_io_cb: call: item=0x20c1f70, need to check
<0x1927>    _gpgme_run_io_cb: call: item=0x20c1f70, handler (0x20c0c10, 4)
<0x1927>      _gpgme_remove_io_cb: call: data=0x20c1f50, setting fd 0x4 (item=0x20c1f70) done
<0x1927>    _gpgme_run_io_cb: call: item=0x20c1fc0, need to check
<0x1927>    _gpgme_run_io_cb: call: item=0x20c1fc0, handler (0x20c0c10, 6)
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c2210, key = 0x20c2b70, line = (null)
<0x1927>    gpgme:gpg_io_event: call: gpg=0x20c0c10, event 0x7fd8b1a20ad0, type 2, type_data 0x20c2b70
<0x1927>      _gpgme_remove_io_cb: call: data=0x20c1fa0, setting fd 0x6 (item=0x20c1fc0) done
<0x1927>    gpgme:gpg_io_event: call: gpg=0x20c0c10, event 0x7fd8b1a20ad0, type 1, type_data 0x7fff5afd0760
<0x1927>  gpgme_op_keylist_next: leave: key=0x20c2b70 (4BE78BDCF3F5352CF624A6DF3AD6F8118300CC02)
<0x1927>  gpgme_release: call: ctx=0x20c2210
<0x1927>  gpgme_new: enter: r_ctx=0x7fff5afd07c8
<0x1927>  gpgme_new: leave: ctx=0x20c2550
<0x1927>  gpgme_set_passphrase_cb: call: ctx=0x20c2550, passphrase_cb=(nil)/(nil)
<0x1927>  gpgme_set_pinentry_mode: call: ctx=0x20c2550, pinentry_mode=4
<0x1927>  gpgme_set_passphrase_cb: call: ctx=0x20c2550, passphrase_cb=0x403420/0x20c0058
<0x1927>  gpgme_set_passphrase_cb: call: ctx=0x20c2550, passphrase_cb=0x403420/0x20c0058
<0x1927>  gpgme_new: enter: r_ctx=0x7fff5afd0768
<0x1927>  gpgme_new: leave: ctx=0x20c0810
<0x1927>  gpgme_op_keylist_start: enter: ctx=0x20c0810, pattern=0BF38589, secret_only=0
<0x1927>    _gpgme_add_io_cb: call: ctx=0x20c0810, fd 4, dir=1 -> tag=0x20c4fd0
<0x1927>    _gpgme_add_io_cb: call: ctx=0x20c0810, fd 6, dir=1 -> tag=0x20c5120
<0x1927>    gpgme:gpg_io_event: call: gpg=0x20c1d50, event 0x7fd8b1a20ad0, type 0, type_data (nil)
<0x1927>  gpgme_op_keylist_start: leave
<0x1927>  gpgme_op_keylist_next: enter: ctx=0x20c0810
<0x1927>    _gpgme_run_io_cb: call: item=0x20c5140, need to check
<0x1927>    _gpgme_run_io_cb: call: item=0x20c5140, handler (0x20c1d50, 6)
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c0810, key = (nil), line = tru::0:1433443869:2410285847:3:1:5
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c0810, key = (nil), line = pub:u:2048:1:CC73A8A60BF38589:1433443717:::u:::scESC:::::: 
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c0810, key = 0x20c5170, line = fpr:::::::::20CD3FF80DA6C1E46CD9F135CC73A8A60BF38589:  
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c0810, key = 0x20c5170, line = uid:u::::1433443717::9963CFDE0C8920AD077B06A281992C4008E67E4F::testkey3 (testkey3) <[email protected]>:
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c0810, key = 0x20c5170, line = sub:u:2048:1:22317805D48C1491:1433443717::::::e::::::
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c0810, key = 0x20c5170, line = fpr:::::::::FB6FFB7D8BEC710A745DE86C22317805D48C1491:
<0x1927>    _gpgme_run_io_cb: call: item=0x20c4ff0, need to check
<0x1927>    _gpgme_run_io_cb: call: item=0x20c4ff0, handler (0x20c1d50, 4)
<0x1927>      _gpgme_remove_io_cb: call: data=0x20c4fd0, setting fd 0x4 (item=0x20c4ff0) done
<0x1927>    _gpgme_run_io_cb: call: item=0x20c5140, need to check
<0x1927>    _gpgme_run_io_cb: call: item=0x20c5140, handler (0x20c1d50, 6)
<0x1927>    gpgme:keylist_colon_handler: call: ctx=0x20c0810, key = 0x20c5170, line = (null)
<0x1927>    gpgme:gpg_io_event: call: gpg=0x20c1d50, event 0x7fd8b1a20ad0, type 2, type_data 0x20c5170
<0x1927>      _gpgme_remove_io_cb: call: data=0x20c5120, setting fd 0x6 (item=0x20c5140) done
<0x1927>    gpgme:gpg_io_event: call: gpg=0x20c1d50, event 0x7fd8b1a20ad0, type 1, type_data 0x7fff5afd06c0
<0x1927>  gpgme_op_keylist_next: leave: key=0x20c5170 (20CD3FF80DA6C1E46CD9F135CC73A8A60BF38589)
<0x1927>  gpgme_release: call: ctx=0x20c0810
<0x1927>  gpgme_signers_clear: call: ctx=0x20c2550
<0x1927>  gpgme_signers_add: enter: ctx=0x20c2550, key=0x20c2850 (20CD3FF80DA6C1E46CD9F135CC73A8A60BF38589)
<0x1927>  gpgme_signers_add: leave
<0x1927>  gpgme_op_encrypt_sign: enter: ctx=0x20c2550, flags=0x1, plain=0x20c2ed0, cipher=0x20c3f20
<0x1927>  gpgme_op_encrypt_sign: check: ctx=0x20c2550, recipient[0] = 0x20c5170 (20CD3FF80DA6C1E46CD9F135CC73A8A60BF38589)
<0x1927>    gpgme_sig_notation_get: call: ctx=0x20c2550, ctx->sig_notations=(nil)
<0x1927>    _gpgme_add_io_cb: call: ctx=0x20c2550, fd 4, dir=1 -> tag=0x20c2070
<0x1927>    _gpgme_add_io_cb: call: ctx=0x20c2550, fd 8, dir=1 -> tag=0x20c21c0
<0x1927>    _gpgme_add_io_cb: call: ctx=0x20c2550, fd 11, dir=0 -> tag=0x20c2210
<0x1927>    gpgme:gpg_io_event: call: gpg=0x20c1d50, event 0x7fd8b1a20ad0, type 0, type_data (nil)
<0x1927>    _gpgme_run_io_cb: call: item=0x20c2230, need to check
<0x1927>    _gpgme_run_io_cb: call: item=0x20c2230, handler (0x20c2ed0, 11)
<0x1927>    _gpgme_data_outbound_handler: enter: dh=0x20c2ed0, fd=0xb
<0x1927>    _gpgme_data_outbound_handler: leave
<0x1927>    _gpgme_run_io_cb: call: item=0x20c2230, need to check
<0x1927>    _gpgme_run_io_cb: call: item=0x20c2230, handler (0x20c2ed0, 11)
<0x1927>    _gpgme_data_outbound_handler: enter: dh=0x20c2ed0, fd=0xb
<0x1927>        _gpgme_remove_io_cb: call: data=0x20c2210, setting fd 0xb (item=0x20c2230) done
<0x1927>    _gpgme_data_outbound_handler: leave
<0x1927>    _gpgme_run_io_cb: call: item=0x20c2090, need to check
<0x1927>    _gpgme_run_io_cb: call: item=0x20c2090, handler (0x20c1d50, 4)
<0x1927>    _gpgme_run_io_cb: call: item=0x20c2090, need to check
<0x1927>    _gpgme_run_io_cb: call: item=0x20c2090, handler (0x20c1d50, 4)
<0x1927>    _gpgme_cancel_with_err: enter: ctx=0x20c2550, ctx_err=117440566, op_err=0
<0x1927>        _gpgme_remove_io_cb: call: data=0x20c2070, setting fd 0x4 (item=0x20c2090) done
<0x1927>        _gpgme_remove_io_cb: call: data=0x20c21c0, setting fd 0x8 (item=0x20c21e0) done
<0x1927>      gpgme:gpg_io_event: call: gpg=0x20c1d50, event 0x7fd8b1a20ad0, type 1, type_data 0x7fff5afd06c0
<0x1927>    _gpgme_cancel_with_err: leave
<0x1927>  gpgme_op_encrypt_sign: error: Unusable secret key <GPGME>
<0x1927>  gpgme_release: call: ctx=0x20c2550

EDIT

As requested by @kylehuff, here's the code for the key selection:

search_key_result_t gpgme::find_key(const std::string & key_id, const bool priv_key_only, gpgme_key_t *k, std::string *const error)
  {
          error -> clear();

          *k = NULL;

          gpgme_ctx_t ctx = NULL;
          if (!my_gpgme_new(&ctx, false, error))
                  return SK_ERROR;

          gpgme_error_t err = gpgme_op_keylist_start(ctx, key_id.c_str(), priv_key_only ? 1 : 0);
          if (err != GPG_ERR_NO_ERROR)
          {
                  error -> append(format("Problem searching for %s: %s (%d)", key_id.c_str(), gpg_strerror(err), err));
                  gpgme_release(ctx);
                  return SK_ERROR;
          }

          err = gpgme_op_keylist_next(ctx, k);
          if (err == GPG_ERR_EOF)
                  return SK_NOT_FOUND;

          if (err != GPG_ERR_NO_ERROR)
          {
                  error -> append(format("Problem finding %s: %s (%d)", key_id.c_str(), gpg_strerror(err), err));
                  gpgme_release(ctx);
                  return SK_ERROR;
          }

          gpgme_release(ctx);

          return SK_FOUND;
  }

Then in the constructor I do:

std::string error;
  if (find_key(my_key_id, true, &my_key, &error) != SK_FOUND)
          error_exit(false, "Cannot find key %s: %s", my_key_id.c_str(), error.c_str());

and when it is time to sign:

          if (find_key(target_uid, false, &recipient[0], error) != SK_FOUND)
                  break;

          gpgme_signers_clear(ctx);
          err = gpgme_signers_add(ctx, my_key);
          if (err != GPG_ERR_NO_ERROR)
          {
                  error -> append(format("gpgme_signers_add(%s) failed: %s (%d)", my_key_id.c_str(), gpg_strerror(err), err));
                  break;
          }

          int n_signers = gpgme_signers_count(ctx);
          if (n_signers != 1)
          {
                  error -> append(format("Number of signers (%d) not expected number (1)", n_signers));
                  break;
          }

          err = gpgme_op_encrypt_sign(ctx, recipient, GPGME_ENCRYPT_ALWAYS_TRUST /* FIXME */, data_in, sig);
          if (err != GPG_ERR_NO_ERROR)
          {
                  error -> append(format("gpgme_op_encrypt failed: %s (%d)", gpg_strerror(err), err));
                  break;
          }

@kylehuff, is this what you requested? Thanks

EDIT

Here's the listing for 20CD3FF80DA6C1E46CD9F135CC73A8A60BF38589:

tru::0:1433443869:2410285847:3:1:5
pub:u:2048:1:CC73A8A60BF38589:2015-06-04:::u:testkey3 (testkey3) <[email protected]>::scESC:
sub:u:2048:1:22317805D48C1491:2015-06-04::::::e:

EDIT

folkert@travelmate:~$ gpg2 --local-user 14B7E8E6 --sign bla.txt

folkert@travelmate:~$ gpg --verify bla.txt.gpg gpg: Signature made Thu 18 Jun 2015 07:18:17 PM UTC using RSA key ID 74D6F5C6 gpg: Good signature from "testkey2 (testkey2) "

and with edit key I can see that 74d6f5c6 is indeed the sign sub-key:

sub 1024R/74D6F5C6 created: 2015-05-31 expires: never usage: S

So I'm a bit surprised that this doesn't work for gpgme. Not doing the find but directly the gpgme_get_key() gives the same error.

like image 711
Folkert van Heusden Avatar asked Jun 12 '15 09:06

Folkert van Heusden


People also ask

Do I need a passphrase for GPG key?

To help safeguard your key, GnuPG does not store your raw private key on disk. Instead it encrypts it using a symmetric encryption algorithm. That is why you need a passphrase to access the key.

Where does GPG store private keys?

Types of GPG keys The private GPG keys are encrypted and stored in the secret keyring, and public keys are maintained with certificates attesting to their trustworthiness in the public keyring. You can use the public key for the data encryption, and that encrypted data will be decrypted using the Private key.

Is it possible to use other public keys in GPG?

GPG would be pretty useless if you could not accept other public keys from people you wished to communicate with. You can import someone’s public key in a variety of ways. If you’ve obtained a public key from someone in a text file, GPG can import it with the following command:

How does GPG generate the keys?

At this point, gpg will generate the keys using entropy. Entropy describes the amount of unpredictability and nondeterminism that exists in a system. GPG needs this entropy to generate a secure set of keys. This process may take a long time depending on how active your system is and the keysize you selected.

How do I decrypt a GPG key?

You can press “CTRL-D” to signify the end of the message and GPG will decrypt it for you. Key Maintenance. There are a number of procedures that you may need to use on a regular basis to manage your key database. To list your available GPG keys that you have from other people, you can issue this command: gpg --list-keys

How do I search the key server from within GPG?

You can also search the key server from within GPG by typing the following: You can use this method of searching by name or email address. You can import keys that you find by following the prompts.


1 Answers

It is difficult to tell what is going on, even with the sample, but here are few possible scenarios -

Something being done in the method my_gpgme_new(&ctx, false, error) could be wayward. What is that function doing exactly? Why not use the standard gpgme_new method?

In the sample provided, you are using the function search_key_result_t gpgme::find_key(); is there a need for multiple signers? if so, at least for testing, why not simplify it and just use gpgme_get_key()? i.e.

  gpgme_error_t err;
  gpgme_key_t key;

  err = gpgme_get_key (ctx, key_string, &key, 1);
  if (err) {
    // .. error handling
  }
  gpgme_signers_clear();
  err = gpgme_signers_add (ctx, key);
  gpgme_key_unref (key);

  int n_signers = gpgme_signers_count(ctx);
  if (n_signers != 1) {
    // .. error handling
  }

  err = gpgme_op_encrypt_sign(ctx, recipient, ....);

Additionally with that method, it appears you are adding that method to the gpgme namesapce. Is it possible you trampling on in-built method in the gpgme namespace as well?

As for the recipient object, how is that constructed? It should be a null terminated gpgme_key_t structure of gpgme_key_ts, even if it is only a single recipient. i.e.

  gpgme_key_t recipients[2] = { NULL, NULL };
  err = gpgme_get_key (ctx, recipient_fpr.c_str(), &recipients[0], 0);
  if (err) { // .. error handling }

Alternately, if the recipients are provided as an array:

  gpgme_key_t recipients = new gpgme_key_t[recip_array.size()];
  err = gpgme_get_key (ctx, recipient_fpr.c_str(), &recipients[0], 0);
  if (err) { // .. error handling }
  recipients[recip_array.size()] = NULL; // null terminate the array
like image 193
kylehuff Avatar answered Oct 04 '22 18:10

kylehuff