你目前正处于脱机状态,正在等待 Internet 重新连接

如何以编程方式为 Internet Information Server (IIS) 安装 SSL 证书

重要说明:本文包含有关编辑元数据库的信息。编辑元数据库之前,请务必保留一个备份副本,如果发生问题,可以还原该副本。有关如何执行此操作的信息,请参见 Microsoft 管理控制台 (MMC) 中的“配置备份/还原”帮助主题。
概要

本文分步介绍了如何以编程方式为 Internet Information Server (IIS) 创建并安装安全套接字层 (SSL) 证书。虽然 IIS 版本 4.0 和 IIS 版本 5.0 各自提供了一个用于创建并安装 SSL 服务器证书的用户界面 (UI),但是您也可以通过编程方式实现该目的。

返回页首

创建并安装 SSL 证书的步骤

要以编程方式为 IIS 服务器创建并安装 SSL 证书,请按照下列步骤操作:
  1. 您必须将请求发送到证书颁发机构,然后才能发布服务器证书。如果您已经持有证书,并已经将其存储在 IIS 服务器上的文件中,则不要发送请求。
  2. 将证书导入到合适的证书存储区。
  3. 配置 IIS 以使用在步骤 1 中获取的证书。

返回页首

配置 IIS 以创建并安装 SSL 证书

警告:如果编辑元数据库不当,可能会导致严重问题,您可能需要重新安装使用元数据库的所有产品。Microsoft 不能保证由于您错误地编辑元数据库而导致的问题能够得到解决。编辑元数据库需要您自担风险。

注意:每次编辑元数据库前都要进行备份。 要配置 IIS 以创建并安装 SSL 证书,请按照下列步骤操作:
  1. 使用 Microsoft Windows Crypto API 获取证书的指纹属性。
  2. 必须将 IIS 元数据库属性 SSLCertHash 设置为指纹的值。
  3. 必须将 IIS 元数据库属性 SSLStoreName 设置为要使用的存储区。
请参见下文中的代码示例,以获取证书的指纹,然后再获取 SSLCertHash 属性:
用于获取服务器身份验证证书指纹的 C 代码
用来将 SSLCertHash 属性输入到元数据库中的 C 代码

返回页首

在特定网站上启用 SSL 的步骤

执行“配置 IIS 以创建并安装 SSL 证书”一节中的步骤后,可以在特定网站或特定文件夹中启用 SSL。为此,您必须在要使用的文件夹中启用 SSL 选项。以下步骤与在“配置 IIS 以创建并安装 SSL 证书”一节中描述的各步骤相对应:
  1. 调用 CEnroll::createPKCS10() 方法时,会创建证书请求。将用法设置为以下预定义值:szOID_PKIX_KP_SERVER_AUTH
    #define szOID_PKIX_KP_SERVER_AUTH       "1.3.6.1.5.5.7.3.1"		
  2. 调用 ICertRequest::Submit () 方法时,会将一个证书请求提交给证书颁发机构。
  3. 证书可以从证书颁发机构检索,然后安装在合适的存储区中。IIS 服务器证书向导只查找本地计算机证书存储区中可用于服务器身份验证的证书。


返回页首

配置 IIS 的步骤

将证书保存到存储区后,必须按照以下方法配置 IIS:
  1. 使用 CertGetCertificateContextProperty() 方法获取指纹属性和 CERT_HASH_PROP_ID 属性的值。CertGetCertificateContextProperty() 函数使用 CryptHashCertificate() 方法来计算 CERT_HASH_PROP_ID 属性的值。如果 HASH 值不存在,则 CertGetCertificateContextProperty() 会返回 SHA1 算法。
  2. 您必须创建新的二进制元数据库属性 SSLCertHash,它与网站相对应。然后将 SSLCertHash 设置为在本节步骤 1 中获取的证书指纹。如果发生此问题,架构会将 SSLCertHash 属性错误地指定为展开的以空字符结尾的字符串,而不是二进制数据,因此无法使用 IIS 管理对象来导入 SSLCertHash。只能使用 IIS 管理基本对象来导入此值。要使用 IIS 管理基本对象导入 SSLCertHash,您必须使用十进制值 5506。
  3. 必须为对应的网站创建新的字符串元数据库属性 SSLStoreName。将 SSLStoreName 设置为字符串值 MY。您可以通过 IIS 管理对象(例如,采用 ADSI 脚本)或 IIS 管理基本对象设置 SSLStoreName。要使用 IIS 管理基本对象来设置 SSLStoreName,请使用十进制值 5511。


返回页首

用来获取服务器身份验证证书指纹的 C 代码

以下 Microsoft C 代码示例描述了如何获取服务器身份验证证书的指纹属性:
#include <stdio.h>#include <windows.h>#include <wincrypt.h>#define MY_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)//--------------------------------------------------------------------//    Define the name of the store where the needed certificate//    can be found. #define CERT_STORE_NAME  L"MY"//--------------------------------------------------------------------//   Declare local functions.//   Local function definitions follow main.void HandleError(char *s);void main(void){//--------------------------------------------------------------------// Declare and initialize local variables. // This includes initializing a pointer to the message. // Usually, the message will exist somewhere and a pointer will// be passed to the application.//--------------------------------------------------------------------// System store handleHCERTSTORE hStoreHandle;   //--------------------------------------------------------------------// Pointer to a certificatePCCERT_CONTEXT pCert;PCCERT_CONTEXT pPrevCert; LPBYTE pEncodedBytes = NULL;LPBYTE pHash;DWORD cbData, i;//--------------------------------------------------------------------// Open a certificate store.if ( !( hStoreHandle = CertOpenStore(   CERT_STORE_PROV_SYSTEM,   0,   NULL,   CERT_SYSTEM_STORE_LOCAL_MACHINE,   CERT_STORE_NAME))){     HandleError("The MY store could not be opened.");}pPrevCert = NULL;for (; ((pCert = CertEnumCertificatesInStore(hStoreHandle, pPrevCert)) 	                                         != NULL);    pPrevCert = pCert){    CERT_ENHKEY_USAGE *pKeyUsage;    DWORD j, nLen;    BOOL bFound = FALSE;    char certName[1024];    nLen = sizeof(certName);    certName[0] = 0;    if (CertNameToStr(MY_TYPE, &(pCert->pCertInfo->Subject),        CERT_X500_NAME_STR,        certName,        sizeof(certName)))    {        printf("Checking %s certificate\n", certName);    }    cbData = 0;    if (!CertGetEnhancedKeyUsage(pCert,        0,        NULL,        &cbData) || cbData == 0)    {        if (GetLastError() == CRYPT_E_NOT_FOUND)        {            printf("%s certificate is for all key usages\n", certName);            break;        }        else            printf("CertGetEnhancedKeyUsage failed with error code : %08X\n",                GetLastError());    }    pKeyUsage = (CERT_ENHKEY_USAGE *)        HeapAlloc(GetProcessHeap(), 0, cbData);    if (pKeyUsage == NULL)    {        printf("HeapAlloc failed with error code : %08X\n",            GetLastError());        HandleError("Certificate not found.");    }    if (!CertGetEnhancedKeyUsage(pCert,        0,        pKeyUsage,        &cbData))    {        if (GetLastError() == CRYPT_E_NOT_FOUND)        {            printf("%s certificate is for all key usages\n", certName);            HeapFree(GetProcessHeap(), 0, pKeyUsage);            break;        }        else        {            printf("CertGetEnhancedKeyUsage failed with error code : %08X\n",                GetLastError());            HeapFree(GetProcessHeap(), 0, pKeyUsage);            continue;        }    }    if (pKeyUsage->cUsageIdentifier == 0)    {        printf("%s certificate is for all key usages\n", certName);        HeapFree(GetProcessHeap(), 0, pKeyUsage);        break;    }    bFound = FALSE;    for (j = 0; j < pKeyUsage->cUsageIdentifier; j++)    {        if (strcmpi(pKeyUsage->rgpszUsageIdentifier[j], 			        szOID_PKIX_KP_SERVER_AUTH) == 0)        {            printf("%s certificate is for Server Authentication\n", 				   certName);            bFound = TRUE;            break;        }    }    HeapFree(GetProcessHeap(), 0, pKeyUsage);    if (bFound)        break;}if (pCert == NULL)   HandleError("Certificate not found.");if (pPrevCert){    CertFreeCertificateContext(pPrevCert);    pPrevCert = NULL;}/// CASE 2 Get the hash from the certificatepHash = NULL;cbData = 0;CertGetCertificateContextProperty(pCert, CERT_HASH_PROP_ID, NULL, &cbData);if (cbData == 0){   HandleError("CertGetCertificateContextProperty 1 failed");}pHash = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, cbData);if (pHash == NULL){   HandleError("HeapAlloc failed");}if (!CertGetCertificateContextProperty(pCert, CERT_HASH_PROP_ID, pHash, 	                                   &cbData)){   HandleError("CertGetCertificateContextProperty 2 failed");}printf("CERT_HASH_PROP_ID Length is %d\n", cbData);printf("CERT_HASH_PROP_ID BYTES [", cbData);for (i = 0; i < cbData; i++){    printf("%02X", pHash[i]);}printf("]\n");//--------------------------------------------------------------------// Clean up and free memory.if (pEncodedBytes)    HeapFree(GetProcessHeap(), 0, pEncodedBytes);if (pHash)    HeapFree(GetProcessHeap(), 0, pHash);if(pCert)     CertFreeCertificateContext(pCert);if(CertCloseStore(      hStoreHandle,       CERT_CLOSE_STORE_CHECK_FLAG)){    printf("The store closed and all certificates are freed. \n");}else{    printf("Store closed -- \n"          "not all certificates, CRLs or CTLs were freed");}} // End of main//--------------------------------------------------------------------//  This example uses the function HandleError, a simple error//  handling function, to print an error message to the standard error //  (stderr) file and exit the program. //  For most applications, replace this function with one //  that does more extensive error reporting.void HandleError(char *s){    fprintf(stderr,"An error occurred in running the program. \n");    fprintf(stderr,"%s\n",s);    fprintf(stderr, "Error number %x.\n", GetLastError());    fprintf(stderr, "Program terminating. \n");    exit(1);} // End of HandleError


返回页首

用来将 SSLCertHash 属性输入到元数据库中的 C 代码

以下示例代码介绍了如何使用 IIS 管理基本对象来设置元数据库中的 SSLCertHash 方法。下面的代码使用任意二进制数组作为证书指纹。实际代码将使用在“获取服务器身份验证证书的指纹”一节中运行代码时获得的指纹值。您可以设置 SSLCertHash;如果您已经配置了网站的 SSL,也可以获取已有的 SSLCertHash。具体采用哪种方式取决于您在编译时如何定义 SetData 方法。SSLCertHash 方法会返回现有的 CertHash
#define UNICODE // unicode must be defined for Metabase access#define INITGUID #include <windows.h>#include <httpfilt.h>#include <stdio.h> #define SETDATA#include <iadmw.h>    // COM Interface header #include <iiscnfg.h>  // MD_ & IIS_MD_ #defines extern "C" wmain (int argc, TCHAR ** argv){  	IMSAdminBase  *pIMeta;  	METADATA_HANDLE MyHandle;     HRESULT hres;   	METADATA_RECORD record = {0};    TCHAR szError [2048];	BYTE *myData=NULL;    DWORD dwSize = sizeof (record); 	DWORD i;	// this just a sample of some thumbprint	BYTE bar[]={0x24, 0xC6, 0xBA, 0xBB, 0x81, 0x76, 0x05, 0xC9, 0xC3, 		        0x97, 0x6D, 0x4D, 0xEB, 0x85, 0x8F, 0x4F, 0xBF, 0x38,				0xFD};            CoInitialize (NULL);        // get a pointer to the IIS Admin Base Object	hres = CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_ALL, 			IID_IMSAdminBase, (void **) &pIMeta);  	if (FAILED(hres))  	{	    wsprintf (szError, L"CoCreateInstance Failed. Error: %x\n", hres);        printf ("%S\n", szError);		CoUninitialize();        return TRUE;  	}        // for this test use only 1st server instance    hres = pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE, L"/LM", 		METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE, 20, &MyHandle); 	if (FAILED (hres) )	{		wsprintf (szError, L"OpenKey Failed. Error: %x\n", hres);        printf ("%S\n", szError);		goto clean;	}    	// SSLCertHash = 5506	record.dwMDIdentifier =  5506;    record.dwMDAttributes =METADATA_INHERIT;    record.dwMDUserType=IIS_MD_UT_SERVER;    record.dwMDDataType= BINARY_METADATA;    record.pbMDData = (unsigned char *) myData;#ifndef SETDATA#pragma message ("Building for GetData\n")    	again:    hres = pIMeta->GetData (MyHandle,argv[1], &record, &dwSize);   	if (FAILED (hres) )	{		if (hres == MD_ERROR_DATA_NOT_FOUND)		{			printf ("%S\n", L"Data not found, no certificate is set!");			goto clean;		}		else if (HRESULT_CODE(hres)==ERROR_INSUFFICIENT_BUFFER)		{			record.dwMDDataLen=dwSize;			myData = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwSize);			record.pbMDData  = (unsigned char *)myData;		    goto again;		}		else  		{			wsprintf (szError, L"GetData Failed. Error: %x\n", hres);			printf ("%S\n", szError);			goto clean;;		}	}	printf ("%S", L"Got thumbprint. You can compare" 		          L" it with the MMC for IIS value:\n");		for ( i=0; i<(record.dwMDDataLen/sizeof (BYTE)); i++)				printf ("%2X ", myData[i]);  	HeapFree(GetProcessHeap(), 0, myData);#else#pragma message ("Building for SetData\n")		record.pbMDData = bar;	record.dwMDDataLen = 19; // in real code it should be the size                                                    // of the thumbprint buffer	hres = pIMeta->SetData (MyHandle,argv[1], &record);	if (FAILED (hres) )	{		printf ("Set data failed: 0x%x!\n", hres);		goto clean;	}	else		printf ("New thumbprint is set\n");#endifclean:	pIMeta->CloseKey(MyHandle); 	pIMeta->SaveData();	pIMeta->Release();	CoUninitialize();	return 1;}


返回页首
参考
有关更多信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
315588如何使用客户端证书保护 ASP.NET 应用程序的安全

返回页首
属性

文章 ID:313624 - 上次审阅时间:08/04/2006 14:12:10 - 修订版本: 2.1

  • Microsoft Internet Information Server 4.0
  • Microsoft Internet Information Services 5.0
  • kbcrypt kbapi kbhowtomaster kbsecurity kbisapiext kbhowto KB313624
反馈