【问题标题】:How to read SSL certificate from windows certificate store using Delphi如何使用 Delphi 从 Windows 证书存储中读取 SSL 证书
【发布时间】:2021-08-21 06:49:13
【问题描述】:

我正在将 Delphi Windows 服务实现为服务器,并希望启用 TLS/SSL 以实现安全通信。为此,我使用了TIdServerIOHandlerSSLOpenSSL 组件,并且能够在客户端和服务器之间建立安全通信。

我的问题与我如何加载证书和密钥有关。我写了下面的代码来加载证书和密钥文件:

  IOHandler.SSLOptions.CertFile := 'localhost.cert.pem';    // Certificate file
  IOHandler.SSLOptions.KeyFile := 'localhost.key.pem';      // Key File    
  IOHandler.SSLOptions.RootCertFile := 'ca.cert.pem';       // Root certificate

所有文件(证书和密钥)都放在应用程序目录中。但其中一项要求是从 Windows 证书存储中读取证书。

如何从 Delphi 代码中的 Windows 证书存储中读取证书并将这些证书分配给 IOHandler.SSLOptions

【问题讨论】:

  • OpenSSL(以及扩展名 Indy)不支持从 Windnows 证书存储加载证书,因此您必须先将所需的证书导出到文件。

标签: delphi ssl openssl indy


【解决方案1】:

您可以使用 WinCrypt 函数将所选证书及其链导出到 PFX 文件,然后将该文件路径分配给 CertFile、KeyFile 和 RootCertFile 属性(是的,如果它包含所有证书,您可以使用同一个文件)。

证书必须有“出口”标志。

下一个例子在 Delphi 2010 中测试并使用JwaWinCrypt 单位:

// Some declarations missing in JwaWinCrypt
function PFXExportCertStoreEx(hStore: HCERTSTORE;
                              var pPFX: CRYPT_DATA_BLOB;
                              szPassword: LPCWSTR;
                              pvPra: Pointer;
                              dwFlags: DWORD): BOOL; stdcall; external 'Crypt32.dll';

const
  REPORT_NO_PRIVATE_KEY                 = $0001;
  REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY = $0002;
  EXPORT_PRIVATE_KEYS                   = $0004;
  PKCS12_INCLUDE_EXTENDED_PROPERTIES    = $0010;

  // Password to protect PFX file
  WidePass: WideString = ''; 


var
  pStore, pStoreTmp: HCERTSTORE;
  pCert: PCERT_CONTEXT;
  PFX,
  Hash: CRYPT_INTEGER_BLOB;

  ChainPara: CERT_CHAIN_PARA;
  EnhkeyUsage: CERT_ENHKEY_USAGE;
  CertUsage: CERT_USAGE_MATCH;
  pChainContext: PCCERT_CHAIN_CONTEXT;
  ppCertSimpleChain: ^PCERT_SIMPLE_CHAIN;
  ppCertChainElement: ^PCERT_CHAIN_ELEMENT;

  i, j: Integer;
  Buffer: RawByteString;

begin
  pStore := nil;
  pStoreTmp := nil;
  pCert := nil;

  PFX.pbData := nil;
  PFX.cbData := 0;

  // Open system certificate store
  pStore := CertOpenSystemStore(0, 'MY');

  // Open in-mem temporal certificate store
  pStoreTmp := CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, nil);

  // HEX SHA1 Hash of the certificate to find
  Buffer := 'YourCertificateSHA1HashHEX9be0c6fef0600b';
  Hash.cbData := Length(Buffer);
  Hash.pbData := @Buffer[1];
  // Find it
  pCert := CertFindCertificateInStore(pStore,
                                      X509_ASN_ENCODING,
                                      0,
                                      CERT_FIND_SHA1_HASH,
                                      @Hash,
                                      nil);

  // Now let's get the certificate's chain context
  EnhkeyUsage.cUsageIdentifier := 0;
  EnhkeyUsage.rgpszUsageIdentifier := nil;
  CertUsage.dwType := USAGE_MATCH_TYPE_AND;
  CertUsage.Usage := EnhkeyUsage;
  ChainPara.cbSize := SizeOf(CERT_CHAIN_PARA);
  ChainPara.RequestedUsage := CertUsage;

  CertGetCertificateChain(0, pCert, nil, nil, 
                          @ChainPara, 0, nil, @pChainContext);

  // Iterate the chain context and add every certificate to mem-store
  ppCertSimpleChain := Pointer(pChainContext^.rgpChain);
  for i := 1 to pChainContext.cChain do
  begin
    ppCertChainElement := Pointer(ppCertSimplechain^.rgpElement);
    for j := 1 to ppCertSimpleChain^.cElement do
    begin
      CertAddCertificateLinkToStore(pStoreTmp,
                                    ppCertChainElement^.pCertContext,
                                    CERT_STORE_ADD_REPLACE_EXISTING,
                                    nil);
      Inc(ppCertChainElement);
    end;
    Inc(ppCertSimpleChain);
  end;

  // Save to PFX ...
  PFX.pbData := nil;
  PFX.cbData := 0;
  // First a call with an empty BLOB to get the space needed
  PFXExportCertStoreEx(pStoreTmp, 
                       PFX, 
                       PWideChar(WidePass), 
                       nil, 
                       EXPORT_PRIVATE_KEYS + 
                       REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY + 
                       REPORT_NO_PRIVATE_KEY + 
                       PKCS12_INCLUDE_EXTENDED_PROPERTIES);

  // OK, reserve the needed memory
  GetMem(PFX.pbData, PFX.cbData);

  // Fill data
  PFXExportCertStoreEx(pStoreTmp, 
                       PFX, 
                       PWideChar(WidePass), 
                       nil, 
                       EXPORT_PRIVATE_KEYS + 
                       REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY + 
                       REPORT_NO_PRIVATE_KEY + 
                       PKCS12_INCLUDE_EXTENDED_PROPERTIES);

  // Now PFX.pbData points to PFX information of length PFX.cbData
  // Write it to a temporary file that replaces your PEM files.

  // Free memory used
  // I deliberately did not check whether 
  // previous API calls returned an error.
  // You should check.
  // Take a look to Microsoft's documentation for functions results
  // and GetLastError function for error code
  CertFreeCertificateChain(pChainContext);
  CertFreeCertificateContext(pCert);
  CertCloseStore(pStoreTmp, 0);
  CertCloseStore(pStore, 0);
  FreeMem(PFX.pbData);
end.

【讨论】:

    【解决方案2】:

    【讨论】:

    • 你在项目中使用过吗?
    • 我将证书保存在数据库中。这很方便,因为我可以从任何计算机访问证书,并且不需要将证书导入到 windows 证书存储中。
    猜你喜欢
    • 1970-01-01
    • 2011-01-01
    • 2018-05-23
    • 2019-01-07
    • 2011-11-09
    • 2020-12-15
    • 1970-01-01
    • 2010-10-24
    • 2019-03-21
    相关资源
    最近更新 更多