自动化扩展validation(EV)代码签名
我们最近购买了DigiCert EV代码签名证书。 我们可以使用signtool.exe签署.exe文件。 但是,每次我们签署文件时,都会提示inputSafeNet eToken密码。
我们如何在没有用户干预的情况下通过在某处存储/caching密码来自动执行此过程?
无法绕过login对话框AFAIK,但您可以configurationSafeNet身份validation客户端,以便每次login会话时只需要一次。
我引用SAC文档(一旦安装在\ProgramFiles\SafeNet\Authentication\SAC\SACHelp.chm
,“ Client Settings
”,“ Enabling Client Logon
”一章)
启用单一login时,用户可以在每次计算机会话期间只访问一个令牌密码请求,以访问多个应用程序。 这减轻了用户分别login每个应用程序的需要。
要启用默认禁用的此function,请转到SAC高级设置,然后选中“启用单一login”框:
重新启动您的计算机,现在应该只提示input令牌密码一次。 在我们的例子中,每个版本都有200多个二进制文件,所以这是一个必须的 。
否则,这里是一个小的C#控制台示例代码(相当于m1st0之一),允许您自动响应login对话框(可能需要以admin身份运行):
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(); }
我做了testing工具,这将有助于自动化构build过程。
它是客户端 – 服务器Windows应用程序。 您可以在插入EV令牌的计算机上启动服务器。 在服务器端应用程序启动时input令牌密码。 在此之后,您可以远程签名文件。 客户端应用程序完全替代signtool.exe,以便您可以使用现有的构build脚本。
源代码位于: https : //github.com/SirAlex/RemoteSignTool
编辑:我们成功地使用这个工具在我们的生成服务器上的24×7全年的代码签名。 一切正常。
从Digicert得到了一个答案:
不幸的是,EV代码签名证书的一部分安全性是您必须每次input密码。 没有办法自动化它。
我使用AutoHotKey来使用以下脚本自动执行密码input。 我们一直在试图为我们的开发人员创build一个基于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} } }
我必须注意到,我所分享的并不完全不安全,但是我们也遇到了这个问题,要么为每个开发人员购买签名密钥,要么分配签名pipe理员的工作来批准已发布软件的签名。 我相信这些都是更好,更安全的stream程 – 只要质量保证通过并获准发布,就可以正式签署。 但是,较小的公司需求可能会决定这是以其他自动化方式完成的。
我最初使用Linux上的osslsigncode (EV证书之前)来自动签署Windows可执行文件(因为我们有一个Linux服务器为开发者的简化和协作做了很多工作)。 我已经联系了osslsigncode的开发者,看看他是否可以利用DigiCert SafeNet令牌以不同的方式实现自动化,因为我可以在Linux上看到它们。 他的答复提供了希望,但我不确定有什么进展,我也不能有更多的时间来帮忙
我的情况Digicert颁发一个CI的标准(OV)证书,如果你已经有一个EV证书是免费的。
我知道这不是解决scheme,但如果你不能把令牌放在服务器(云服务器),这是要走的路。
实际上在Windows上,您可以完全以编程方式指定令牌密码。 这可以通过创build带有标志CRYPT_SILENT的上下文( CryptAcquireContext )来完成,使用forms为“\\。\ AKS ifdh 0”的令牌名称或令牌容器名称,或者是在身份validation客户端应用程序中的证书属性中可见的一些guid。 然后您需要使用参数PP_SIGNATURE_PIN的CryptSetProvParam来指定您的令牌密码。 之后,进程可以使用该令牌上的证书来签名文件。
注意:一旦你创build上下文似乎只是为当前进程完全工作,不需要将其传递给其他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; } }
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控制台具有发送locking到Windows的默认行为。 您需要在服务器选项和设置自动login中禁用它。