使用SafeNet eToken自动进行扩展验证(EV)代码签名。

137
我们最近购买了一张DigiCert EV代码签名证书。我们可以使用signtool.exe对.exe文件进行签名。然而,每次我们签署一个文件时,都会提示输入SafeNet eToken密码。
我们如何能够在不需要用户干预的情况下自动化这个过程,并将密码存储/缓存到某个地方?

问题“SafeNet eToken 5110或类似的加密硬件令牌的密码提示有多安全?”有一定关联性,如果它得到回答,对于那些评估是否自动输入密码的人来说应该是有趣的。顺便说一下,如果目前拥有该令牌或类似令牌的某个人阅读了这篇文章,如果您可以尝试“黑客攻击”并回答该问题,我们将不胜感激 :) - gbr
不幸的是,对我有效并获得最多投票的答案出现在答案列表的末尾,因此不要浪费时间,直接转到Simon Mourier的答案https://dev59.com/qGMm5IYBdhLWcg3wB7d5#26126701。 - Patrick from NDepend team
1
在尝试这些解决方案之前,请注意一点。硬件令牌有一个“令牌密码重试剩余次数”计数器(可以在SafeNet身份验证客户端中检查)。在实验时,请确保它永远不会因为明显的原因而达到零。否则,您可能会永久锁定硬件令牌,并且必须订购新的令牌!我是通过吃亏才学到这个教训的... - Sundae
Simon的答案不幸地已经失效了(请参见我的评论)。而Austin的答案不仅有效,而且在我看来更好。 - Martin Prikryl
2
Austin Morton所描述的方法非常有效,但需要注意的是,它需要一个最新版本signtool.exe。如果使用过时的版本(我的版本是2016年的),则会出现错误信息:“CryptExportPublicKeyInfoEx failed”(87/0x57)。您可以通过安装Windows SDK来获取最新版本。至少在撰写本文时,SDK提供的版本支持使用Austin Morton所描述的方法。 - Crown
13个回答

114

在这个帖子中进一步扩展已有的答案,可以使用微软的标准signtool程序来提供令牌密码。

0. 在高级视图中打开SafeNet客户端

安装路径可能会有所不同,但对于我来说,SafeNet客户端安装在:C:\Program Files\SafeNet\Authentication\SAC\x64\SACTools.exe

点击右上角的齿轮图标以打开“高级视图”。 SafeNet Advanced View

1. 从SafeNet客户端将您的公共证书导出到文件 Exporting the Certificate to a File

2. 查找您的私钥容器名称
Private Key Container Name

3. 找到你的读者名称 Reader Name

4. 将所有内容格式化在一起

eToken CSP具有隐藏(或至少没有广泛宣传的)功能,可以从容器名称中解析令牌密码。

格式有以下四个选项之一:

[]=name
[reader]=name
[{{password}}]=name
[reader{{password}}]=name

说明:

  • reader 是 SafeNet 客户端 UI 中的“读卡器名称”
  • password 是您的令牌密码
  • name 是 SafeNet 客户端 UI 中的“容器名称”

如果您连接了多个读卡器,则必须指定读卡器名称 - 因为我只有一个读卡器,所以无法确认此点。

请注意,双大括号({{}})是语法的一部分,必须包含在命令行参数中。

5. 将信息传递给 signtool

  • /f certfile.cer
  • /csp "eToken Base Cryptographic Provider"
  • /k "<value from step 4>"
  • 您需要的任何其他 signtool 标志

以下是示例 signtool 命令

signtool sign /f mycert.cer /csp "eToken Base Cryptographic Provider" /k "[{{TokenPasswordHere}}]=KeyContainerNameHere" myfile.exe

该答案中的一些图片:https://dev59.com/qGMm5IYBdhLWcg3wB7d5#47894907


4
非常好用,可惜我在自己费力挖掘和实现“signtool”两天后才发现它 :D 谢谢 - Lukáš Koten
5
警告:如果您输入错误的密码,即使只输入两次,此解决方案可能会锁定您的硬件令牌!在执行一次带有无效密码的命令后,密码重试剩余计数器从15减少到3,原因不明。 但是,“etokensign.exe”解决方案似乎工作正常,即在一次无效密码尝试后,剩余密码计数器按预期从15减少到14。 - Sundae
7
据我所知,这种语法没有公开的文档说明。我是通过在IDA Pro中对驱动程序二进制文件进行反向工程来发现这个功能的。 - Austin Morton
5
这比其他伪造密码输入到GUI提示的答案更好。此外,它甚至在非交互式的Windows会话中也能工作。而其他答案则似乎无法与最新的工具一起使用。 - Martin Prikryl
5
当尝试使用此方法时,是否还有其他人收到“SignTool错误:没有可用的私钥。”?/c/PROGRA~2/WI3CF2~1/10/bin/10.0.19041.0/x64/signtool sign -f "certfile.cer" -csp "eToken Base Cryptographic Provider" -k "[{{Password*Goes$Here}}]=te-UUID" file.exe - SRG3006
显示剩余18条评论

93

据我所知,无法绕过登录对话框,但您可以配置SafeNet认证客户端,使其仅在每个登录会话期间询问一次。

我引用了SAC文档(一旦安装在 \ ProgramFiles \ SafeNet \ Authentication \ SAC \ SACHelp.chm 中找到,章节为“ 客户端设置”,“启用客户端登录”):

  

启用单一登录后,用户可以在每个计算机会话期间仅使用一个令牌密码访问多个应用程序。 这消除了用户需要单独登录到每个应用程序的需要。

要启用此默认情况下禁用的功能,请转到SAC高级设置并选中“启用单一登录”框:

“输入图像描述”

重新启动计算机,现在它应该只提示一次令牌密码。 在我们的情况下,每次构建我们有200多个二进制文件要签名,因此这是绝对必要的。

否则,这是一个小的C#控制台示例代码(与m1st0等效),允许您自动响应登录对话框(可能需要以管理员身份运行)(您需要从控制台项目引用 UIAutomationClient.dll UIAutomationTypes.dll ):

using System;
using System.Windows.Automation;

namespace AutoSafeNetLogon {
   class Program {
      static void Main(string[] args) {
         SatisfyEverySafeNetTokenPasswordRequest("YOUR_TOKEN_PASSWORD");
      }


      static void SatisfyEverySafeNetTokenPasswordRequest(string password) {
         int count = 0;
         Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, (sender, e) =>
         {
            var element = sender as AutomationElement;
            if (element.Current.Name == "Token Logon") {
               WindowPattern pattern = (WindowPattern)element.GetCurrentPattern(WindowPattern.Pattern);
               pattern.WaitForInputIdle(10000);
               var edit = element.FindFirst(TreeScope.Descendants, new AndCondition(
                   new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit),
                   new PropertyCondition(AutomationElement.NameProperty, "Token Password:")));

               var ok = element.FindFirst(TreeScope.Descendants, new AndCondition(
                   new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button),
                   new PropertyCondition(AutomationElement.NameProperty, "OK")));

               if (edit != null && ok != null) {
                  count++;
                  ValuePattern vp = (ValuePattern)edit.GetCurrentPattern(ValuePattern.Pattern);
                  vp.SetValue(password);
                  Console.WriteLine("SafeNet window (count: " + count + " window(s)) detected. Setting password...");

                  InvokePattern ip = (InvokePattern)ok.GetCurrentPattern(InvokePattern.Pattern);
                  ip.Invoke();
               } else {
                  Console.WriteLine("SafeNet window detected but not with edit and button...");
               }
            }
         });

         do {
            // press Q to quit...
            ConsoleKeyInfo k = Console.ReadKey(true);
            if (k.Key == ConsoleKey.Q)
               break;
         }
         while (true);
         Automation.RemoveAllEventHandlers();
      }
   }
}

13
这可能不是来自DigiCert的官方回答,但他们的回答很糟糕,而这个回答非常好!感谢您的帮助! - lordjeb
3
让我惊讶的是看到人们编写脚本来自动化用户输入等,这样就打败了设置密码的目的,他们只需要知道这个选项在哪里就行了。我怀疑这个选项永远不会消失,因为发布者们知道开发人员无法在每次签署二进制文件时都输入密码。 - dyasta
4
我可以确认,在TeamCity中运行这个程序是可行的(只要TeamCity Windows服务已勾选“允许服务与桌面交互”的选项)。我们还需要在另一个线程中运行密码输入过程,并在构建机器上禁用“交互式服务检测”服务。我们创建了一个C#包装器来执行签名并处理上述的密码输入,所有这些都在一个自包含的应用程序中。我不能相信我们必须跨越多少障碍才能使其正常工作,但对于其他有同样问题的人,请专注于使用上述描述的C#方法... - Alan Spark
3
FYI...Symantec EV证书也使用SafeNet。我们不得不围绕这个过程构建一个笨拙的解决方案,但在阅读了您的答案并实施控制台应用程序后,这极大地帮助了我们的构建过程。谢谢。这是一个对糟糕的代码签名流程的伟大解决方案。 - Zoltan
1
我已经成功使用这个有用的解决方案一段时间了。但是现在,在新的Windows 10 Pro 2004机器上安装时,使用SafeNet客户端9.0.34 x64 for Windows 8及以上版本,它不再起作用。出现了一个新的密码提示框。它似乎是Windows内置的,而不是以前的自定义SafeNet提示框。新提示框的密码框无法自动化(在AutomationElement树中没有显示)。我不知道是否可以通过某种方式解决它。但是重新访问这个问题并找到@Austin的答案后,我相信这是更好的解决方案。 - Martin Prikryl
显示剩余10条评论

25

扩展此答案,可以使用CryptAcquireContextCryptSetProvParam自动输入令牌PIN码,以及使用CryptUIWizDigitalSign自动执行签名操作。我创建了一个控制台应用程序(下面是代码),它将证书文件(通过在 SafeNet 认证客户端中右键单击证书并选择“导出…”导出),私钥容器名称(在 SafeNet 认证客户端中找到),令牌 PIN 码、时间戳 URL 和要签名的文件路径作为输入。当 USB 令牌连接时,该控制台应用程序可被 TeamCity 构建代理调用。

示例用法:
etokensign.exe c:\CodeSigning.cert CONTAINER PIN http://timestamp.digicert.com C:\program.exe

代码:

#include <windows.h>
#include <cryptuiapi.h>
#include <iostream>
#include <string>

const std::wstring ETOKEN_BASE_CRYPT_PROV_NAME = L"eToken Base Cryptographic Provider";

std::string utf16_to_utf8(const std::wstring& str)
{
    if (str.empty())
    {
        return "";
    }

    auto utf8len = ::WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), NULL, 0, NULL, NULL);
    if (utf8len == 0)
    {
        return "";
    }

    std::string utf8Str;
    utf8Str.resize(utf8len);
    ::WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), &utf8Str[0], utf8Str.size(), NULL, NULL);

    return utf8Str;
}

struct CryptProvHandle
{
    HCRYPTPROV Handle = NULL;
    CryptProvHandle(HCRYPTPROV handle = NULL) : Handle(handle) {}
    ~CryptProvHandle() { if (Handle) ::CryptReleaseContext(Handle, 0); }
};

HCRYPTPROV token_logon(const std::wstring& containerName, const std::string& tokenPin)
{
    CryptProvHandle cryptProv;
    if (!::CryptAcquireContext(&cryptProv.Handle, containerName.c_str(), ETOKEN_BASE_CRYPT_PROV_NAME.c_str(), PROV_RSA_FULL, CRYPT_SILENT))
    {
        std::wcerr << L"CryptAcquireContext failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
        return NULL;
    }

    if (!::CryptSetProvParam(cryptProv.Handle, PP_SIGNATURE_PIN, reinterpret_cast<const BYTE*>(tokenPin.c_str()), 0))
    {
        std::wcerr << L"CryptSetProvParam failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
        return NULL;
    }

    auto result = cryptProv.Handle;
    cryptProv.Handle = NULL;
    return result;
}

int wmain(int argc, wchar_t** argv)
{
    if (argc < 6)
    {
        std::wcerr << L"usage: etokensign.exe <certificate file path> <private key container name> <token PIN> <timestamp URL> <path to file to sign>\n";
        return 1;
    }

    const std::wstring certFile = argv[1];
    const std::wstring containerName = argv[2];
    const std::wstring tokenPin = argv[3];
    const std::wstring timestampUrl = argv[4];
    const std::wstring fileToSign = argv[5];

    CryptProvHandle cryptProv = token_logon(containerName, utf16_to_utf8(tokenPin));
    if (!cryptProv.Handle)
    {
        return 1;
    }

    CRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO extInfo = {};
    extInfo.dwSize = sizeof(extInfo);
    extInfo.pszHashAlg = szOID_NIST_sha256; // Use SHA256 instead of default SHA1

    CRYPT_KEY_PROV_INFO keyProvInfo = {};
    keyProvInfo.pwszContainerName = const_cast<wchar_t*>(containerName.c_str());
    keyProvInfo.pwszProvName = const_cast<wchar_t*>(ETOKEN_BASE_CRYPT_PROV_NAME.c_str());
    keyProvInfo.dwProvType = PROV_RSA_FULL;

    CRYPTUI_WIZ_DIGITAL_SIGN_CERT_PVK_INFO pvkInfo = {};
    pvkInfo.dwSize = sizeof(pvkInfo);
    pvkInfo.pwszSigningCertFileName = const_cast<wchar_t*>(certFile.c_str());
    pvkInfo.dwPvkChoice = CRYPTUI_WIZ_DIGITAL_SIGN_PVK_PROV;
    pvkInfo.pPvkProvInfo = &keyProvInfo;

    CRYPTUI_WIZ_DIGITAL_SIGN_INFO signInfo = {};
    signInfo.dwSize = sizeof(signInfo);
    signInfo.dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE;
    signInfo.pwszFileName = fileToSign.c_str();
    signInfo.dwSigningCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_PVK;
    signInfo.pSigningCertPvkInfo = &pvkInfo;
    signInfo.pwszTimestampURL = timestampUrl.c_str();
    signInfo.pSignExtInfo = &extInfo;

    if (!::CryptUIWizDigitalSign(CRYPTUI_WIZ_NO_UI, NULL, NULL, &signInfo, NULL))
    {
        std::wcerr << L"CryptUIWizDigitalSign failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
        return 1;
    }

    std::wcout << L"Successfully signed " << fileToSign << L"\n";
    return 0;
}

将证书导出到文件:
Exporting the Certificate to a File

私钥容器名称:
Private Key Container Name


2
这个应该是被接受的答案,它非常有效! - shawn
2
这很完美。特别是在我意识到我只需要使用这个工具对某些虚拟文件进行签名时。如果启用了“单点登录”(SafeNet驱动程序),所有随后的步骤都可以使用标准的signtool完成。这对于签署使用不同工具的Office Addin(VSTO)非常有用,这也意味着我的构建脚本/流程仅需要进行最少的更改。 - Stefan Egli
这个答案是对avzhatkin提供的答案的很好的补充。目前,该代码已经接近替换signtools.exe。这个程序需要支持交叉签名。幸运的是,有另一个SO帖子介绍了如何执行交叉签名 - sdc
这个答案最终帮了我很多。在使用VS2017构建时,我忽略了一个外部引用,但是当按照这里的建议添加一些pragma注释后,我成功让Bamboo(Atlassian的CI / CD工具)签名了。 - HTBR
1
成功构建需要链接到"Cryptui.lib"库。对于Visual C++,只需在#include之后插入#pragma comment(lib, "Cryptui.lib")即可。 - zett42
@StefanEgli 实际上,您甚至不需要签署一个虚拟文件。只需调用 token_logon() 函数即可。之后,标准的 signtool.exe 就可以成功执行,而无需再次要求输入令牌密码。 - zett42

14

signtool.exe sign /fd sha256 /f "signing.cer" /csp "eToken Base Cryptographic Provider" /kc "[{{在此输入令牌密码}}]=容器名称" "ConsoleApp1.exe"

请使用Microsoft Windows SDK 10来运行signtool。


1
太棒了!这个一行代码肯定让所有其他答案都相形见绌(尽管一开始我困惑如何找到我的“容器名称”,直到在draketb的答案中找到了说明)。 - Dan Z

12

我制作了一款Beta工具,可以帮助自动化构建过程。

这是一个客户端-服务器Windows应用程序。您可以在插入EV令牌的计算机上启动服务器。在服务器端应用程序启动时输入令牌密码后,您可以远程签署文件。 客户端应用程序完全替代了signtool.exe,因此您可以使用现有的构建脚本。

源代码位于此处:https://github.com/SirAlex/RemoteSignTool

编辑:我们在过去半年里成功地将该工具用于代码签名,而且在我们的构建服务器上24x7地运行,一切正常。


2
这种方法有多安全?这难道不意味着任何能够通过HTTP连接到您的签名服务器的人都可以使用您的EV证书签署任何二进制文件吗? - Gene Pavlovsky

8

实际上,在Windows上,您可以完全以编程方式指定令牌密码。这可以通过使用形式为“\\。 \ AKS ifdh 0”的标记名称或标记容器名称(在身份验证客户端应用程序中的证书属性中可见的某个GUID)创建带有CRYPT_SILENT标志的上下文(CryptAcquireContext)来完成。然后,您需要使用CryptSetProvParam参数PP_SIGNATURE_PIN来指定您的令牌密码。之后,该进程可以使用该令牌上的证书对文件进行签名。
注意:一旦您创建上下文,它似乎只适用于当前进程,无需将其传递给其他Crypto API函数或任何其他内容。但是,如果您发现需要更多的努力,请随时发表评论。
编辑:添加了代码示例

HCRYPTPROV OpenToken(const std::wstring& TokenName, const std::string& TokenPin)
{
  const wchar_t DefProviderName[] = L"eToken Base Cryptographic Provider";

  HCRYPTPROV hProv = NULL;
  // Token naming can be found in "eToken Software Developer's Guide"
  // Basically you can either use "\\.\AKS ifdh 0" form
  // Or use token's default container name, which looks like "ab-c0473610-8e6f-4a6a-ae2c-af944d09e01c"
  if(!CryptAcquireContextW(&hProv, TokenName.c_str(), DefProviderName, PROV_RSA_FULL, CRYPT_SILENT))
    {
      DWORD Error = GetLastError();
      //TracePrint("CryptAcquireContext for token %ws failed, error 0x%08X\n", TokenName.c_str(), Error);
      return NULL;
      }
  if(!CryptSetProvParam(hProv, PP_SIGNATURE_PIN, (BYTE*)TokenPin.c_str(), 0))
    {
      DWORD Error = GetLastError();
      //TracePrint("Token %ws unlock failed, error 0x%08X\n", TokenName.c_str(), Error);
      CryptReleaseContext(hProv, 0);
      return NULL;
      }
  else
    {
      //TracePrint("Unlocked token %ws\n", TokenName.c_str());
      return hProv;
      }
}

1
有意思。看起来很有前途,我个人认为你应该详细阐述一下(增强解释,提供代码等)。 - Simon Mourier
请发布一个完整的示例。这听起来非常有用。 - dten
感谢提供额外的细节。这是您提到的指南吗?http://read.pudn.com/downloads128/ebook/549477/eToken_SDK_3_50[1].pdf - dten
我相信这不是我所拥有的确切版本,但它似乎包含了关于创建上下文和提供 PIN 的类似信息,尽管用于不同的使用场景。 - avzhatkin
我猜你会称呼这个函数为OpenToken(L"\\.\AKS ifdh 0",<token password>)... 好吧,它对我起作用了! - Michael Haephrati
1
好的解决方案,我现在已经使用了一年。 我有一个 PowerShell 函数,首先使用这段代码调用一个非托管的 exe 解锁令牌。 然后从 PowerShell 文件中,我调用 SignTool.exe。 我尝试过几种方法来丢弃非托管的 exe,例如使用托管的 exe,将 C# 源嵌入到 PowerShell 文件中以及编写一个 cmdlet,该 cmdlet 先解锁令牌,然后从 C# 调用 SignTool。 这些方法都没有奏效。 令牌容器始终在另一个进程上下文中解锁。 - sdc

5

我使用AutoHotKey来自动化密码输入,使用以下脚本。我们一直在尝试使用此脚本运行Web前端,让开发人员将二进制文件发送到Windows框以进行签名并返回。

  Loop
  {   
    Sleep 2000

    if (WinExist("Token Logon"))
    {   
      WinActivate ; use the window found above
      SendInput [your_password]
      SendInput {Enter}
    }   
    if (WinExist("DigiCert Certificate Utility for Windows©"))
    {   
      WinActivate ; use the window found above
      SendInput [your_password]
      SendInput {Enter}
    }   
  } 

我必须指出,我分享的内容并不完全不安全,但我们也遇到了这个问题,需要购买每个开发人员的签名密钥或分配签名管理员的工作来批准发布软件的签名。我相信这些是更好、更安全的流程——一旦事情经过质量保证并获得发布批准,它们就可以被正式签名。然而,较小的公司需求可能要求以其他自动化方式完成此操作。
我最初在 Linux 上使用 osslsigncode(在 EV 证书之前)自动签名 Windows 可执行文件(因为我们有一个 Linux 服务器为开发人员提供便利和协作)。我已经联系了 osslsigncode 的开发人员,看看他是否可以利用 DigiCert SafeNet 令牌以不同的方式自动化它,因为我可以在 Linux 上看到它们。他的回复给了我希望,但我不确定有没有进展,我不能再花更多时间帮助。

请查看其他答案。有一个选项可以每个会话仅解锁一次,这对大多数用户来说已经足够了。 - dyasta

4

安装 https://chocolatey.org/docs/installation(可以使用管理员命令提示符中的一个命令完成)

(重新启动命令提示符)

抑制choco在每次安装时不停提示:

choco feature enable -n=allowGlobalConfirmation

安装Python,使用以下命令:
choco install python

重新启动命令提示符。 安装额外的Python模块:

pip install pypiwin32

请将以下文本保存到disableAutoprompt.py文件中:
import pywintypes
import win32con
import win32gui
import time



DIALOG_CAPTION = 'Token Logon'
DIALOG_CLASS = '#32770'
PASSWORD_EDIT_ID = 0x3ea
TOKEN_PASSWORD_FILE = 'password.txt'
SLEEP_TIME = 10


def get_token_password():
    password = getattr(get_token_password, '_password', None)
    if password is None:
        with open(TOKEN_PASSWORD_FILE, 'r') as f:
            password = get_token_password._password = f.read()

    return password

def enumHandler(hwnd, lParam):
    if win32gui.IsWindowVisible(hwnd):
        if win32gui.GetWindowText(hwnd) == DIALOG_CAPTION and win32gui.GetClassName(hwnd) == DIALOG_CLASS:
            print('Token logon dialog has been detected, trying to enter password...')
            try:
                ed_hwnd = win32gui.GetDlgItem(hwnd, PASSWORD_EDIT_ID)
                win32gui.SendMessage(ed_hwnd, win32con.WM_SETTEXT, None, get_token_password())
                win32gui.PostMessage(ed_hwnd, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0)
                print('Success.')
            except Exception as e:
                print('Fail: {}'.format(str(e)))
                return False

    return True


def main():
    while True:
        try:
            win32gui.EnumWindows(enumHandler, None)
            time.sleep(SLEEP_TIME)
        except pywintypes.error as e:
            if e.winerror != 0:
                raise e


if __name__ == '__main__':
    print('Token unlocker has been started...')
    print('DO NOT CLOSE THE WINDOW!')
    main()

将您的密码保存到passwd.txt文件中,然后运行以下命令:

python disableAutoprompt.py

SafeNet身份验证客户端 - 配置 > 客户端设置 > 高级 > 启用单点登录 选项可使密码提示数量最小化,但并不能完全禁用它们(在版本10.4.26.0上测试过)。

C# 应用程序(例如:https://github.com/ganl/safenetpass)在锁屏状态下无法使用,但可以使用此 Python 脚本。


这个脚本非常棒,我能够轻松地将其适应于使用 Yubikey dongle。然而,Windows 10 却破坏了它。Windows 10 转向 XAML,因此 win32gui.xxxx() 函数无法工作。/叹气。谢谢你,微软。这就是为什么我们不能拥有好东西的原因。 - John Rocha

2

我从Digicert得到了一个回答:

不幸的是,EV代码签名证书的一部分安全措施是每次都必须输入密码。无法自动化。


我们得到了相同的回复,尽管他们正在寻找解决方案,但他们没有时间表来确定何时可能会有一个可用的解决方案。不过,他们知道这个 Stack Overflow 的帖子,所以希望他们能意识到这是多么严重的问题。 - Alan Spark
我们找到了一种解决方法:https://github.com/mareklinka/SafeNetTokenSigner/issues/8 - gunslingor

2

在我的情况下,Digicert为CI发放了标准(OV)证书,如果您已经拥有EV证书,则免费提供。

我知道这不是解决方案,但如果您无法将令牌放入服务器(云服务器),那么这就是走的方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接