Wir haben kürzlich ein DigiCert EV-Codesignaturzertifikat gekauft. Wir können EXE-Dateien mit signtool.exe signieren. Jedes Mal, wenn wir eine Datei signieren, werden Sie zur Eingabe des SafeNet eToken-Kennworts aufgefordert.
Wie können wir diesen Prozess ohne Benutzereingriff automatisieren, indem wir das Passwort irgendwo speichern / zwischenspeichern?
passwords
code-signing
authenticode
code-signing-certificate
decasteljau
quelle
quelle
Antworten:
Es gibt keine Möglichkeit, den Anmeldedialog AFAIK zu umgehen. Sie können den SafeNet-Authentifizierungsclient jedoch so konfigurieren, dass er nur einmal pro Anmeldesitzung abgefragt wird.
Ich zitiere das SAC-Dokument (gefunden nach der Installation in
\ProgramFiles\SafeNet\Authentication\SAC\SACHelp.chm
Kapitel 'Client Settings
', 'Enabling Client Logon
') hier:Um diese standardmäßig deaktivierte Funktion zu aktivieren, gehen Sie zu den erweiterten SAC-Einstellungen und aktivieren Sie das Kontrollkästchen "Einzelanmeldung aktivieren":
Starten Sie Ihren Computer neu und er sollte nur noch einmal zur Eingabe des Token-Kennworts auffordern. In unserem Fall müssen pro Build mehr als 200 Binärdateien signiert werden. Dies ist also ein absolutes Muss .
Andernfalls finden Sie hier einen kleinen C # -Konsolen-Beispielcode (entspricht m1st0), mit dem Sie automatisch auf Anmeldedialoge reagieren können (muss wahrscheinlich als Administrator ausgeführt werden) (Sie müssen in Ihrem Konsolenprojekt auf (
UIAutomationClient.dll
undUIAutomationTypes.dll
) verweisen :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(); } } }
quelle
AutomationElement
Baum angezeigt ). Ich weiß nicht, ob es irgendwie umgangen werden kann. Aber wenn ich diese Frage noch einmal besuche und die Antwort von @Austin finde, glaube ich, dass es sowieso eine bessere Lösung ist.Wenn Sie die Antworten in diesem Thread erweitern, können Sie das Token-Kennwort mit dem Standardprogramm signtool von Microsoft bereitstellen.
0. Öffnen Sie den SafeNet-Client in der erweiterten Ansicht
Die Installationspfade können variieren, aber für mich wird der SafeNet-Client installiert für:
C:\Program Files\SafeNet\Authentication\SAC\x64\SACTools.exe
Klicken Sie auf das Zahnradsymbol oben rechts, um die "erweiterte Ansicht" zu öffnen.
1. Exportieren Sie Ihr öffentliches Zertifikat vom SafeNet-Client in eine Datei
2. Suchen Sie den Namen Ihres privaten Schlüsselcontainers
3. Finden Sie Ihren Lesernamen
4. Formatieren Sie alles zusammen
Der eToken CSP verfügt über versteckte (oder zumindest nicht weit verbreitete) Funktionen zum Parsen des Token-Passworts aus dem Containernamen.
Das Format ist eines der folgenden
Wo:
reader
ist der "Lesername" auf der SafeNet-Client-Benutzeroberflächepassword
ist Ihr Token-Passwortname
ist der "Containername" in der SafeNet-Client-BenutzeroberflächeVermutlich müssen Sie den Namen des Lesers angeben, wenn Sie mehr als einen Leser angeschlossen haben - da ich nur einen Leser habe, kann ich dies nicht bestätigen.
5. Übergeben Sie die Informationen an signtool
/f certfile.cer
/csp "eToken Base Cryptographic Provider"
/k "<value from step 4>"
Beispiel signtool Befehl wie folgt
Einige Bilder aus dieser Antwort: https://stackoverflow.com/a/47894907/5420193
quelle
Wenn Sie diese Antwort erweitern , kann dies mithilfe von CryptAcquireContext und CryptSetProvParam automatisiert werden , um die Token-PIN programmgesteuert und CryptUIWizDigitalSign einzugeben , , um die Signatur programmgesteuert durchzuführen. Ich habe eine Konsolen-App (Code unten) erstellt, die als Eingabe die Zertifikatdatei (exportiert durch Klicken mit der rechten Maustaste auf das Zertifikat im SafeNet Authentication Client und Auswahl von "Exportieren ..."), den Namen des privaten Schlüsselcontainers (im SafeNet Authentication Client) verwendet. die Token-PIN, die Zeitstempel-URL und den Pfad der zu signierenden Datei. Diese Konsolen-App funktionierte, wenn sie vom TeamCity-Build-Agenten aufgerufen wurde, mit dem das USB-Token verbunden war.
Anwendungsbeispiel:
etokensign.exe c:\CodeSigning.cert CONTAINER PIN http://timestamp.digicert.com C:\program.exe
Code:
#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; }
Exportieren des Zertifikats in eine Datei:
Name des privaten Schlüsselcontainers:
quelle
Ich bin ein Beta-Tool, das dabei hilft, den Build-Prozess zu automatisieren.
Es ist eine Client-Server-Windows-Anwendung. Sie können den Server auf einem Computer starten, auf dem ein EV-Token eingefügt ist. Geben Sie beim Start der serverseitigen Anwendung das Kennwort für das Token ein. Danach können Sie Dateien remote signieren. Die clientseitige Anwendung ersetzt signtool.exe vollständig, sodass Sie vorhandene Build-Skripte verwenden können.
Der Quellcode befindet sich hier: https://github.com/SirAlex/RemoteSignTool
Bearbeiten: Wir haben dieses Tool erfolgreich für die Codesignatur im letzten Halbjahr rund um die Uhr auf unserem Build-Server verwendet. Alles funktioniert gut.
quelle
Tatsächlich können Sie unter Windows das Token-Passwort vollständig programmgesteuert angeben. Dies kann durch Erstellen eines Kontexts ( CryptAcquireContext ) mit dem Flag CRYPT_SILENT unter Verwendung des Token-Namens in der Form "\\. \ AKS ifdh 0" oder des Token-Containernamens erfolgen, der in den Zertifikateigenschaften in der Authentication Client-Anwendung angezeigt wird. Anschließend müssen Sie CryptSetProvParam mit dem Parameter PP_SIGNATURE_PIN verwenden, um Ihr Token-Kennwort anzugeben. Danach kann der Prozess Zertifikate für dieses Token verwenden, um Dateien zu signieren.
Hinweis: Sobald Sie den Kontext erstellt haben, scheint er nur für den aktuellen Prozess vollständig zu funktionieren. Sie müssen ihn nicht mehr an andere Crypto API-Funktionen oder ähnliches übergeben. Sie können jedoch gerne einen Kommentar abgeben, wenn Sie eine Situation finden, in der weitere Anstrengungen erforderlich sind.
Bearbeiten: Codebeispiel hinzugefügt
quelle
Ich habe AutoHotKey verwendet, um die Passworteingabe mithilfe des folgenden Skripts zu automatisieren. Wir haben versucht, ein webbasiertes Frontend für unsere Entwickler zu erstellen, um die Binärdateien mit diesem Skript an die Windows-Box zu senden, damit sie signiert und zurückgegeben werden können.
Ich muss beachten, dass das, was ich geteilt habe, nicht vollständig nicht sicher ist, aber wir sind auch auf dieses Problem gestoßen, bei dem entweder Signaturschlüssel für jeden Entwickler gekauft oder ein Job eines Signaturmanagers zugewiesen werden muss, der die Signatur der freigegebenen Software genehmigt. Ich glaube, das sind die besseren und sichereren Prozesse - sobald die Dinge die Qualitätssicherung bestanden und zur Freigabe freigegeben wurden, können sie offiziell unterzeichnet werden. Kleinere Unternehmensanforderungen können jedoch vorschreiben, dass dies auf eine andere automatisierte Weise erfolgt.
Ich habe ursprünglich Osslsigncode unter Linux (vor EV-Zertifikaten) verwendet, um das Signieren von ausführbaren Windows-Dateien zu automatisieren (da wir einen Linux-Server hatten, der viel Arbeit für die Benutzerfreundlichkeit und Zusammenarbeit leistete). Ich habe den Entwickler von osslsigncode kontaktiert, um zu prüfen, ob er die DigiCert SafeNet-Token verwenden kann, um sie auf andere Weise zu automatisieren, da ich sie unter Linux sehen kann. Seine Antwort gab Hoffnung, aber ich bin mir über keine Fortschritte sicher und konnte nicht mehr Zeit für Hilfe aufwenden
quelle
Installieren https://chocolatey.org/docs/installation (kann mit einem Befehl an der administrativen Eingabeaufforderung ausgeführt werden)
(Eingabeaufforderung neu starten)
Unterdrücken Sie die ständige Aufforderung von choco für jede Installation:
Installieren Sie Python mit dem folgenden Befehl:
(Eingabeaufforderung neu starten) Installieren Sie ein zusätzliches Python-Modul:
Speichern Sie folgenden Text in
disableAutoprompt.py
:Speichern Sie Ihr Passwort in passwd.txt und führen Sie es anschließend aus
Aus
SafeNet Authentication Client
- Konfiguration>Client Settings
>Advanced
>Enable Single Log On
Option aktiviert werden kann Menge Kennwortabfragen zu minimieren, aber es sie nicht vollständig deaktivieren (Getestet auf Version 10.4.26.0)Die C # -Anwendung (z. B. https://github.com/ganl/safenetpass ) funktioniert nicht mit aktiviertem Sperrbildschirm, aber mit diesem Python-Skript.
quelle
Verwenden Sie Microsoft Windows SDK 10 für signtool
quelle
Ich habe eine Antwort von Digicert erhalten:
quelle
In meinem Fall stellt Digicert ein Standardzertifikat (OV) für das CI kostenlos aus, wenn Sie bereits über ein EV-Zertifikat verfügen.
Ich weiß, dass dies nicht die Lösung ist, aber wenn Sie das Token nicht auf dem Server (einem Cloud-Server) ablegen können, ist dies der richtige Weg.
quelle
Ich mache es so:
Öffnen Sie den Token
PCCERT_CONTEXT cert = OpenToken (SAFENET_TOKEN, EV_PASS);
Signieren Sie die Datei bei Bedarf mit dem Token, den Root- / Cross-Zertifikaten und dem in den Speicher geladenen EV-Zertifikat.
HRESULT hr = SignAppxPackage (cert, FILETOSIGN);
Verwenden von SignerSignEx2 ():
Die Datei wird mit SignerSignEx2 () signiert, das mit LoadLibrary () und GetProcAddress () in den Speicher geladen werden muss:
Zeitstempeln
Außerdem müssen Sie Ihre signierte Datei mit einem Zeitstempel versehen und dies mit einer Zeitstempelberechtigung tun, mit der Sie eine Verbindung herstellen.
Dies erfolgt durch sicheres Überprüfen eines Zeitstempelservers über eine URL auf das aktuelle Datum und die aktuelle Uhrzeit. Jede Signaturbehörde verfügt über einen eigenen Zeitstempelserver. Das Zeitstempeln ist ein zusätzlicher Schritt im Code-Signierprozess. Wenn es jedoch um das Signieren von EV-Codes geht, ist dies eine Anforderung, die dem signierten PE eine zusätzliche Sicherheitsebene hinzufügt. Fügen Sie aus diesem Grund Ihrem Code eine Überprüfung hinzu, ob der Benutzer mit dem Internet verbunden ist.
Laden eines Zertifikats aus einer Datei
Laden eines Zertifikats in den Speicher
Nachdem das Zertifikat nach dem Zugriff auf das Hardware-Token geladen wurde, laden wir es:
Schließlich erfolgt die Signierung in der folgenden Funktion:
Siehe diesen Artikel, den ich geschrieben habe .
quelle