Browse Source

HPCC-25072 Gpg keys from secrets and helm changes for code signing

Signed-off-by: Shamser Ahmed <shamser.ahmed@lexisnexis.com>

HPCC-25072 Changes following review and fix helm values

Signed-off-by: Shamser Ahmed <shamser.ahmed@lexisnexis.com>
Shamser Ahmed 4 năm trước cách đây
mục cha
commit
a22abfd8a2

+ 1 - 0
ecl/eclcc/CMakeLists.txt

@@ -48,6 +48,7 @@ include_directories (
          ./../../fs/dafsclient
          ./../../fs/dafsclient
          ./../../system/security/shared
          ./../../system/security/shared
          ./../../system/security/zcrypt
          ./../../system/security/zcrypt
+         ${HPCC_SOURCE_DIR}/system/codesigner
     )
     )
 
 
 ADD_DEFINITIONS( -D_CONSOLE )
 ADD_DEFINITIONS( -D_CONSOLE )

+ 6 - 1
ecl/eclcc/eclcc.cpp

@@ -58,6 +58,7 @@
 
 
 #include "reservedwords.hpp"
 #include "reservedwords.hpp"
 #include "eclcc.hpp"
 #include "eclcc.hpp"
+#include "codesigner.hpp"
 
 
 #ifndef _CONTAINERIZED
 #ifndef _CONTAINERIZED
 #include "environment.hpp"
 #include "environment.hpp"
@@ -526,6 +527,11 @@ int main(int argc, const char *argv[])
         queryLogMsgManager()->changeMonitorFilter(queryStderrLogMsgHandler(), filter);
         queryLogMsgManager()->changeMonitorFilter(queryStderrLogMsgHandler(), filter);
 #else
 #else
         setupContainerizedLogMsgHandler();
         setupContainerizedLogMsgHandler();
+        bool useChildProcesses = configuration->getPropInt("@useChildProcesses", false);
+        if (!useChildProcesses)  // If using eclcc in separate container (useChildProcesses==false),
+        {                        // it will need to create a directory for gpg and import keys from secrets
+            queryCodeSigner().initForContainer();
+        }
 #endif
 #endif
         exitCode = doMain(argc, argv);
         exitCode = doMain(argc, argv);
         stopPerformanceMonitor();
         stopPerformanceMonitor();
@@ -2103,7 +2109,6 @@ bool EclCC::processFiles()
         }
         }
     }
     }
 
 
-
     StringBuffer searchPath;
     StringBuffer searchPath;
     if (!optNoStdInc && stdIncludeLibraryPath.length())
     if (!optNoStdInc && stdIncludeLibraryPath.length())
         searchPath.append(stdIncludeLibraryPath).append(ENVSEPCHAR);
         searchPath.append(stdIncludeLibraryPath).append(ENVSEPCHAR);

+ 1 - 0
ecl/eclccserver/CMakeLists.txt

@@ -43,6 +43,7 @@ include_directories (
          ./../../common/dllserver 
          ./../../common/dllserver 
          ./../../system/jlib
          ./../../system/jlib
          ./../../system/security/shared
          ./../../system/security/shared
+         ${HPCC_SOURCE_DIR}/system/codesigner
     )
     )
 
 
 HPCC_ADD_EXECUTABLE ( eclccserver ${SRCS} ${INCLUDES} )
 HPCC_ADD_EXECUTABLE ( eclccserver ${SRCS} ${INCLUDES} )

+ 3 - 0
ecl/eclccserver/eclccserver.cpp

@@ -35,6 +35,7 @@
 #endif
 #endif
 #include <unordered_map>
 #include <unordered_map>
 #include <string>
 #include <string>
+#include "codesigner.hpp"
 
 
 static Owned<IPropertyTree> globals;
 static Owned<IPropertyTree> globals;
 static const char * * globalArgv = nullptr;
 static const char * * globalArgv = nullptr;
@@ -914,6 +915,8 @@ int main(int argc, const char *argv[])
 #endif
 #endif
 
 
 #ifdef _CONTAINERIZED
 #ifdef _CONTAINERIZED
+            queryCodeSigner().initForContainer();
+
             bool filtered = false;
             bool filtered = false;
             std::unordered_map<std::string, bool> listenQueues;
             std::unordered_map<std::string, bool> listenQueues;
             Owned<IPTreeIterator> listening = globals->getElements("listen");
             Owned<IPTreeIterator> listening = globals->getElements("listen");

+ 3 - 0
esp/services/ws_codesign/ws_codesignService.cpp

@@ -35,6 +35,9 @@ void Cws_codesignEx::init(IPropertyTree *cfg, const char *process, const char *s
     StringBuffer xpath;
     StringBuffer xpath;
     xpath.appendf("Software/EspProcess[@name=\"%s\"]/EspService[@name=\"%s\"]", process, service);
     xpath.appendf("Software/EspProcess[@name=\"%s\"]/EspService[@name=\"%s\"]", process, service);
     m_serviceCfg.setown(cfg->getPropTree(xpath.str()));
     m_serviceCfg.setown(cfg->getPropTree(xpath.str()));
+#ifdef _CONTAINERIZED
+    queryCodeSigner().initForContainer();
+#endif
 }
 }
 
 
 bool Cws_codesignEx::onSign(IEspContext &context, IEspSignRequest &req, IEspSignResponse &resp)
 bool Cws_codesignEx::onSign(IEspContext &context, IEspSignRequest &req, IEspSignResponse &resp)

+ 4 - 4
helm/hpcc/templates/eclccserver.yaml

@@ -47,12 +47,12 @@ data:
 {{ include "hpcc.addConfigMapVolumeMount" .me | indent 12 }}
 {{ include "hpcc.addConfigMapVolumeMount" .me | indent 12 }}
 {{ include "hpcc.addDataVolumeMount" . | indent 12 }}
 {{ include "hpcc.addDataVolumeMount" . | indent 12 }}
 {{ include "hpcc.addDllVolumeMount" .root | indent 12 }}
 {{ include "hpcc.addDllVolumeMount" .root | indent 12 }}
-{{ include "hpcc.addSecretVolumeMounts" (dict "root" .root "categories" (list "system" ) ) | indent 12 }}
+{{ include "hpcc.addSecretVolumeMounts" (dict "root" .root "categories" (list "system" "codeVerify" ) ) | indent 12 }}
           volumes:
           volumes:
 {{ include "hpcc.addConfigMapVolume" .me | indent 10 }}
 {{ include "hpcc.addConfigMapVolume" .me | indent 10 }}
 {{ include "hpcc.addDataVolume" . | indent 10 }}
 {{ include "hpcc.addDataVolume" . | indent 10 }}
 {{ include "hpcc.addDllVolume" .root | indent 10 }}
 {{ include "hpcc.addDllVolume" .root | indent 10 }}
-{{ include "hpcc.addSecretVolumes" (dict "root" .root "categories" (list "system" ) ) | indent 10 }}
+{{ include "hpcc.addSecretVolumes" (dict "root" .root "categories" (list "system" "codeVerify" ) ) | indent 10 }}
           restartPolicy: Never
           restartPolicy: Never
       backoffLimit: 0
       backoffLimit: 0
 {{- end }}
 {{- end }}
@@ -106,14 +106,14 @@ spec:
 {{ include "hpcc.addConfigMapVolumeMount" . | indent 8 }}
 {{ include "hpcc.addConfigMapVolumeMount" . | indent 8 }}
 {{ include "hpcc.addDataVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addDataVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addDllVolumeMount" $ | indent 8 }}
 {{ include "hpcc.addDllVolumeMount" $ | indent 8 }}
-{{ include "hpcc.addSecretVolumeMounts" (dict "root" $ "categories" (list "system" ) ) | indent 8 }}
+{{ include "hpcc.addSecretVolumeMounts" (dict "root" $ "categories" (list "system" "codeVerify" ) ) | indent 8 }}
         - name: "hpccbundles"
         - name: "hpccbundles"
           mountPath: "/home/hpcc/.HPCCSystems"
           mountPath: "/home/hpcc/.HPCCSystems"
       volumes:
       volumes:
 {{ include "hpcc.addConfigMapVolume" . | indent 6 }}
 {{ include "hpcc.addConfigMapVolume" . | indent 6 }}
 {{ include "hpcc.addDataVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addDataVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addDllVolume" $ | indent 6 }}
 {{ include "hpcc.addDllVolume" $ | indent 6 }}
-{{ include "hpcc.addSecretVolumes" (dict "root" $ "categories" (list "system" ) ) | indent 6 }}
+{{ include "hpcc.addSecretVolumes" (dict "root" $ "categories" (list "system" "codeVerify" ) ) | indent 6 }}
       - name: hpccbundles
       - name: hpccbundles
         emptyDir: {}
         emptyDir: {}
 ---
 ---

+ 2 - 2
helm/hpcc/templates/esp.yaml

@@ -62,12 +62,12 @@ spec:
 {{ include "hpcc.addConfigMapVolumeMount" . | indent 8 }}
 {{ include "hpcc.addConfigMapVolumeMount" . | indent 8 }}
 {{ include "hpcc.addDataVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addDataVolumeMount" $commonCtx | indent 8 }}
 {{ include "hpcc.addDllVolumeMount" $ | indent 8 }}
 {{ include "hpcc.addDllVolumeMount" $ | indent 8 }}
-{{ include "hpcc.addSecretVolumeMounts" (dict "root" $ "categories" (list "system" "storage" ) ) | indent 8 }}
+{{ include "hpcc.addSecretVolumeMounts" (dict "root" $ "categories" (list "system" "storage" "codeSign" ) ) | indent 8 }}
       volumes:
       volumes:
 {{ include "hpcc.addConfigMapVolume" . | indent 6 }}
 {{ include "hpcc.addConfigMapVolume" . | indent 6 }}
 {{ include "hpcc.addDataVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addDataVolume" $commonCtx | indent 6 }}
 {{ include "hpcc.addDllVolume" $ | indent 6 }}
 {{ include "hpcc.addDllVolume" $ | indent 6 }}
-{{ include "hpcc.addSecretVolumes" (dict "root" $ "categories" (list "system" "storage" ) ) | indent 6 }}
+{{ include "hpcc.addSecretVolumes" (dict "root" $ "categories" (list "system" "storage" "codeSign") ) | indent 6 }}
 ---
 ---
 kind: ConfigMap 
 kind: ConfigMap 
 {{ include "hpcc.espConfigMap" $commonCtx }}
 {{ include "hpcc.espConfigMap" $commonCtx }}

+ 12 - 0
helm/hpcc/values.schema.json

@@ -49,6 +49,12 @@
         "ecl": {
         "ecl": {
           "$ref": "#/definitions/secrets"
           "$ref": "#/definitions/secrets"
         },
         },
+        "codeSign": {
+          "$ref": "#/definitions/secrets"
+        },
+        "codeVerify": {
+          "$ref": "#/definitions/secrets"
+        },
         "system": {
         "system": {
           "$ref": "#/definitions/secrets"
           "$ref": "#/definitions/secrets"
         }
         }
@@ -73,6 +79,12 @@
         },
         },
         "ecl-user": {
         "ecl-user": {
           "$ref": "#/definitions/vaultCategory"
           "$ref": "#/definitions/vaultCategory"
+        },
+        "codeSign": {
+          "$ref": "#/definitions/vaultCategory"
+        },
+        "codeVerify": {
+          "$ref": "#/definitions/vaultCategory"
         }
         }
       },
       },
       "additionalProperties": false
       "additionalProperties": false

+ 12 - 0
helm/hpcc/values.yaml

@@ -102,6 +102,14 @@ secrets:
   ecl: {}
   ecl: {}
     ## Category for secrets published to all components that run ecl
     ## Category for secrets published to all components that run ecl
 
 
+  codeSign: {}
+    #gpg-private-key-1: codesign-gpg-key-1
+    #gpg-private-key-2: codesign-gpg-key-2
+
+  codeVerify: {}
+    #gpg-public-key-1: codesign-gpg-public-key-1
+    #gpg-public-key-2: codesign-gpg-public-key-2
+
   system: {}
   system: {}
     ## Category for secrets published to all components for system level useage
     ## Category for secrets published to all components for system level useage
 
 
@@ -127,6 +135,10 @@ vaults:
 
 
   esp:
   esp:
 
 
+  codeSign:
+
+  codeVerify:
+
 bundles: []
 bundles: []
 ## Specifying bundles here will cause the indicated bundles to be downloaded and installed automatically
 ## Specifying bundles here will cause the indicated bundles to be downloaded and installed automatically
 ## whenever an eclccserver pod is started
 ## whenever an eclccserver pod is started

+ 1 - 0
system/codesigner/codesigner.hpp

@@ -30,6 +30,7 @@
 
 
 interface ICodeSigner
 interface ICodeSigner
 {
 {
+    virtual void initForContainer() = 0;
     virtual void sign(const char * text, const char * userId, const char * passphrase, StringBuffer & signedText) = 0;
     virtual void sign(const char * text, const char * userId, const char * passphrase, StringBuffer & signedText) = 0;
     virtual bool verifySignature(const char *text, StringBuffer &signer) = 0;
     virtual bool verifySignature(const char *text, StringBuffer &signer) = 0;
     virtual bool hasSignature(const char *text) const = 0;
     virtual bool hasSignature(const char *text) const = 0;

+ 148 - 6
system/codesigner/gpgcodesigner.cpp

@@ -18,6 +18,8 @@
 #include "jlib.hpp"
 #include "jlib.hpp"
 #include "jexcept.hpp"
 #include "jexcept.hpp"
 #include "jlog.hpp"
 #include "jlog.hpp"
+#include "jfile.hpp"
+#include "jsecrets.hpp"
 #include "gpgcodesigner.hpp"
 #include "gpgcodesigner.hpp"
 #include "atomic"
 #include "atomic"
 
 
@@ -31,6 +33,7 @@
  */
  */
 class GpgCodeSigner : implements ICodeSigner
 class GpgCodeSigner : implements ICodeSigner
 {
 {
+    virtual void initForContainer();
     virtual void sign(const char * text, const char * userId, const char * passphrase, StringBuffer & signedText) override;
     virtual void sign(const char * text, const char * userId, const char * passphrase, StringBuffer & signedText) override;
     virtual bool verifySignature(const char * text, StringBuffer & signer) override;
     virtual bool verifySignature(const char * text, StringBuffer & signer) override;
     virtual bool hasSignature(const char * text) const override;
     virtual bool hasSignature(const char * text) const override;
@@ -38,11 +41,20 @@ class GpgCodeSigner : implements ICodeSigner
     virtual StringArray &getUserIds(StringArray & userIds) override;
     virtual StringArray &getUserIds(StringArray & userIds) override;
 private:
 private:
     void initGpg(void);
     void initGpg(void);
+    bool importKey(const char *key, const char *passphrase);
+    void importKeysFromSecret(const char * cat, const char *type);
+    void importSigningKeysFromSecrets();
+    void importVerifyKeysFromSecrets();
     bool getKeyGrip(const char * user, StringBuffer & keygrip);
     bool getKeyGrip(const char * user, StringBuffer & keygrip);
     void clearPassphrase(const char * key);
     void clearPassphrase(const char * key);
     CriticalSection crit;
     CriticalSection crit;
     std::atomic<bool> isGpgV1{false};
     std::atomic<bool> isGpgV1{false};
     std::atomic<bool> isInitialized{false};
     std::atomic<bool> isInitialized{false};
+    std::atomic<bool> getSignKeysFromSecrets {false};
+    std::atomic<bool> getVerifyKeysFromSecrets {false};
+    StringBuffer gpgOptions;
+    StringBuffer gpgHomeDir;
+    bool createNewGpgHomeDir = false;
     static constexpr const char* signatureMsgHeader = "-----BEGIN PGP SIGNED MESSAGE-----";
     static constexpr const char* signatureMsgHeader = "-----BEGIN PGP SIGNED MESSAGE-----";
     static constexpr const char* signatureBegin = "-----BEGIN PGP SIGNATURE-----";
     static constexpr const char* signatureBegin = "-----BEGIN PGP SIGNATURE-----";
 };
 };
@@ -70,10 +82,131 @@ void GpgCodeSigner::initGpg(void)
     if (ret != 0)
     if (ret != 0)
         throw makeStringExceptionV(MSGAUD_operator, CODESIGNER_ERR_GPG, "Error running gpg: %s", errmsg.str());
         throw makeStringExceptionV(MSGAUD_operator, CODESIGNER_ERR_GPG, "Error running gpg: %s", errmsg.str());
     isGpgV1 = strstr(output.str(), "gpg (GnuPG) 1.");
     isGpgV1 = strstr(output.str(), "gpg (GnuPG) 1.");
+    if (createNewGpgHomeDir)
+    {
+        try
+        {
+            Owned<IFile> dir = createIFile(gpgHomeDir);
+            dir->createDirectory();
+        }
+        catch (IException *e)
+        {
+            OERRLOG("Create directory failed: %s", gpgHomeDir.str());
+            e->Release();
+        }
+        createNewGpgHomeDir = false;
+    }
     isInitialized = true;
     isInitialized = true;
 }
 }
 
 
 /**
 /**
+ * Import a gpg key
+ *
+ * @param key           The gpg key
+ * @param passphrase    The passphrase for key (optional)
+ *
+ * @return              True if successfully imported
+ *                      False if import failed
+ */
+bool GpgCodeSigner::importKey(const char *key, const char *passphrase)
+{
+    initGpg();
+    VStringBuffer cmd("gpg %s --batch --passphrase-fd 0 --import ", gpgOptions.str());
+    StringBuffer output, errmsg;
+    VStringBuffer input("%s\n", passphrase);
+    input.append(key);
+    int ret = runExternalCommand(output, errmsg, cmd.str(), input);
+    if (ret != 0)
+    {
+        OERRLOG("External command failed: %s", errmsg.str());
+        return false;
+    }
+    return true;
+}
+
+/**
+ * Import a key from secrets
+ *
+ * @param key           Secrets category
+ * @param keytype       Type of key: public or private
+ */
+void GpgCodeSigner::importKeysFromSecret(const char * cat, const char *keytype)
+{
+    unsigned importCount = 0;
+    unsigned failCount = 0;
+    for (int keyentry = 1; ; keyentry++)
+    {
+        VStringBuffer keysecretname("gpg-%s-key-%d", keytype, keyentry);
+        Owned<IPropertyTree> secretKey = getSecret(cat, keysecretname.str());
+        if (secretKey)
+        {
+            StringBuffer gpgKey;
+            if (secretKey->getProp(keytype, gpgKey))
+            {
+                StringBuffer passphrase;
+                secretKey->getProp("passphrase", passphrase);
+                if (importKey(gpgKey, passphrase))
+                    ++importCount;
+                else
+                    ++failCount;
+            }
+        }
+        else
+        {
+            break; // finished importing keys
+        }
+    }
+    if (failCount)
+        OERRLOG("Keys imported from %s/gpg-%s-key-* failed: %u (succeeded %u)", cat, keytype, failCount, importCount);
+    else
+        DBGLOG("Keys imported from %s/gpg-%s-key-*: %u", cat, keytype, importCount);
+}
+
+/**
+ * Imports signing keys from secrets
+ * - Imports takes place just once. All subsequant calls ignored.
+ * - (this may be called before every operation so keys imported only if needed)
+ */
+void GpgCodeSigner::importSigningKeysFromSecrets()
+{
+    if (!getSignKeysFromSecrets) return;
+    CriticalBlock block(crit);
+    if (!getSignKeysFromSecrets) return;
+    importKeysFromSecret("codeSign","private");
+    getSignKeysFromSecrets = false;
+}
+
+/**
+ * Imports verifying keys from secrets
+ * - Imports takes place just once. All subsequant calls ignored.
+ * - (this may be called before every operation so keys imported only if needed)
+ */
+void GpgCodeSigner::importVerifyKeysFromSecrets()
+{
+    if (!getVerifyKeysFromSecrets) return;
+    CriticalBlock block(crit);
+    if (!getVerifyKeysFromSecrets) return;
+    importKeysFromSecret("codeVerify","public");
+    getVerifyKeysFromSecrets = false;
+}
+
+/**
+ * Initialize gpg code signer for containers
+ * - uses current directory for gpg instead of home directory (which may not exist)
+ */
+void GpgCodeSigner::initForContainer()
+{
+    // Processes running in containers may be owned by a user without a home directory
+    // so use a directory under the current directory (create it later on 1st use)
+    appendCurrentDirectory(gpgHomeDir, false);
+    addPathSepChar(gpgHomeDir).append("gnugpg");
+    gpgOptions.appendf(" --homedir %s", gpgHomeDir.str());
+    createNewGpgHomeDir = true;
+    getSignKeysFromSecrets = true;
+    getVerifyKeysFromSecrets = true;
+}
+
+/**
  * Sign a block of text with a pgp signature - used of code signing
  * Sign a block of text with a pgp signature - used of code signing
  *
  *
  * @param text          Text block to sign
  * @param text          Text block to sign
@@ -95,6 +228,7 @@ void GpgCodeSigner::sign(const char * text, const char * userId, const char * pa
     if (strchr(userId, '\"')!=nullptr || strlen(userId) > 2000)
     if (strchr(userId, '\"')!=nullptr || strlen(userId) > 2000)
         throw makeStringExceptionV(MSGAUD_user, CODESIGNER_ERR_BADUSERID, "Invalid user id: %s", userId);
         throw makeStringExceptionV(MSGAUD_user, CODESIGNER_ERR_BADUSERID, "Invalid user id: %s", userId);
 
 
+    importSigningKeysFromSecrets();
     StringBuffer keygrip;
     StringBuffer keygrip;
     if (!isGpgV1)
     if (!isGpgV1)
     {
     {
@@ -103,7 +237,7 @@ void GpgCodeSigner::sign(const char * text, const char * userId, const char * pa
         clearPassphrase(keygrip);
         clearPassphrase(keygrip);
     }
     }
     StringBuffer cmd, errmsg;
     StringBuffer cmd, errmsg;
-    cmd.setf("gpg --clearsign -u \"%s\" --yes --batch --passphrase-fd 0", userId);
+    cmd.setf("gpg %s --clearsign -u \"%s\" --yes --batch --passphrase-fd 0", gpgOptions.str(), userId);
     if (!isGpgV1)
     if (!isGpgV1)
         cmd.append(" --pinentry-mode loopback");
         cmd.append(" --pinentry-mode loopback");
     VStringBuffer input("%s\n", passphrase);
     VStringBuffer input("%s\n", passphrase);
@@ -138,8 +272,11 @@ void GpgCodeSigner::sign(const char * text, const char * userId, const char * pa
 bool GpgCodeSigner::verifySignature(const char * text, StringBuffer & signer)
 bool GpgCodeSigner::verifySignature(const char * text, StringBuffer & signer)
 {
 {
     initGpg();
     initGpg();
+    importVerifyKeysFromSecrets();
+
     Owned<IPipeProcess> pipe = createPipeProcess();
     Owned<IPipeProcess> pipe = createPipeProcess();
-    if (!pipe->run("gpg", "gpg --verify -", ".", true, false, true, 0, false))
+    VStringBuffer cmd("gpg %s --verify -", gpgOptions.str());
+    if (!pipe->run("gpg", cmd.str(), ".", true, false, true, 0, false))
         throw makeStringExceptionV(MSGAUD_user, CODESIGNER_ERR_VERIFY, "Code sign verify failed (gpg --verify failed)");
         throw makeStringExceptionV(MSGAUD_user, CODESIGNER_ERR_VERIFY, "Code sign verify failed (gpg --verify failed)");
     pipe->write(strlen(text), text);
     pipe->write(strlen(text), text);
     pipe->closeInput();
     pipe->closeInput();
@@ -252,10 +389,15 @@ const char* skipn(const char * str, char c, int n)
 StringArray &GpgCodeSigner::getUserIds(StringArray & userIds)
 StringArray &GpgCodeSigner::getUserIds(StringArray & userIds)
 {
 {
     initGpg();
     initGpg();
+    importSigningKeysFromSecrets();
     StringBuffer errmsg, output("\n");
     StringBuffer errmsg, output("\n");
-    int ret = runExternalCommand(output, errmsg, "gpg --list-secret-keys --with-colon", nullptr);
+    VStringBuffer cmd("gpg %s --list-secret-keys --with-colon", gpgOptions.str());
+    int ret = runExternalCommand(output, errmsg, cmd.str(), nullptr);
     if (ret != 0)
     if (ret != 0)
+    {
+        IERRLOG("list secret keys failed: %s", errmsg.str());
         throw makeStringExceptionV(MSGAUD_user, CODESIGNER_ERR_LISTKEYS, "list secret keys failed: %s", errmsg.str());
         throw makeStringExceptionV(MSGAUD_user, CODESIGNER_ERR_LISTKEYS, "list secret keys failed: %s", errmsg.str());
+    }
 
 
     const char* START = "\nuid:";
     const char* START = "\nuid:";
     if (isGpgV1)
     if (isGpgV1)
@@ -317,9 +459,9 @@ bool GpgCodeSigner::getKeyGrip(const char * userId, StringBuffer & keygrip)
 
 
     StringBuffer cmd;
     StringBuffer cmd;
     if (isGpgV1)
     if (isGpgV1)
-        cmd.appendf("gpg --list-secret-keys \"=%s\"", userId); // = means exact match
+        cmd.appendf("gpg %s --list-secret-keys \"=%s\"", gpgOptions.str(), userId); // = means exact match
     else
     else
-        cmd.appendf("gpg --list-secret-keys --with-keygrip \"=%s\"", userId); // = means exact match
+        cmd.appendf("gpg %s --list-secret-keys --with-keygrip \"=%s\"", gpgOptions.str(), userId); // = means exact match
 
 
     StringBuffer output, errmsg;
     StringBuffer output, errmsg;
     int ret = runExternalCommand(output, errmsg, cmd.str(), nullptr);
     int ret = runExternalCommand(output, errmsg, cmd.str(), nullptr);
@@ -351,6 +493,6 @@ void GpgCodeSigner::clearPassphrase(const char * key)
 {
 {
     initGpg();
     initGpg();
     StringBuffer output, errmsg;
     StringBuffer output, errmsg;
-    VStringBuffer cmd("gpg-connect-agent \"clear_passphrase --mode=normal %s\" /bye", key);
+    VStringBuffer cmd("gpg-connect-agent %s \"clear_passphrase --mode=normal %s\" /bye", gpgOptions.str(), key);
     runExternalCommand(output, errmsg, cmd.str(), nullptr);
     runExternalCommand(output, errmsg, cmd.str(), nullptr);
 }
 }