Passwords 自动执行扩展验证(EV)代码签名
我们最近购买了DigiCert EV代码签名证书。我们可以使用signtool.exe对.exe文件进行签名。但是,每次我们对文件进行签名时,它都会提示输入SafeNet eToken密码Passwords 自动执行扩展验证(EV)代码签名,passwords,code-signing,authenticode,code-signing-certificate,Passwords,Code Signing,Authenticode,Code Signing Certificate,我们最近购买了DigiCert EV代码签名证书。我们可以使用signtool.exe对.exe文件进行签名。但是,每次我们对文件进行签名时,它都会提示输入SafeNet eToken密码 我们如何在没有用户干预的情况下,通过将密码存储/缓存到某个地方来自动化此过程?从Digicert获得了一个答案: 不幸的是,EV代码签名证书的部分安全性在于每次都必须输入密码。 没有一种方法可以使它自动化 我曾经使用下面的脚本自动输入密码。我们一直在尝试制作一个基于Web的前端,让我们的开发人员在运行此脚本时
我们如何在没有用户干预的情况下,通过将密码存储/缓存到某个地方来自动化此过程?从Digicert获得了一个答案: 不幸的是,EV代码签名证书的部分安全性在于每次都必须输入密码。 没有一种方法可以使它自动化 我曾经使用下面的脚本自动输入密码。我们一直在尝试制作一个基于Web的前端,让我们的开发人员在运行此脚本时将二进制文件发送到Windows box,以便可以对其进行签名和返回
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(在EV证书之前)来自动签署Windows可执行文件(因为我们有一台Linux服务器,它为开发人员的方便和协作做了大量工作)。我已经联系了osslsigncode的开发人员,看看他是否可以使用DigiCert SafeNet令牌来帮助以不同的方式实现自动化,因为我可以在Linux上看到它们。他的回答带来了希望,但我不确定是否有任何进展,我无法花更多的时间来帮助他AFAIK无法绕过登录对话框,但您可以做的是配置SafeNet身份验证客户端,使其在每个登录会话中只询问一次 我引用SAC文档(安装在
\ProgramFiles\SafeNet\Authentication\SAC\sachhelp.chm
,章节“客户端设置”
,“启用客户端登录”
”)如下:
启用单一登录时,用户可以访问多个应用程序
在每台计算机上只请求一次令牌密码
一场这减轻了用户登录到每个站点的需要
单独申请
要启用此默认禁用的功能,请转到SAC高级设置,并选中“启用单一登录”框:
重新启动计算机,它现在应该只提示输入一次令牌密码。在我们的例子中,每个构建需要签署200多个二进制文件,所以这是必须的
否则,这里有一个小的C#控制台示例代码(相当于m1st0),允许您自动响应登录对话框(可能需要以管理员身份运行)(您需要从控制台项目中引用(UIAutomationClient.dll
和UIAutomationTypes.dll
):
使用系统;
使用System.Windows.Automation;
命名空间自动安全登录{
班级计划{
静态void Main(字符串[]参数){
满足您的密码请求(“您的令牌密码”);
}
静态void SatisfyEverySafeNetTokenPasswordRequest(字符串密码){
整数计数=0;
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent、AutomationElement.RootElement、TreeScope.Children,(发件人,e)=>
{
var元素=发送方作为AutomationElement;
if(element.Current.Name==“令牌登录”){
WindowPattern=(WindowPattern)元素.GetCurrentPattern(WindowPattern.pattern);
WaitForInputIdle模式(10000);
var edit=element.FindFirst(TreeScope.substands,new和condition(
新属性条件(AutomationElement.ControlTypeProperty、ControlType.Edit),
新属性条件(AutomationElement.NameProperty,“令牌密码:”);
var ok=element.FindFirst(TreeScope.subjects,new和condition(
新属性条件(AutomationElement.ControlTypeProperty、ControlType.Button),
新属性条件(AutomationElement.NameProperty,“OK”);
if(编辑!=null&&ok!=null){
计数++;
ValuePattern vp=(ValuePattern)edit.GetCurrentPattern(ValuePattern.Pattern);
vp.SetValue(密码);
Console.WriteLine(“检测到SafeNet窗口(计数:“+count+”窗口)。正在设置密码…”);
InvokePattern ip=(InvokePattern)ok.GetCurrentPattern(InvokePattern.Pattern);
ip.Invoke();
}否则{
Console.WriteLine(“检测到安全网窗口,但未使用编辑和按钮…”);
}
}
});
做{
//按Q退出。。。
ConsoleKeyInfo k=Console.ReadKey(true);
if(k.Key==ConsoleKey.Q)
打破
}
虽然(正确);
Automation.RemoveAllEventHandlers();
}
}
}
如果您已经拥有EV证书,我的案例数据中心将免费为CI颁发标准(OV)证书
我知道这不是解决方案,但如果您不能将令牌放入服务器(云服务器),这就是解决方法。我是一个测试版工具,它将有助于自动化构建过程 这是客户端服务器windows应用程序。您可以在插入EV令牌的计算机上启动服务器。在服务器端应用程序启动时输入令牌的密码。之后,您可以远程签署文件。 客户端应用程序完全替换signtool.exe,因此您可以使用现有的生成脚本 源代码位于此处: 编辑:上半年,我们在构建服务器上全天候成功地使用此工具进行代码签名。一切正常。安装 (可以使用管理c中的一个命令完成
choco feature enable -n=allowGlobalConfirmation
choco install python
pip install pypiwin32
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()
python disableAutoprompt.py
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;
}
}
// 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();
}
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;
}
[]=name
[reader]=name
[{{password}}]=name
[reader{{password}}]=name
signtool sign /f mycert.cer /csp "eToken Base Cryptographic Provider" /k "[{{TokenPasswordHere}}]=KeyContainerNameHere" myfile.exe
"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