Pergunta

Question related to Lazarus or Delphi. Is there a way to programmatically access trusted Root Certificate Authorities certificates in Windows. I know there is GUI based tool in Windows called 'mmc.exe', but I need to access certificate files (like .crt or .cer or .pem etc.) using Object Pascal syntax. Can anyone help me with that?

Foi útil?

Solução

As alternatives, there's CAPICOM which you can simply import as ActiveX type library, but there's also the plain old Windows Cryptography API.

As an example, here's a very old test project of mine (I haven't tried it recently). You're going to need the WinCrypt or JwaWinCrypt unit with the relevant API translations which should be available on JEDI:

program lstore;

{$APPTYPE CONSOLE}

uses
  Windows,
  SysUtils,
  WinCrypt;

var
  StoreName: array[0..255] of Char;
  hStore: HCERTSTORE;
  CertContext: PCertContext;
  CertPropId: DWORD;
  Data: array[0..511] of Char;
  DataLen: DWORD;
  i: Integer;

procedure DisplayCertContext(Cert: PCertContext);
var
  CertName: array[0..255] of Char;
begin
  if CertGetNameString(CertContext, CERT_NAME_EMAIL_TYPE, 0, nil,
    CertName, 256) = 0 then
    RaiseLastWin32Error;
  Writeln('Subject CERT_NAME_EMAIL_TYPE: ', CertName);
  if CertGetNameString(CertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nil,
    CertName, 256) = 0 then
    RaiseLastWin32Error;
  Writeln('Subject CERT_NAME_SIMPLE_DISPLAY_TYPE: ', CertName);
  if CertGetNameString(CertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, nil,
    CertName, 256) = 0 then
    RaiseLastWin32Error;
  Writeln('Subject CERT_NAME_FRIENDLY_DISPLAY_TYPE: ', CertName);

  if CertGetNameString(CertContext, CERT_NAME_EMAIL_TYPE, CERT_NAME_ISSUER_FLAG, nil,
    CertName, 256) = 0 then
    RaiseLastWin32Error;
  Writeln('Issuer CERT_NAME_EMAIL_TYPE: ', CertName);
  if CertGetNameString(CertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, nil,
    CertName, 256) = 0 then
    RaiseLastWin32Error;
  Writeln('Issuer CERT_NAME_SIMPLE_DISPLAY_TYPE: ', CertName);
  if CertGetNameString(CertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, nil,
    CertName, 256) = 0 then
    RaiseLastWin32Error;
  Writeln('Issuer CERT_NAME_FRIENDLY_DISPLAY_TYPE: ', CertName);
end;

begin
  try
    Write('Enter name of store to be listed: ');
    Readln(StoreName);
    hStore := CertOpenSystemStore(0, StoreName);
    if hStore = nil then
      RaiseLastWin32Error;
    try
      CertContext := CertEnumCertificatesInStore(hStore, nil);
      while CertContext <> nil do
      begin
        DisplayCertContext(CertContext);
        CertPropId := CertEnumCertificateContextProperties(CertContext, 0);
        while CertPropId <> 0 do
        begin
          DataLen := 512;
  //        Writeln(Format('CertPropId: %d', [CertPropId]));
          case CertPropId of
          CERT_KEY_PROV_HANDLE_PROP_ID:
          begin
            CertGetCertificateContextProperty(CertContext, CertPropId,
              @Data[0], DataLen);
            Writeln(Format('KEY_PROV_HANDLE: $%.8x', [PDWORD(@Data[0])^]));
          end;
          CERT_KEY_PROV_INFO_PROP_ID:
          begin
            CertGetCertificateContextProperty(CertContext, CertPropId,
              @Data[0], DataLen);
            with PCryptKeyProvInfo(@Data[0])^ do
            begin
              Writeln(Format('pwszContainerName = %s', [pwszContainerName]));
              Writeln(Format('pwszProvName = %s', [pwszProvName]));
              Writeln(Format('dwFlags = %d', [dwFlags]));
              Writeln(Format('cProvParams = %d', [cProvParams]));
              //Writeln(Format('rgProvParam', [rgProvParam]));
              Writeln(Format('dwKeySpec = %d', [dwKeySpec]));
            end;
            Writeln(Format('KEY_PROV_INFO: %d', [@Data[0]]));
          end;
          CERT_FRIENDLY_NAME_PROP_ID:
          begin
            CertGetCertificateContextProperty(CertContext, CertPropId,
              @Data[0], DataLen);
            Writeln(Format('FRIENDLY_NAME: %s', [PChar(@Data[0])]));
          end;
          CERT_KEY_IDENTIFIER_PROP_ID:
          begin
            CertGetCertificateContextProperty(CertContext, CertPropId,
              @Data[0], DataLen);
            Write('KEY_IDENTIFIER: ');
            for i := 1 to DataLen do
              Write(Format('%.2x ', [PBYTE(@Data[i - 1])^]));
            Writeln;
          end;
          CERT_SHA1_HASH_PROP_ID:
          begin
            CertGetCertificateContextProperty(CertContext, CertPropId,
              @Data[0], DataLen);
            Write('SHA1_HASH: ');
            for i := 1 to DataLen do
              Write(Format('%.2x ', [PBYTE(@Data[i - 1])^]));
            Writeln;
          end;
          CERT_MD5_HASH_PROP_ID:
          begin
            CertGetCertificateContextProperty(CertContext, CertPropId,
              @Data[0], DataLen);
            Write('MD5_HASH: ');
            for i := 1 to DataLen do
              Write(Format('%.2x ', [PBYTE(@Data[i - 1])^]));
            Writeln;
          end;
          else
          end;
          CertPropId := CertEnumCertificateContextProperties(CertContext,
            CertPropId);
        end;
        CertContext := CertEnumCertificatesInStore(hStore, CertContext);
      end;
//      if GetLastError <> CRYPT_E_NOT_FOUND then
//        RaiseLastWin32Error;
    finally
      CertCloseStore(hStore, 0);
    end;
  except
    on E: Exception do
    begin
      ExitCode := 1;
      Writeln(Format('[%s] %s', [E.ClassName, E.Message]));
    end;
  end;
end.

Outras dicas

You can do just about anything with SecureBlackBox by EldoS. http://www.eldos.com/sbb/

This software makes it seem easy. It's not easy.

I recommend Eldos SecureBlackBox, but there is a free alternative.

You can also use OpenSSL libraries. You'll need a full OpenSSL port, as the Indy OpenSSL headers are incomplete. You'll also need to compile a database of public certificates from the certificate authorities since it's not included.

With OpenSSL, you won't have access to the Windows certificate store directly. You can export the certificates from Windows or Firefox and import them.

Eldos SecureBlackBox provides access to the Windows certificate store, including methods to verify the code signing signature of a program.

You can include OpenSSL binaries with your Windows product, and OpenSSL is already installed and available on Mac OS. So, OpenSSL is viable for FireMonkey or Lazarus.

OpenSSL is fairly well documented and you can find a lot of examples online, though mostly in C or C++. I've had good luck implementing anything I needed with it. It has a lot of functions for reading and writing to PEM files.

SecureBlackBox does not yet have a FireMonkey version, but they say they will have one next year (per their forum). They do have an fpc (free pascal) version.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top