我正在将 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/