web-dev-qa-db-fra.com

Automatisez la signature de code de validation étendue (EV)

Nous avons récemment acheté un certificat de signature de code DigiCert EV. Nous pouvons signer des fichiers .exe à l'aide de signtool.exe. Cependant, chaque fois que nous signons un fichier, il demande le mot de passe SafeNet eToken.

Comment pouvons-nous automatiser ce processus, sans intervention de l'utilisateur, en stockant/mettant en cache le mot de passe quelque part?

55
decasteljau

Vous avez une réponse de Digicert:

Malheureusement, une partie de la sécurité avec le certificat de signature de code EV est que vous devez entrer le mot de passe à chaque fois. Il n'y a aucun moyen de l'automatiser.

3
decasteljau

Il n'y a aucun moyen de contourner la boîte de dialogue de connexion AFAIK, mais ce que vous pouvez faire est de configurer le client d'authentification SafeNet afin qu'il ne le demande qu'une seule fois par session de connexion.

Je cite le doc SAC (trouvé une fois installé dans \ProgramFiles\SafeNet\Authentication\SAC\SACHelp.chm, chapitre 'Client Settings ',' Enabling Client Logon') ici:

Lorsque la connexion unique est activée, les utilisateurs peuvent accéder à plusieurs applications avec une seule demande de mot de passe de jeton au cours de chaque session d'ordinateur. Cela évite à l'utilisateur de se connecter séparément à chaque application.

Pour activer cette fonctionnalité qui est désactivée par défaut, accédez aux paramètres avancés du SAC et cochez la case "Activer la connexion unique":

enter image description here

Redémarrez votre ordinateur et il ne devrait désormais demander qu'une seule fois le mot de passe du jeton. Dans notre cas, nous avons plus de 200 binaires à signer pour chaque build, c'est donc un total must.

Sinon, voici un petit exemple de code de console C # (équivalent à celui de m1st0) qui vous permet de répondre automatiquement aux dialogues de connexion (doit probablement s'exécuter en tant qu'administrateur):

    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();
    }
52
Simon Mourier

En développant cette réponse , cela peut être automatisé en utilisant CryptAcquireContext et CryptSetProvParam pour entrer le jeton PIN par programme et CryptUIWizDigitalSign pour effectuer la signature par programme. J'ai créé une application console (code ci-dessous) qui prend en entrée le fichier de certificat (exporté en cliquant avec le bouton droit sur le certificat dans SafeNet Authentication Client et en sélectionnant "Exporter ...") , le nom du conteneur de clé privée (trouvé dans SafeNet Authentication Client), le code PIN du jeton, l'URL d'horodatage et le chemin d'accès du fichier à signer. Cette application de console a fonctionné lorsqu'elle était appelée par l'agent de génération TeamCity où le jeton USB était connecté.

Exemple d'utilisation:
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;
}

Exportation du certificat dans un fichier:
Exporting the Certificate to a File

Nom du conteneur de clé privée:
Private Key Container Name

11
draketb

Je suis devenu un outil bêta qui aidera à automatiser le processus de construction.

C'est l'application Windows Client-Server. Vous pouvez démarrer le serveur sur l'ordinateur où le jeton EV a été inséré. Entrez le mot de passe pour le jeton au démarrage de l'application côté serveur. Après cela, vous pouvez signer des fichiers à distance. L'application côté client remplace entièrement signtool.exe afin que vous puissiez utiliser les scripts de génération existants.

Code source situé ici: https://github.com/SirAlex/RemoteSignTool

Edit: Nous avons utilisé avec succès cet outil pour la signature de code 24 heures sur 24, 7 jours sur 7 sur notre serveur de génération. Tout fonctionne bien.

9
Aleksey Kharlanov

En développant les réponses déjà dans ce fil, il est possible de fournir le mot de passe du jeton en utilisant le programme standard signtool de Microsoft.

1. Exportez votre certificat public vers un fichier à partir du client SafeNet Exporting the Certificate to a File

2. Trouvez le nom de votre conteneur de clé privée
Private Key Container Name

3. Trouvez le nom de votre lecteur Reader Name

4. Formatez le tout ensemble

Le CSP eToken a une fonctionnalité cachée (ou du moins pas largement annoncée) pour analyser le mot de passe du jeton hors du nom du conteneur.

Le format est l'un des suivants

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

Où:

  • reader est le "nom du lecteur" de l'interface utilisateur du client SafeNet
  • password est votre mot de passe de jeton
  • name est le "nom du conteneur" de l'interface utilisateur du client SafeNet

Vraisemblablement, vous devez spécifier le nom du lecteur si vous avez plus d'un lecteur connecté - comme je n'ai qu'un seul lecteur, je ne peux pas le confirmer.

5. Transmettez les informations à signtool

  • /f certfile.cer
  • /csp "eToken Base Cryptographic Provider"
  • /k "<value from step 4>"
  • tout autre indicateur signtool dont vous avez besoin

Exemple de commande signtool comme suit

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

Quelques images prises de cette réponse: https://stackoverflow.com/a/47894907/542019

9
Austin Morton

En fait, sous Windows, vous pouvez spécifier le mot de passe du jeton entièrement par programme. Cela peut être fait en créant un contexte ( CryptAcquireContext ) avec l'indicateur CRYPT_SILENT en utilisant le nom du jeton sous la forme "\\.\AKS ifdh 0" ou le nom du conteneur de jetons, qui est un guide visible dans les propriétés du certificat dans le Application client d'authentification. Vous devez ensuite utiliser CryptSetProvParam avec le paramètre PP_SIGNATURE_PIN pour spécifier votre mot de passe de jeton. Après cela, le processus peut utiliser des certificats sur ce jeton pour signer des fichiers.
Remarque: une fois que vous avez créé le contexte, cela semble fonctionner entièrement pour le processus actuel, pas besoin de le passer à d'autres fonctions de l'API Crypto ou quoi que ce soit. Mais n'hésitez pas à commenter si vous trouvez une situation où des efforts supplémentaires seront nécessaires.
Modifier: exemple de code ajouté

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;
    }
}
6
avzhatkin

J'ai utilisé AutoHotKey pour automatiser la saisie du mot de passe à l'aide du script suivant. Nous avons essayé de créer une interface Web pour que nos développeurs envoient les fichiers binaires à la boîte Windows avec ce script en cours d'exécution afin qu'il puisse être signé et renvoyé.

  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}
    }   
  } 

Je dois noter que ce que j'ai partagé n'est pas complètement sécurisé, mais nous avons également rencontré ce problème nécessitant soit l'achat de clés de signature pour chaque développeur, soit l'attribution d'un travail à un gestionnaire de signature qui approuverait la signature du logiciel publié. Je pense que ce sont les meilleurs processus sécurisés - une fois que les choses passent l'assurance qualité et sont approuvées pour publication, elles peuvent être officiellement signées. Cependant, les besoins des petites entreprises peuvent exiger que cela se fasse d'une autre manière automatisée.

J'ai utilisé à l'origine osslsigncode sous Linux (avant les certificats EV) pour automatiser la signature des exécutables Windows (car nous avions un serveur Linux faisant beaucoup de travail pour la facilité et la collaboration des développeurs). J'ai contacté le développeur d'osslsigncode pour voir s'il peut utiliser les jetons DigiCert SafeNet pour aider à l'automatiser d'une manière différente puisque je peux les voir sur Linux. Sa réponse a donné de l'espoir mais je ne suis pas sûr de tout progrès et je n'ai pas pu consacrer plus de temps à aider

5
m1st0

Variante Python de l'outil:

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 __== '__main__':
    print('Token unlocker has been started...')
    print('DO NOT CLOSE THE WINDOW!')
    main()

De plus, j'ai découvert que la console oVirt a un comportement par défaut pour envoyer un verrou à Windows. Vous devez le désactiver dans les options du serveur et configurer la connexion automatique.

2
A.N.

Dans mon cas, Digicert délivre un certificat Standard (OV) pour le CI, gratuitement si vous avez déjà un certificat EV.

Je sais que ce n'est pas la solution, mais si vous ne pouvez pas mettre le jeton dans le serveur (un serveur cloud), c'est la voie à suivre.

2
Ricardo Polo

Ma façon de procéder est:

  1. Ouvrez le jeton

    PCCERT_CONTEXT cert = OpenToken (SAFENET_TOKEN, EV_PASS);

  2. Signez le fichier en utilisant le jeton, les certificats racine/croisé si nécessaire et le certificat EV chargé en mémoire.

    HRESULT hr = SignAppxPackage (cert, FILETOSIGN);

Utilisation de SignerSignEx2 ():

Le fichier est signé à l'aide de SignerSignEx2 () qui doit être chargé en mémoire à l'aide de LoadLibrary () et 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();
}

Horodatage

De plus, vous devez horodater votre fichier signé et le faire en utilisant une autorité d'horodatage à laquelle vous vous connectez.

Cela se fait en vérifiant en toute sécurité un serveur d'horodatage via URL pour la date et l'heure actuelles. Chaque autorité de signature a son propre serveur d'horodatage. L'horodatage est une étape supplémentaire dans le processus de signature de code, mais en ce qui concerne la signature de code EV, c'est une exigence qui ajoute une couche de sécurité supplémentaire au PE signé. Pour cette raison, ajoutez à votre code une vérification si l'utilisateur est connecté à 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;
}

Chargement d'un certificat à partir d'un fichier

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);
}

Chargement d'un certificat en mémoire

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, "");
}

Une fois le certificat chargé après avoir accédé au jeton matériel, nous le chargeons:

std::vector<unsigned char> dataCertEV(signingCertContext->pbCertEncoded,
        signingCertContext->pbCertEncoded + signingCertContext->cbCertEncoded);

Enfin, la signature se fait dans la fonction suivante:

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;
}

Voir cet article que j'ai écrit .

0
Michael Haephrati