delphi - 如何使用 Delphi 从 Windows 证书存储中读取 SSL 证书

标签 delphi ssl openssl indy

我正在将 Delphi Windows 服务实现为服务器,并希望启用 TLS/SSL 以进行安全通信。为此我使用了TIdServerIOHandlerSSLOpenSSL组件,我能够在客户端和服务器之间建立安全通信。
我的问题与我如何加载证书和 key 有关。我写了下面的代码来加载证书和 key 文件:

  IOHandler.SSLOptions.CertFile := 'localhost.cert.pem';    // Certificate file
  IOHandler.SSLOptions.KeyFile := 'localhost.key.pem';      // Key File    
  IOHandler.SSLOptions.RootCertFile := 'ca.cert.pem';       // Root certificate
所有文件(证书和 key )都放在应用程序目录中。但其中一项要求是从 Windows 证书存储中读取证书。
我如何从 Delphi 代码中的 Windows 证书存储中读取证书并将这些证书分配给 IOHandler.SSLOptions ?

最佳答案

您可以使用 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.

关于delphi - 如何使用 Delphi 从 Windows 证书存储中读取 SSL 证书,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67816499/

相关文章:

c++ - 我如何在客户端支持多个版本的 TLS?

api - 我可以在 OpenSSL 中禁用 MAC 吗?

delphi - cxGrid - 检查记录的页脚摘要

delphi - 解码错误

delphi - 如何从键=值对字符串中获取电子邮件地址?

java - WebSphere + 简单的 HTTPS Post = SocketException

android - 桌面版 Chrome 中的正 SSL 受信任,但不适用于 Android

openssl - 如何编辑使用openssl xampp创建的自签名证书?

multithreading - 消息如何在多个 WndProcs 之间序列化?

wordpress - IIS 和 Wordpress 反向代理的 HTTPS (SSL) 问题