delphicertificatecryptoapi

How to export certificate from Windows certificate store via CryptoAPI as Base64 string


i've got following C# code for doing what i asked for in subject:

public static void ExportCertificatesToFile(string FileName)
{
    stringBuilder builder = new StringBuilder();

    X509Store storeMy = new X509Store(StoreName.My);
    storeMy.Open(OpenFlags.ReadOnly);

    foreach (X509Certificate2 cert in storeMy.Certificates)
    {
        builder.AppendLine("-----BEGIN CERTIFICATE-----");   
              builder.AppendLine(Convert.ToBase64String(cert.Export(X509ContentType.Cert),  Base64FormattingOptions.InsertLineBreaks));
        builder.AppendLine("-----END CERTIFICATE-----");
    }

    storeMy.Close();

    File.WriteAllText(FileName, builder.ToString());
}

Exactly that i want to archieve with Delphi using CryptoAPI (JwaWinCrypt.pas) I've tried following code:

procedure TForm1.Button1Click(Sender: TObject);
var
  hStore: HCERTSTORE;
  CertContext: PCertContext;
  pszString: PAnsiChar;
  pchString: Cardinal;
begin
  hStore := CertOpenSystemStore(0, PChar('MY'));

  try
    CertContext := CertEnumCertificatesInStore(hStore, nil);
    while CertContext <> nil do
    begin
      pszString := '';
      pchString := 0;
      CryptBinaryToString(CertContext.pbCertEncoded, CertContext.cbCertEncoded,  CRYPT_STRING_BASE64, pszString, pchString);

      ShowMessage(StrPas(pszString));

      CertContext := CertEnumCertificatesInStore(hStore, CertContext);
    end;
  finally
    CertCloseStore(hStore, 0);
  end;
end;

Problem is that ShowMessage shows nothing, the string is empty. Has someone an idea what i do wrong?


Solution

  • The documentation for CryptBinaryToString says this about the pszString parameter.

    A pointer to a buffer that receives the converted string. To calculate the number of characters that must be allocated to hold the returned string, set this parameter to NULL. The function will place the required number of characters, including the terminating NULL character, in the value pointed to by pcchString.

    You are obliged to allocate the buffer so that the API function can populate it. You are failing to do so. In order to proceed you must read the documentation carefully and abide by the requirements of the API.

    So you need to call the function like this:

    szString: AnsiString;
    ....
    chString := 0;
    CryptBinaryToString(CertContext.pbCertEncoded, CertContext.cbCertEncoded,  
        CRYPT_STRING_BASE64, nil, chString);
    SetLength(szString, chString-1);
    CryptBinaryToString(CertContext.pbCertEncoded, CertContext.cbCertEncoded,  
        CRYPT_STRING_BASE64, PAnsiChar(szString), chString);
    

    You should also check the return value of CryptBinaryToString to detect failures. I omitted that for brevity.

    I'm also assuming that yours is an ANSI Delphi. I assumed that because you used PAnsiChar.