Microsoft KB Archive/925521

From BetaArchive Wiki
Knowledge Base


Article ID: 925521

Article Last Modified on 1/13/2007



APPLIES TO

  • Microsoft Visual Studio 2005 Standard Edition
  • Microsoft Visual Studio 2005 Professional Edition
  • Microsoft Visual Studio 2005 Express Edition



SYMPTOMS

When you try to update a Microsoft Visual Studio 2005 ClickOnce application after the certificate that you used to sign the installation expires, you receive the following error message:

The deployment identity does not match the subscription.

CAUSE

This problem occurs because you must sign all ClickOnce deployments by using a digital certificate. You cannot use an expired certificate to sign a ClickOnce application. To deploy an update to an application that has an expired certificate, you must assign the application a new certificate. When the new certificate does not match the original certificate, you receive the error message that is mentioned in the "Symptoms" section.

WORKAROUND

To work around this problem, use one of the following methods.

Method 1

Uninstall the ClickOnce application that you signed by using the expired certificate. Then, reinstall the updated ClickOnce application that uses the new certificate.

Method 2

Create a command-line assembly that updates the certificate. To do this, follow these steps.

Microsoft provides programming examples for illustration only, without warranty either expressed or implied. This includes, but is not limited to, the implied warranties of merchantability or fitness for a particular purpose. This article assumes that you are familiar with the programming language that is being demonstrated and with the tools that are used to create and to debug procedures. Microsoft support engineers can help explain the functionality of a particular procedure. However, they will not modify these examples to provide added functionality or construct procedures to meet your specific requirements.

  1. In Visual Studio 2005, click New on the File menu, and then click Project.
  2. Click Visual C++, click Win32 Console Application, type RenewCert in the Name box, and then click OK.
  3. In the Win32 Application Wizard dialog box, click Finish.
  4. In the RenewCert.cpp file, replace the existing code with the following code:

    #include "stdafx.h"
    
     
    
     
    
    void ReadPFXFile(LPCWSTR fileName, CRYPT_DATA_BLOB *pPFX)
    
    {
    
                HANDLE hCertFile = NULL;
    
                DWORD cbRead = 0;
    
                DWORD dwFileSize = 0, dwFileSizeHi = 0;
    
     
    
                hCertFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    
                dwFileSize = GetFileSize(hCertFile, &dwFileSizeHi);
    
                pPFX->pbData = (BYTE *) CryptMemAlloc(dwFileSize*sizeof(BYTE));
    
                pPFX->cbData = dwFileSize;
    
     
    
                ReadFile(hCertFile, pPFX->pbData, pPFX->cbData, &cbRead, NULL);
    
                CloseHandle(hCertFile);
    
    }
    
     
    
    void GetPrivateKey(CRYPT_DATA_BLOB pPFX, LPCWSTR szPassword, HCRYPTPROV *hCPContext)
    
    {
    
                HCERTSTORE hCertStore = NULL;
    
                PCCERT_CONTEXT hCertContext = NULL;
    
                DWORD dwKeySpec = AT_SIGNATURE;
    
                BOOL bFreeCertKey = TRUE;
    
     
    
                hCertStore = PFXImportCertStore(&pPFX, szPassword, CRYPT_EXPORTABLE);
    
                hCertContext = CertEnumCertificatesInStore(hCertStore, NULL);
    
                CryptAcquireCertificatePrivateKey(hCertContext, 0, NULL, hCPContext, &dwKeySpec, &bFreeCertKey);
    
                CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
    
    }
    
     
    
    void PrintContainerName(HCRYPTPROV hCPContext)
    
    {
    
                DWORD containerNameLen = 0;
    
                CHAR *szContainerName = NULL;
    
     
    
                CryptGetProvParam(hCPContext, PP_CONTAINER, NULL, &containerNameLen, 0);
    
                szContainerName = (CHAR *)CryptMemAlloc(sizeof(BYTE)*containerNameLen);
    
                CryptGetProvParam(hCPContext, PP_CONTAINER, (BYTE *)szContainerName, &containerNameLen, 0);
    
                printf("This certificate's container name is: %s", szContainerName);
    
    }
    
     
    
    void MakeNewCert(HCRYPTPROV hCPContext, LPCWSTR szCertName, LPCWSTR szPassword, CRYPT_DATA_BLOB *pPFX)
    
    {
    
                CERT_NAME_BLOB certNameBlob = {0,NULL};
    
                PCCERT_CONTEXT hCertContext = NULL;
    
                SYSTEMTIME certExpireDate;
    
                HCERTSTORE hTempStore = NULL;
    
     
    
                CertStrToName(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szCertName, CERT_OID_NAME_STR, NULL, NULL, &certNameBlob.cbData, NULL);
    
                certNameBlob.pbData = (BYTE *)CryptMemAlloc(sizeof(BYTE)*certNameBlob.cbData);
    
                CertStrToName(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szCertName, CERT_OID_NAME_STR, NULL, certNameBlob.pbData, &certNameBlob.cbData, NULL);
    
     
    
                GetSystemTime(&certExpireDate);
    
                certExpireDate.wYear += 5;
    
     
    
                hCertContext = CertCreateSelfSignCertificate(hCPContext, &certNameBlob, 0, NULL, NULL, NULL, &certExpireDate, NULL);
    
                hTempStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, CERT_STORE_CREATE_NEW_FLAG, 0);
    
                CertAddCertificateContextToStore(hTempStore, hCertContext, CERT_STORE_ADD_NEW, NULL);
    
                PFXExportCertStoreEx(hTempStore, pPFX, szPassword, NULL, EXPORT_PRIVATE_KEYS);
    
                pPFX->pbData = (BYTE *)CryptMemAlloc(sizeof(BYTE)*pPFX->cbData);
    
                PFXExportCertStoreEx(hTempStore, pPFX, szPassword, NULL, EXPORT_PRIVATE_KEYS);
    
     
    
                CryptMemFree(certNameBlob.pbData);
    
                CertCloseStore(hTempStore, CERT_CLOSE_STORE_FORCE_FLAG);
    
                CertFreeCertificateContext(hCertContext);
    
    }
    
     
    
    void WritePFX(CRYPT_DATA_BLOB pPFX, LPCWSTR szOutputFile)
    
    {
    
                HANDLE hOutputFile = NULL;
    
                DWORD cbWritten = 0;
    
     
    
                hOutputFile = CreateFile(szOutputFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
    
                WriteFile(hOutputFile, pPFX.pbData, pPFX.cbData, &cbWritten, NULL);
    
     
    
                CloseHandle(hOutputFile);
    
    }
    
     
    
    int _tmain(int argc, _TCHAR* argv[])
    
    {
    
                LPCWSTR szCertFileName = NULL;
    
     
    
                CRYPT_DATA_BLOB pPFX;
    
                LPCWSTR szPassword = NULL;
    
                HCRYPTPROV hCPContext = NULL;
    
     
    
                LPCWSTR szCertName = L"CN=NewCert";
    
                CRYPT_DATA_BLOB pPfxOutputBlob = {0,NULL};
    
                LPCWSTR szOutFile = NULL;
    
     
    
                // Parse the command line.
    
                if(argc == 1)
    
                {
    
                            printf("renewcert <PFX File> <new cert filename> <new cert friendly name> [optional]<password>\n");
    
                            printf("Example: renewcert oldcert.pfx newcert.pfx \"CN=MyNewCert\" MySuperSecretPassword");
    
                            return 0;
    
                }
    
     
    
                if(argc >= 2)
    
                            szCertFileName = argv[1];
    
                if(argc >= 5)
    
                            szPassword = argv[4];
    
                // Uncomment this block to add <new cert filename> and <new cert friendly name> as parameters
    
                // NOTE: <new cert friendly name> must be of format "CN=<name>"
    
                if(argc >= 3)
    
                            szOutFile = argv[2];
    
                if(argc >= 4)
    
                            szCertName = argv[3];
    
     
    
                ReadPFXFile(szCertFileName, &pPFX);
    
                
    
                GetPrivateKey(pPFX, szPassword, &hCPContext);
    
     
    
                //PrintContainerName(hCPContext);
    
     
    
                // Uncomment this section to make a new PFX rather than just printing the container name.
    
                // Make sure you also uncomment the command line parameter section above.
    
                MakeNewCert(hCPContext, szCertName, szPassword, &pPfxOutputBlob);
    
                WritePFX(pPfxOutputBlob, szOutFile);
    
                
    
     
    
                // Clean up.
    
                CryptReleaseContext(hCPContext, 0);
    
                CryptMemFree(pPfxOutputBlob.pbData);
    
                CryptMemFree(pPFX.pbData);
    
                return 0;
    
    }
    
  5. In the stdafx.h file, replace the existing code with the following code:

    // stdafx.h : include file for standard system include files,
    
    // or project specific include files that are used frequently, but
    
    // are changed infrequently.
    
    //
    
     
    
    #pragma once
    
     
    
     
    
    #define WIN32_LEAN_AND_MEAN                     // Exclude rarely used material from Windows headers.
    
    #include <stdio.h>
    
    #include <tchar.h>
    
    #include <windows.h>
    
    #include <wincrypt.h>
    
  6. On the Project menu, click Properties to open the property pages for this project.
  7. Expand the Linker node, and then click Input.
  8. Click in the blank window to the right of Additional Dependencies, and then click the ellipsis button (...) to open to the Additional Dependencies dialog box.
  9. In the blank window, type Crypt32.lib, and then click OK.
  10. Click Apply, and then click OK to close the property pages.
  11. On the Build menu, click Build Solution.
  12. After the solution is built, excute the following command to update the certificate:

    renewcert <OldCertificate>.pfx <NewCertificate>.pfx \"CN=<NewCertificateName>\" <Password>

    Note <Oldcertificate> is a placeholder for the old certificate, <NewCertificate> is a placeholder for the new certificate, <NewCertificateName> is a placeholder for the name of the new certificate, and <Password> is a placeholder for the password.


STATUS

Microsoft has confirmed that this is a problem in the Microsoft products that are listed in the "Applies to" section.

MORE INFORMATION

Steps to reproduce this problem

  1. Start Visual Studio 2005.
  2. On the File menu, click New, and then click Project.
  3. Click Visual C#, click Windows Application, type WindowsApplication1 in the Name box, and then click OK.
  4. In Solution Explorer, right-click WindowsApplication1, and then click Properties.
  5. Click Signing, and then assign a certificate that expires soon.
  6. In Solution Explorer, right-click WindowsApplication1, and then click Publish. The Publish wizard starts.
  7. On the Where do you want to publish the application? page, type a valid URL, and then click Next. Use the following format for the URL:

    http://ServerName/FolderName

  8. On the Will the application be available offline? page, click the appropriate option.

    Notes
    • If you want to let the user run the application when the user is disconnected from the network, click Yes, this application will be available online or offline. The wizard creates a shortcut for the application on the Start menu.
    • If you want to run the application directly from the publish location, click No, this application is only available online. The wizard does not create a shortcut on the Start menu.
  9. Click Next to continue.
  10. Click Finish to publish the application.
  11. Install the ClickOnce application from the http://ServerName/FolderName/publish.htm URL.
  12. After the certificate expires, repeat steps 6 to 10 to republish the application.
  13. Try to install the ClickOnce application update from the http://ServerName/FolderName/publish.htm URL.



Additional query words: ClickOnce certificate

Keywords: kbtshoot kberrmsg kbbug kbprb KB925521