web-dev-qa-db-ja.com

拡張検証(EV)コード署名の自動化

最近、DigiCert EVコード署名証明書を購入しました。 signtool.exeを使用して.exeファイルに署名できます。ただし、ファイルに署名するたびに、SafeNet eTokenパスワードの入力を求められます。

パスワードをどこかに保存/キャッシュすることにより、ユーザーの介入なしにこのプロセスを自動化するにはどうすればよいですか?

55
decasteljau

Digicertから回答を得ました:

残念ながら、EVコード署名証明書のセキュリティの一部は、毎回パスワードを入力する必要があることです。それを自動化する方法はありません。

3
decasteljau

ログインダイアログを知ることはできませんが、SafeNet Authentication Clientを設定して、ログインセッションごとに1回だけ要求するようにすることができます。

私はSACドキュメントを引用します(\ProgramFiles\SafeNet\Authentication\SAC\SACHelp.chm、章 'Client Settings '、' Enabling Client Logon') ここに:

シングルログオンが有効になっている場合、ユーザーは各コンピューターセッション中にトークンパスワードの要求を1つだけ持つ複数のアプリケーションにアクセスできます。これにより、ユーザーが各アプリケーションに個別にログオンする必要性が軽減されます。

デフォルトで無効になっているこの機能を有効にするには、SACの詳細設定に移動し、[シングルログオンを有効にする]ボックスをオンにします。

enter image description here

コンピューターを再起動すると、トークンパスワードの入力が1回だけ要求されます。私たちの場合、ビルドごとに署名するバイナリが200以上あるため、これは合計mustです。

それ以外の場合、ログオンダイアログに自動的に応答できるようにする小さなC#コンソールサンプルコード(m1st0に相当)があります(おそらく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();
    }
52
Simon Mourier

この回答 を展開すると、これは 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;
}

ファイルへの証明書のエクスポート:
Exporting the Certificate to a File

プライベートキーコンテナ名:
Private Key Container Name

11
draketb

このスレッドで既に回答を展開すると、Microsoftの標準signtoolプログラムを使用してトークンパスワードを提供できます。

1。 SafeNet Clientから公開証明書をファイルにエクスポートしますExporting the Certificate to a File

2。秘密鍵コンテナ名を見つけます
Private Key Container Name

3。リーダー名を見つけてくださいReader Name

4。すべて一緒にフォーマットします

EToken CSPには、コンテナ名からトークンパスワードを解析するための隠された(または少なくとも広く宣伝されていない)機能があります。

形式は次のいずれかです

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

どこ:

  • readerはSafeNetクライアントUIからの「リーダー名」です
  • passwordはトークンのパスワードです
  • nameは、SafeNetクライアントUIからの「コンテナ名」です

複数のリーダーを接続している場合は、おそらくリーダー名を指定する必要があります。リーダーが1つしかないため、これを確認できません。

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/542019

9
Austin Morton

実際、Windowsでは、トークンパスワードを完全にプログラムで指定できます。これは、「\\。\ AKS ifdh 0」形式のトークン名またはトークンコンテナ名を使用してフラグCRYPT_SILENTを使用してコンテキスト( CryptAcquireContext )を作成することで実行できます。認証クライアントアプリケーション。次に、パラメータ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;
    }
}
6
avzhatkin

AutoHotKey を使用して、次のスクリプトを使用してパスワード入力を自動化しました。開発者がこのスクリプトを実行してバイナリをWindowsボックスに送信し、署名して返送できるように、Webベースのフロントエンドを作成しようとしています。

  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
m1st0

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

また、oVirtコンソールにはWindowsにロックを送信するデフォルトの動作があることがわかりました。サーバーオプションで無効にし、自動ログインを設定する必要があります。

2
A.N.

私の場合、Digicertは、既にEV証明書を持っている場合、CIの標準(OV)証明書を無料で発行します。

私はこれが解決策ではないことを知っていますが、トークンをサーバー(クラウドサーバー)に入れることができない場合、これが方法です。

2
Ricardo Polo

私のやり方は次のとおりです。

  1. トークンを開く

    PCCERT_CONTEXT cert = OpenToken(SAFENET_TOKEN、EV_PASS);

  2. トークン、必要に応じてルート/クロス証明書、およびメモリにロードされたEV証明書を使用してファイルに署名します

    HRESULT hr = SignAppxPackage(cert、FILETOSIGN);

SignerSignEx2()の使用:

ファイルは、LoadLibrary()およびGetProcAddress()を使用してメモリにロードする必要があるSignerSignEx2()を使用して署名されます。

// 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にセキュリティの追加レイヤーを追加する要件です。そのため、ユーザーがインターネットに接続しているかどうかをチェックするコードを追加してください。

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

私が書いたこの記事 を参照してください。

0