我们最近购买了DigiCert EV代码签名证书。我们可以使用signtool.exe来签署.exe文件。但是,每次我们签署文件时,都会提示输入SafeNet eToken密码。
如何在没有用户干预的情况下,通过在某处存储/缓存密码来自动执行此过程?
答案 0 :(得分:52)
无法绕过登录对话框AFAIK,但您可以做的是配置SafeNet身份验证客户端,以便每次登录会话只需要一次。
我引用了SAC文档(在\ProgramFiles\SafeNet\Authentication\SAC\SACHelp.chm
中安装,“Client Settings
”,“Enabling Client Logon
”一章):
启用单点登录后,用户可以访问多个应用程序 每台计算机只有一个令牌密码请求 会话。这减轻了用户登录每个用户的需要 单独申请。
要启用默认禁用的此功能,请转到SAC高级设置,然后选中“启用单一登录”框:
重新启动计算机,它现在应该只提示一次令牌密码。在我们的例子中,我们每个构建都有200多个二进制文件,所以这是一个必须。
否则,这是一个小的C#控制台示例代码(相当于m1st0),允许您自动响应登录对话框(可能需要以管理员身份运行):
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();
}
答案 1 :(得分:10)
展开this answer,可以使用CryptAcquireContext和CryptSetProvParam以编程方式输入令牌PIN,然后CryptUIWizDigitalSign以编程方式执行签名。我创建了一个控制台应用程序(下面的代码),它将证书文件作为输入(通过右键单击SafeNet Authentication Client中的证书并选择“导出...”),私钥容器名称(在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;
}
答案 2 :(得分:9)
我制作了测试工具,有助于自动化构建过程。
它的客户端 - 服务器窗口应用程序。您可以在插入EV令牌的计算机上启动服务器。在服务器端应用程序启动时输入令牌密码。在此之后,您可以远程签名文件。 客户端应用程序完全替换signtool.exe,因此您可以使用现有的构建脚本。
源代码位于此处:https://github.com/SirAlex/RemoteSignTool
编辑:我们在Build服务器上成功使用此工具进行上半年24x7的代码签名。一切正常。
答案 3 :(得分:6)
实际上在Windows上,您可以完全以编程方式指定令牌密码。这可以通过使用表格&#34; \\。\ AKS ifdh 0&#34;中的令牌名称创建带有标志CRYPT_SILENT的上下文(CryptAcquireContext)来完成。或令牌容器名称,它是Authentication Client应用程序中的证书属性中可见的一些guid。然后,您需要使用带有参数PP_SIGNATURE_PIN的CryptSetProvParam来指定令牌密码。之后,该进程可以使用该令牌上的证书对文件进行签名。
注意:一旦创建了上下文,似乎完全适用于当前进程,无需将其传递给其他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;
}
}
答案 4 :(得分: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上看到它们。他的回答提供了希望,但我不确定是否有任何进展,我无法再投入更多时间来帮助
答案 5 :(得分:5)
signtool.exe标志/ fd sha256 / f“ signing.cer” / csp“ eToken基本密码提供程序” / kc“ [{{此处是令牌密码”] =这里的容器名称“” ConsoleApp1.exe“
使用Microsoft Windows SDK 10签名工具
答案 6 :(得分:3)
得到了Digicert的回答:
不幸的是,EV代码签名证书的部分安全性是您必须每次都输入密码。 没有办法让它自动化。
答案 7 :(得分:3)
扩展此线程中已经存在的答案,可以使用Microsoft的标准signtool程序提供令牌密码。
4。一起格式化
eToken CSP具有隐藏(或至少未广泛宣传)的功能,可以从容器名称中解析令牌密码。
格式为以下其中一种
[]=name
[reader]=name
[{{password}}]=name
[reader{{password}}]=name
位置:
reader
是SafeNet客户端用户界面中的“读者名称” password
是您的令牌密码name
是SafeNet客户端用户界面中的“容器名称” 如果您连接了多个阅读器,则大概必须指定阅读器名称-因为我只有一个阅读器,所以我无法确认。
5。将信息传递给signtool
/f certfile.cer
/csp "eToken Base Cryptographic Provider"
/k "<value from step 4>"
示例signtool命令如下
signtool sign /f mycert.cer /csp "eToken Base Cryptographic Provider" /k "[{{TokenPasswordHere}}]=KeyContainerNameHere" myfile.exe
从此答案拍摄的一些图像:https://stackoverflow.com/a/47894907/5420193
答案 8 :(得分:2)
我的情况如果您已经拥有EV证书,Digicert会为CI发放标准(OV)证书。
我知道这不是解决方案,但是如果你不能将令牌放在服务器(云服务器)中,这就是你要走的路。
答案 9 :(得分:2)
该工具的Python变体:
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()
此外,我发现,oVirt控制台具有向Windows发送锁定的默认行为。 您需要在服务器选项和设置自动登录中禁用它。
答案 10 :(得分:0)
我的方法是:
打开令牌
PCCERT_CONTEXT cert = OpenToken(SAFENET_TOKEN,EV_PASS);
使用令牌签名文件,必要时使用根/交叉证书,并将EV证书加载到内存中。
HRESULT hr = SignAppxPackage(cert,FILETOSIGN);
使用SignerSignEx2():
使用SignerSignEx2()对文件进行签名,需要使用LoadLibrary()和GetProcAddress()将文件加载到内存中。
// Type definition for invoking SignerSignEx2 via GetProcAddress
typedef HRESULT(WINAPI *SignerSignEx2Function)(
DWORD,
PSIGNER_SUBJECT_INFO,
PSIGNER_CERT,
PSIGNER_SIGNATURE_INFO,
PSIGNER_PROVIDER_INFO,
DWORD,
PCSTR,
PCWSTR,
PCRYPT_ATTRIBUTES,
PVOID,
PSIGNER_CONTEXT *,
PVOID,
PVOID);
// Load the SignerSignEx2 function from MSSign32.dll
HMODULE msSignModule = LoadLibraryEx(
L"MSSign32.dll",
NULL,
LOAD_LIBRARY_SEARCH_SYSTEM32);
if (msSignModule)
{
SignerSignEx2Function SignerSignEx2 = reinterpret_cast<SignerSignEx2Function>(
GetProcAddress(msSignModule, "SignerSignEx2"));
if (SignerSignEx2)
{
hr = SignerSignEx2(
signerParams.dwFlags,
signerParams.pSubjectInfo,
signerParams.pSigningCert,
signerParams.pSignatureInfo,
signerParams.pProviderInfo,
signerParams.dwTimestampFlags,
signerParams.pszAlgorithmOid,
signerParams.pwszTimestampURL,
signerParams.pCryptAttrs,
signerParams.pSipData,
signerParams.pSignerContext,
signerParams.pCryptoPolicy,
signerParams.pReserved);
}
else
{
DWORD lastError = GetLastError();
hr = HRESULT_FROM_WIN32(lastError);
}
FreeLibrary(msSignModule);
}
else
{
DWORD lastError = GetLastError();
hr = HRESULT_FROM_WIN32(lastError);
}
// Free any state used during app package signing
if (sipClientData.pAppxSipState)
{
sipClientData.pAppxSipState->Release();
}
时间戳
此外,您必须对签名文件进行时间戳记,并使用要连接的时间戳记授权机构进行签名。
这可以通过使用URL安全地检查时间戳服务器来获取当前日期和时间来完成。每个签名机构都有自己的时间戳服务器。时间戳是代码签名过程中的一个额外步骤,但是对于EV代码签名,这是一项要求,它为签名的PE添加了额外的安全性。 因此,请在代码中检查用户是否已连接到Internet。
DWORD dwReturnedFlag;
if (InternetGetConnectedState(&dwReturnedFlag,0) == NULL) // use https://docs.microsoft.com/en-us/windows/desktop/api/netlistmgr/nf-netlistmgr-inetworklistmanager-getconnectivity
{
wprintf(L"Certificate can't be dated with no Internet connection\n");
return 1;
}
从文件加载证书
std::tuple<DWORD, DWORD, std::string> GetCertificateFromFile
(const wchar_t* FileName
, std::shared_ptr<const CERT_CONTEXT>* ResultCert)
{
std::vector<unsigned char> vecAsn1CertBuffer;
auto tuple_result = ReadFileToVector(FileName, &vecAsn1CertBuffer);
if (std::get<0>(tuple_result) != 0)
{
return tuple_result;
}
return GetCertificateFromMemory(vecAsn1CertBuffer, ResultCert);
}
将证书加载到内存中
std::tuple<DWORD, DWORD, std::string> GetCertificateFromMemory
(const std::vector<unsigned char>& CertData
, std::shared_ptr<const CERT_CONTEXT>* ResultCert)
{
const CERT_CONTEXT* crtResultCert = ::CertCreateCertificateContext
(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
, &CertData[0]
, static_cast<DWORD>(CertData.size()));
if (crtResultCert == NULL)
{
return std::make_tuple(E_FAIL
, ::GetLastError()
, "CertCreateCertificateContext");
}
*ResultCert = std::shared_ptr<const CERT_CONTEXT>(crtResultCert
, ::CertFreeCertificateContext);
return std::make_tuple(0, 0, "");
}
在访问硬件令牌后加载证书之后,我们将其加载:
std::vector<unsigned char> dataCertEV(signingCertContext->pbCertEncoded,
signingCertContext->pbCertEncoded + signingCertContext->cbCertEncoded);
最后,签名通过以下功能完成:
HRESULT SignAppxPackage(
_In_ PCCERT_CONTEXT signingCertContext,
_In_ LPCWSTR packageFilePath)
{
HRESULT hr = S_OK;
if (PathFileExists(CertAuthority_ROOT))
{
wprintf(L"Cross Certificate '%s' was found\n", CertAuthority_ROOT);
}
else
{
wprintf(L"Error: Cross Certificate '%s' was not found\n", CertAuthority_ROOT);
return 3;
}
DWORD dwReturnedFlag;
if (InternetGetConnectedState(&dwReturnedFlag,0) == NULL)
{
wprintf(L"Certificate can't be dated with no Internet connection\n");
return 1;
}
if (PathFileExists(CertAuthority_RSA))
{
wprintf(L"Cross Certificate '%s' was found\n", CertAuthority_RSA);
}
else
{
wprintf(L"Error: Cross Certificate '%s' was not found\n", CertAuthority_RSA);
return 2;
}
if (PathFileExists(CROSSCERTPATH))
{
wprintf(L"Microsoft Cross Certificate '%s' was found\n", CROSSCERTPATH);
}
else
{
wprintf(L"Error: Microsoft Cross Certificate '%s' was not found\n", CROSSCERTPATH);
return 3;
}
// Initialize the parameters for SignerSignEx2
DWORD signerIndex = 0;
SIGNER_FILE_INFO fileInfo = {};
fileInfo.cbSize = sizeof(SIGNER_FILE_INFO);
fileInfo.pwszFileName = packageFilePath;
SIGNER_SUBJECT_INFO subjectInfo = {};
subjectInfo.cbSize = sizeof(SIGNER_SUBJECT_INFO);
subjectInfo.pdwIndex = &signerIndex;
subjectInfo.dwSubjectChoice = SIGNER_SUBJECT_FILE;
subjectInfo.pSignerFileInfo = &fileInfo;
SIGNER_CERT_STORE_INFO certStoreInfo = {};
certStoreInfo.cbSize = sizeof(SIGNER_CERT_STORE_INFO);
certStoreInfo.dwCertPolicy = SIGNER_CERT_POLICY_STORE;// SIGNER_CERT_POLICY_CHAIN_NO_ROOT;
certStoreInfo.pSigningCert = signingCertContext;
// Issuer: 'CertAuthority RSA Certification Authority'
// Subject 'CertAuthority RSA Extended Validation Code Signing CA'
auto fileCertAuthorityRsaEVCA = CertAuthority_RSA;
std::shared_ptr<const CERT_CONTEXT> certCertAuthorityRsaEVCA;
auto tuple_result = GetCertificateFromFile(fileCertAuthorityRsaEVCA, &certCertAuthorityRsaEVCA);
if (std::get<0>(tuple_result) != 0)
{
std::cout << "Error: " << std::get<0>(tuple_result) << " " << std::get<1>(tuple_result) << " " << std::get<2>(tuple_result) << "\n";
return std::get<0>(tuple_result);
}
std::shared_ptr<const CERT_CONTEXT> certCertEV;
std::vector<unsigned char> dataCertEV(signingCertContext->pbCertEncoded,
signingCertContext->pbCertEncoded + signingCertContext->cbCertEncoded);
tuple_result = GetCertificateFromMemory(dataCertEV, &certCertEV);
if (std::get<0>(tuple_result) != 0)
{
std::cout << "Error: " << std::get<0>(tuple_result) << " " << std::get<1>(tuple_result) << " " << std::get<2>(tuple_result) << "\n";
return std::get<0>(tuple_result);
}
// Issuer: 'Microsoft Code Verification Root'
// Subject: 'CertAuthority RSA Certification Authority'
auto fileCertCross = CertAuthority_ROOT;
std::shared_ptr<const CERT_CONTEXT> certCertCross;
tuple_result = GetCertificateFromFile(fileCertCross, &certCertCross);
if (std::get<0>(tuple_result) != 0)
{
std::cout << "Error: " << std::get<0>(tuple_result) << " " << std::get<1>(tuple_result) << " " << std::get<2>(tuple_result) << "\n";
return std::get<0>(tuple_result);
}
//certificate 1 Issuer : '<Certificate Provider> RSA Certification Authority'
// Subject : '<Certificate Provider> Extended Validation Code Signing CA'
//
//certificate 2 Issuer : '<Certificate Provider> Extended Validation Code Signing CA'
// Subject : '<Your company / entity name>'
//
//certificate 3 Issuer : 'Microsoft Code Verification Root'
// Subject : '<Certificate Provider> Certification Authority'
std::vector<std::shared_ptr<const CERT_CONTEXT> > certs;
certs.push_back(certCertAuthorityRsaEVCA);
certs.push_back(certCertEV);
certs.push_back(certCertCross);
std::shared_ptr<void> resultStore;
tuple_result = FormMemoryCertStore(certs, CERT_STORE_ADD_NEW, &resultStore);
if (std::get<0>(tuple_result) != 0)
{
std::cout << "Error: " << std::get<0>(tuple_result) << " " << std::get<1>(tuple_result) << " " << std::get<2>(tuple_result) << "\n";
return std::get<0>(tuple_result);
}
certStoreInfo.hCertStore = resultStore.get();
//--------------------------------------------------------------------
SIGNER_CERT cert = {};
cert.cbSize = sizeof(SIGNER_CERT);
cert.dwCertChoice = SIGNER_CERT_STORE;
cert.pCertStoreInfo = &certStoreInfo;
// The algidHash of the signature to be created must match the
// hash algorithm used to create the app package
SIGNER_SIGNATURE_INFO signatureInfo = {};
signatureInfo.cbSize = sizeof(SIGNER_SIGNATURE_INFO);
signatureInfo.algidHash = CALG_SHA_256;
signatureInfo.dwAttrChoice = SIGNER_NO_ATTR;
SIGNER_SIGN_EX2_PARAMS signerParams = {};
signerParams.pSubjectInfo = &subjectInfo;
signerParams.pSigningCert = &cert;
signerParams.pSignatureInfo = &signatureInfo;
signerParams.dwTimestampFlags = SIGNER_TIMESTAMP_RFC3161;
signerParams.pszAlgorithmOid = szOID_NIST_sha256;
//signerParams.dwTimestampFlags = SIGNER_TIMESTAMP_AUTHENTICODE;
//signerParams.pszAlgorithmOid = NULL;
signerParams.pwszTimestampURL = TIMESTAMPURL;
APPX_SIP_CLIENT_DATA sipClientData = {};
sipClientData.pSignerParams = &signerParams;
signerParams.pSipData = &sipClientData;
// Type definition for invoking SignerSignEx2 via GetProcAddress
typedef HRESULT(WINAPI *SignerSignEx2Function)(
DWORD,
PSIGNER_SUBJECT_INFO,
PSIGNER_CERT,
PSIGNER_SIGNATURE_INFO,
PSIGNER_PROVIDER_INFO,
DWORD,
PCSTR,
PCWSTR,
PCRYPT_ATTRIBUTES,
PVOID,
PSIGNER_CONTEXT *,
PVOID,
PVOID);
// Load the SignerSignEx2 function from MSSign32.dll
HMODULE msSignModule = LoadLibraryEx(
L"MSSign32.dll",
NULL,
LOAD_LIBRARY_SEARCH_SYSTEM32);
if (msSignModule)
{
SignerSignEx2Function SignerSignEx2 = reinterpret_cast<SignerSignEx2Function>(
GetProcAddress(msSignModule, "SignerSignEx2"));
if (SignerSignEx2)
{
hr = SignerSignEx2(
signerParams.dwFlags,
signerParams.pSubjectInfo,
signerParams.pSigningCert,
signerParams.pSignatureInfo,
signerParams.pProviderInfo,
signerParams.dwTimestampFlags,
signerParams.pszAlgorithmOid,
signerParams.pwszTimestampURL,
signerParams.pCryptAttrs,
signerParams.pSipData,
signerParams.pSignerContext,
signerParams.pCryptoPolicy,
signerParams.pReserved);
}
else
{
DWORD lastError = GetLastError();
hr = HRESULT_FROM_WIN32(lastError);
}
FreeLibrary(msSignModule);
}
else
{
DWORD lastError = GetLastError();
hr = HRESULT_FROM_WIN32(lastError);
}
// Free any state used during app package signing
if (sipClientData.pAppxSipState)
{
sipClientData.pAppxSipState->Release();
}
return hr;
}
答案 11 :(得分:0)
我使用的是globalsign证书,他们很好地说了同样的话。
不可能用标准的EV代码签名来编写签名脚本,因为它们正在促进HSM平台的使用
...这远远超出了我的预算。与他们所说的相反,我成功做到了:
"C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe" sign /fd sha256 /f "MyCertificate.cer" /csp "eToken Base Cryptographic Provider" /kc "[{{TokenPassword}}]=ContainerTame" "FileToSign"
=>此命令返回以下错误:
Error information: "CryptExportPublicKeyInfoEx failed" (87/0x57)
我不太了解这个问题。 但是,如果您再次运行以下命令,它将起作用
"C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\SignTool.exe" sign /tr http://timestamp.globalsign.com/scripts/timestamp.dll "MyFileToSign"
Done Adding Additional Store
Successfully signed: MyFileToSign
这在teamcity构建中有效,无需在teamcity构建代理中登录活动帐户。