Cara menandatangani paket aplikasi secara terprogram (C++)

Pelajari cara menandatangani paket aplikasi dengan menggunakan fungsi SignerSignEx2.

Jika Anda ingin membuat paket aplikasi Windows secara terprogram dengan menggunakan API Kemasan, Anda juga perlu menandatangani paket aplikasi sebelum dapat disebarkan. API Kemasan tidak menyediakan metode khusus untuk menandatangani paket aplikasi. Sebagai gantinya, gunakan fungsi kriptografi standar untuk menandatangani paket aplikasi Anda.

Apa yang perlu Anda ketahui

Teknologi

Prasyarat

  • Anda harus memiliki aplikasi Windows yang dipaketkan. Untuk informasi tentang membuat paket aplikasi, lihat Cara membuat paket aplikasi.
  • Anda harus memiliki sertifikat penandatanganan kode yang sesuai untuk menandatangani paket aplikasi. Untuk informasi tentang membuat sertifikat penandatanganan kode pengujian, lihat Cara membuat sertifikat penandatanganan paket aplikasi. Muat sertifikat penandatanganan ini ke dalam struktur CERT_CONTEXT. Misalnya, Anda dapat menggunakan PFXImportCertStore dan CertFindCertificateInStore untuk memuat sertifikat penandatanganan.
  • Windows 8 memperkenalkan fungsi SignerSignEx2. Gunakan SignerSignEx2 saat Anda menandatangani paket aplikasi Windows.

Petunjuk

Langkah 1: Tentukan struktur yang diperlukan untuk SignerSignEx2

Selain header Wincrypt.h, fungsi SignerSignEx2 bergantung pada banyak struktur yang tidak ditentukan dalam file header SDK apa pun. Untuk menggunakan SignerSignEx2, Anda harus menentukan struktur ini sendiri:

typedef struct _SIGNER_FILE_INFO
{
    DWORD cbSize;
    LPCWSTR pwszFileName;
    HANDLE hFile;
}SIGNER_FILE_INFO, *PSIGNER_FILE_INFO;
 
typedef struct _SIGNER_BLOB_INFO
{
    DWORD cbSize;
    GUID *pGuidSubject;
    DWORD cbBlob;
    BYTE *pbBlob;
    LPCWSTR pwszDisplayName;
}SIGNER_BLOB_INFO, *PSIGNER_BLOB_INFO;
 
typedef struct _SIGNER_SUBJECT_INFO
{
    DWORD cbSize;
    DWORD *pdwIndex;
    DWORD dwSubjectChoice;
    union
    {
        SIGNER_FILE_INFO *pSignerFileInfo;
        SIGNER_BLOB_INFO *pSignerBlobInfo;
    };
}SIGNER_SUBJECT_INFO, *PSIGNER_SUBJECT_INFO;
 
// dwSubjectChoice should be one of the following:
#define SIGNER_SUBJECT_FILE    0x01
#define SIGNER_SUBJECT_BLOB    0x02
 
typedef struct _SIGNER_ATTR_AUTHCODE
{
    DWORD cbSize;
    BOOL fCommercial;
    BOOL fIndividual;
    LPCWSTR pwszName;
    LPCWSTR pwszInfo;
}SIGNER_ATTR_AUTHCODE, *PSIGNER_ATTR_AUTHCODE;
 
typedef struct _SIGNER_SIGNATURE_INFO
{
    DWORD cbSize;
    ALG_ID algidHash;
    DWORD dwAttrChoice;
    union
    {
        SIGNER_ATTR_AUTHCODE *pAttrAuthcode;
    };
    PCRYPT_ATTRIBUTES psAuthenticated;
    PCRYPT_ATTRIBUTES psUnauthenticated;
}SIGNER_SIGNATURE_INFO, *PSIGNER_SIGNATURE_INFO;
 
// dwAttrChoice should be one of the following:
#define SIGNER_NO_ATTR          0x00
#define SIGNER_AUTHCODE_ATTR    0x01
 
typedef struct _SIGNER_PROVIDER_INFO
{
    DWORD cbSize;
    LPCWSTR pwszProviderName;
    DWORD dwProviderType;
    DWORD dwKeySpec;
    DWORD dwPvkChoice;
    union
    {
        LPWSTR pwszPvkFileName;
        LPWSTR pwszKeyContainer;
    };
}SIGNER_PROVIDER_INFO, *PSIGNER_PROVIDER_INFO;
 
//dwPvkChoice should be one of the following:
#define PVK_TYPE_FILE_NAME       0x01
#define PVK_TYPE_KEYCONTAINER    0x02
 
typedef struct _SIGNER_SPC_CHAIN_INFO
{
    DWORD cbSize;
    LPCWSTR pwszSpcFile;
    DWORD dwCertPolicy; 
    HCERTSTORE hCertStore;
}SIGNER_SPC_CHAIN_INFO, *PSIGNER_SPC_CHAIN_INFO;
 
typedef struct _SIGNER_CERT_STORE_INFO
{
    DWORD cbSize;
    PCCERT_CONTEXT pSigningCert;
    DWORD dwCertPolicy;
    HCERTSTORE hCertStore;
}SIGNER_CERT_STORE_INFO, *PSIGNER_CERT_STORE_INFO;
 
//dwCertPolicy can be a combination of the following flags:
#define SIGNER_CERT_POLICY_STORE            0x01
#define SIGNER_CERT_POLICY_CHAIN            0x02
#define SIGNER_CERT_POLICY_SPC              0x04
#define SIGNER_CERT_POLICY_CHAIN_NO_ROOT    0x08
 
typedef struct _SIGNER_CERT
{
    DWORD cbSize;
    DWORD dwCertChoice;
    union
    {
        LPCWSTR pwszSpcFile;
        SIGNER_CERT_STORE_INFO *pCertStoreInfo;
        SIGNER_SPC_CHAIN_INFO *pSpcChainInfo;
    };
    HWND hwnd;
}SIGNER_CERT, *PSIGNER_CERT;
 
//dwCertChoice should be one of the following
#define SIGNER_CERT_SPC_FILE     0x01
#define SIGNER_CERT_STORE        0x02
#define SIGNER_CERT_SPC_CHAIN    0x03
 
typedef struct _SIGNER_CONTEXT
{
    DWORD cbSize;
    DWORD cbBlob;
    BYTE *pbBlob;
}SIGNER_CONTEXT, *PSIGNER_CONTEXT;
 
typedef struct _SIGNER_SIGN_EX2_PARAMS
{
    DWORD dwFlags;
    PSIGNER_SUBJECT_INFO pSubjectInfo;
    PSIGNER_CERT pSigningCert;
    PSIGNER_SIGNATURE_INFO pSignatureInfo;
    PSIGNER_PROVIDER_INFO pProviderInfo;
    DWORD dwTimestampFlags;
    PCSTR pszAlgorithmOid;
    PCWSTR pwszTimestampURL;
    PCRYPT_ATTRIBUTES pCryptAttrs;
    PVOID pSipData;
    PSIGNER_CONTEXT *pSignerContext;
    PVOID pCryptoPolicy;
    PVOID pReserved;
} SIGNER_SIGN_EX2_PARAMS, *PSIGNER_SIGN_EX2_PARAMS;
 
typedef struct _APPX_SIP_CLIENT_DATA
{
    PSIGNER_SIGN_EX2_PARAMS pSignerParams;
    IUnknown* pAppxSipState;
} APPX_SIP_CLIENT_DATA, *PAPPX_SIP_CLIENT_DATA;

Langkah 2: Panggil SignerSignEx2 untuk menandatangani paket aplikasi

Setelah menentukan struktur yang diperlukan yang ditentukan di langkah sebelumnya, Anda dapat menggunakan salah satu opsi yang tersedia pada fungsi SignerSignEx2 untuk menandatangani paket aplikasi. Saat Anda menggunakan SignerSignEx2 dengan paket aplikasi Windows, pembatasan ini berlaku:

  • Anda harus menyediakan penunjuk ke struktur APPX_SIP_CLIENT_DATA sebagai parameter pSipData saat Anda menandatangani paket aplikasi. Anda harus mengisi anggota pSignerParams dari APPX_SIP_CLIENT_DATA dengan parameter yang sama dengan yang Anda gunakan untuk menandatangani paket aplikasi. Untuk melakukan ini, tentukan parameter yang Anda inginkan pada struktur SIGNER_SIGN_EX2_PARAMS , tetapkan alamat struktur ini ke pSignerParams, lalu langsung referensikan anggota struktur juga ketika Anda memanggil SignerSignEx2.
  • Setelah Anda memanggil SignerSignEx2, Anda harus membebaskan pAppxSipState di pSipData dengan memanggil IUnknown::Release di pAppxSipState jika bukan NULL.
  • Anggota algidHash dari struktur SIGNER_SIGNATURE_INFO harus memiliki algoritma hash yang sama yang digunakan dalam membuat paket aplikasi. Untuk informasi tentang cara menentukan algoritma hash dari paket aplikasi, lihat Cara menandatangani paket aplikasi menggunakan SignTool. Algoritma default Windows 8 yang digunakan MakeAppx dan Visual Studio untuk membuat paket aplikasi adalah "algidHash = CALG_SHA_256".
  • Jika Anda ingin memberi stempel waktu tanda tangan pada paket aplikasi juga, Anda harus melakukannya selama panggilan ke SignerSignEx2 dengan menyediakan parameter stempel waktu opsional SignerSignEx2 (dwTimestampFlags, pszTimestampAlgorithmOid, pwszHttpTimeStamp, psRequest). Memanggil SignerTimeStampEx3 atau variannya pada paket aplikasi yang sudah ditandatangani tidak didukung.

Berikut adalah beberapa contoh kode yang menunjukkan cara memanggil SignerSignEx2:

HRESULT SignAppxPackage(
    _In_ PCCERT_CONTEXT signingCertContext,
    _In_ LPCWSTR packageFilePath)
{
    HRESULT hr = S_OK;
 
    // 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_CHAIN_NO_ROOT;
    certStoreInfo.pSigningCert = signingCertContext;
 
    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;
 
    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;
}

Keterangan

Setelah menandatangani paket aplikasi, Anda juga dapat mencoba memvalidasi tanda tangan secara terprogram dengan menggunakan fungsi WinVerifyTrust dengan WINTRUST_ACTION_GENERIC_VERIFY_V2. Tidak ada pertimbangan khusus dalam hal ini untuk menggunakan WinVerifyTrust dengan paket aplikasi Windows.

Pengantar Penandatanganan Kode

API Pengemasan

fungsi kriptografi