#1 Re: mORMot 2 » Abstract Error using TCryptCert.LoadFromFile or Load (PFX and WindowS » 2025-04-15 18:21:53

Hi

Thank you for your reply, unfortunately I don't have enough knowledge in mormot, I'm doing my best to understand everything. I'm reading the documentation and looking at examples, but due to the tight time, everything is still very confusing. What I've managed so far is:

 case FonteCert of
    csPFX:
      begin
        Certificado := Cert('x509-rs256').Instance;
        if not(Certificado.LoadFromFile(CertOrigem, cccCertOnly, CertSenha)) or not(Assigned(Certificado)) then
        begin
          ShowMessage('Erro ao carregar certificado PFX: ' + '');
          Exit;
        end;

        Assinatura := Certificado.Sign(@SHA, SizeOf(SHA), cuDigitalSignature);

        if Assinatura = '' then
        begin
          ShowMessage('Erro ao gerar assinatura.');
          Exit;
        end;

      end;

    csWindowsStore:
      begin
        Armazem := Store('x509-store');
        if Armazem = nil then
        begin
          ShowMessage('Erro ao obter a store de certificados');
          Exit;
        end;
        Certificado := Armazem.GetBySerial(CertOrigem); // Espera serial como hex string
        if not Assigned(Certificado) then
        begin
          ShowMessage('Erro ao localizar certificado pela serial na store');
          Exit;
        end;
      end;
  end;

But I still can't open the WindowsStore or sign the document, I didn't see any examples of this in the documentation, the automated tests I found in the test folder don't have this.

#2 mORMot 2 » Abstract Error using TCryptCert.LoadFromFile or Load (PFX and WindowS » 2025-04-13 11:43:47

Linces
Replies: 3

Hello,

I'm currently working on a complete PAdES signature implementation using mORMot 2 and OpenSSL, including support for visible digital signatures on PDFs, multiple sequential signatures (PAdES).

Certificates from PFX and Windows Store

But I'm facing a critical problem I couldn't solve or debug further:

No matter if I try to load the certificate from a .pfx file or from the Windows Certificate Store, I receive a runtime Abstract Error at the moment I call:

Cert := TCryptCert.Create;

if not Cert.LoadFromFile(CertOrigem, cccCertWithPrivateKey, CertSenha) then
  Exit;

or when using the Windows Store PEM:

tmpPEM := GetOneSystemStoreAsPem(scsMY);
if not Cert.Load(tmpPEM, cccCertWithPrivateKey, '') then
  Exit;

In both cases, I receive no stack trace, no explanation — just "Abstract Error".

Here’s the "almost" code:

type
  TCertSource = (csPFX, csWindowsStore);

function AssinarPDF_PAdES_Completo(
  const ArquivoOrigem, ArquivoDestino: string;
  const CertOrigem, CertSenha: string;
  const Razao, Local, Contato, TSA_URL: string;
  const PosX, PosY, Largura, Altura: Integer;
  FonteCert: TCertSource
): Boolean;

implementation

function GerarByteRangePlaceholder(const TamanhoPDF, AssinaturaMax: Integer): RawUTF8;
begin
  Result := FormatUTF8('[0 % 0 %]', [100000, 100000 + AssinaturaMax]);
end;

function CriarPlaceholderAssinatura(AssinaturaMax: Integer): RawUTF8;
begin
  SetLength(Result, AssinaturaMax * 2);
  FillChar(Pointer(Result)^, Length(Result), Ord('0'));
end;


// Função auxiliar para contar ocorrências em RawByteString
function AnsiStringReplaceCount(const Source: RawByteString; const Pattern: RawUtf8): Integer;
var
  P, Start: PAnsiChar;
  PatLen: Integer;
begin
  Result := 0;
  if (Source = '') or (Pattern = '') then Exit;
  PatLen := Length(Pattern);
  Start := Pointer(Source);
  P := AnsiStrPos(Start, Pointer(Pattern));
  while P <> nil do
  begin
    Inc(Result);
    Start := P + PatLen;
    P := AnsiStrPos(Start, Pointer(Pattern));
  end;
end;


function AssinarPDF_PAdES_Completo(
  const ArquivoOrigem, ArquivoDestino: string;
  const CertOrigem, CertSenha: string;
  const Razao, Local, Contato, TSA_URL: string;
  const PosX, PosY, Largura, Altura: Integer;
  FonteCert: TCertSource
): Boolean;
var
  OriginalPDF, FinalPDF: RawByteString;
  ByteRangeStr, PlaceholderAssinatura: RawUtf8;
  AssinaturaMax, PosicaoAssinatura: Integer;
  Cert: TCryptCert;
  Assinatura: RawByteString;
  SHA: TSHA256Digest;
  StreamOut: TFileStream;
  AssinaturaHex: RawUTF8;
  SigCount: Integer;
  tmpPEM: RawUtf8;
begin
  Result := False;

  RegisterOpenSsl;
  RegisterX509;


  if not FileExists(ArquivoOrigem) then
    Exit;

  OriginalPDF := StringFromFile(ArquivoOrigem);
  AssinaturaMax := 8192;

  ByteRangeStr := GerarByteRangePlaceholder(Length(OriginalPDF), AssinaturaMax);
  PlaceholderAssinatura := CriarPlaceholderAssinatura(AssinaturaMax);

  FinalPDF := OriginalPDF +
    Format(#13#10 +
           '1 0 obj << /Type /Sig /Filter /Adobe.PPKLite /SubFilter /adbe.pkcs7.detached ' +
           '/ByteRange % /Contents <%> /Reason (%)/M (D:%s)/ContactInfo (%)/Location (%) ' +
           '/Rect [%d %d %d %d] >> endobj'#13#10 + '%%EOF',
           [ByteRangeStr, PlaceholderAssinatura, Razao,
            FormatDateTime('yyyymmddhhmmss', Now), Contato, Local,
            PosX, PosY, PosX + Largura, PosY + Altura]);

  SHA := SHA256Digest(FinalPDF);

  Cert := TCryptCert.Create;
  try
    case FonteCert of
      csPFX:
        begin
          if not Cert.LoadFromFile(CertOrigem, cccCertOnly, CertSenha) then
          begin
            ShowMessage('Erro ao carregar o certificado PFX com senha');
            Exit;
          end;
        end;
      csWindowsStore:
        begin
          tmpPEM := GetOneSystemStoreAsPem(scsMY);
          if tmpPEM = '' then
          begin
            ShowMessage('Não foi possível obter certificados da Windows Store');
            Exit;
          end;
          if not Cert.Load(tmpPEM, cccCertWithPrivateKey, '') then
          begin
            ShowMessage('Erro ao carregar certificado da store');
            Exit;
          end;
        end;
    end;

    // Se houver TSA, aplicar carimbo de tempo (exemplo básico)
    if TSA_URL <> '' then
      // Cert.TsaUrl := TSA_URL; // Não implementado em mORMot2

    Assinatura := Cert.Sign(@SHA, SizeOf(SHA), cuDigitalSignature);
    if Assinatura = '' then
    begin
      ShowMessage('Erro ao gerar assinatura.');
      Exit;
    end;

    AssinaturaHex := BinToHex(Pointer(Assinatura), Length(Assinatura));
    PosicaoAssinatura := Pos('<' + PlaceholderAssinatura + '>', UTF8ToString(FinalPDF));
    if PosicaoAssinatura <= 0 then Exit;

    Delete(FinalPDF, PosicaoAssinatura + 1, Length(PlaceholderAssinatura));
    Insert(AssinaturaHex, FinalPDF, PosicaoAssinatura + 1);

    StreamOut := TFileStream.Create(ArquivoDestino, fmCreate);
    try
      StreamOut.WriteBuffer(Pointer(FinalPDF)^, Length(FinalPDF));
      Result := True;
    finally
      StreamOut.Free;
    end;

    // Contar quantas assinaturas já existem (assina em sequência)
    SigCount := 0;
    SigCount := AnsiStringReplaceCount(OriginalPDF, '/SubFilter /adbe.pkcs7.detached');
    Inc(SigCount);
    ShowMessage(Format('Assinatura #%d aplicada com sucesso: %s', [SigCount, ArquivoDestino]));
  finally
    Cert.Free;
  end;
end;

What is the proper way to instantiate a concrete implementation of TCryptCert when I want to:

Load .pfx with private key

Or load from PEM (from Windows Store or external file)

Am I supposed to call some factory or registration function explicitly before calling TCryptCert.Create?

Any insight or sample would be appreciated.

* Environment:
mORMot 2 (latest GitHub build)
Delphi 11.3
Windows 11 x64
Cert tested: standard ICP-Brasil .pfx (with password), and one from Windows Store (Personal store)


Thank you for your amazing framework and dedication!

Cheers

Board footer

Powered by FluxBB