Teknisk artikel

Verifiera och signera om digitala Excel-signaturer i Delphi

HotXLS verifierar och signerar om digitala Excel-signaturer i Delphi utan att Excel är installerat. Uniten lxSignatureOpcIntegrity kontrollerar ECMA-376 Part 2-paketsignaturer direkt från arbetsbokens byte: RSA-SHA256 över den kanoniska SignedInfo, varje SHA-256-Reference-digest, och exakt de delar varje signatur väljer ut. Certifikatkedjeförtroende är ett separat, explicit steg, och redigering eller omsignering publicerar bara utdata som verifierar igen

Den vanliga utlösaren är ett dokumentarbetsflöde som vuxit ur skrivbordet. En controller signerar en budgetarbetsbok i Excel, en Delphi-tjänst måste senare bekräfta signaturen innan den importerar talen, och ett tredje steg behöver rätta ett blad och signera igen under ett tjänstecertifikat. Att automatisera Excel på en server för det är skört och stöds inte, och att behandla signaturen som en ja/nej-flagga gömmer de två frågor som faktiskt spelar roll: vilka byte åtog sig signeraren sig att, och litar du på personen som håller den nyckeln

Vad täcker en OPC-paketsignatur egentligen?

En OPC-paketsignatur täcker bara de delar dess Manifest listar, inte hela XLSX-filen. ECMA-376 Part 2, §13 (Digital Signatures) placerar en signaturursprungsdel i paketet, typiskt /_xmlsignatures/origin.sigs, med relationer till en eller flera signaturdelar som /_xmlsignatures/sig1.xml. Varje signaturdel är en W3C XML-DSig Signature vars SignedInfo refererar till en intern idPackageObject, och det Object:et bär ett Manifest av References till enskilda paketdelar, var och en med sin egen digest

HotXLS stödjer tre sätt en Reference kan binda en del, och de är inte utbytbara. En rå Reference hasherar de lagrade delbytena. En kanonisk XML-Reference hasherar Canonical XML-formen (utan kommentarer), så attributordning och namnrymdsdeklarationernas stavning spelar ingen roll längre. En relations-Reference tillämpar först OPC RelationshipTransform och kanoniserar sedan, och den transformen behåller bara relationerna som matchas av dess SourceId- eller SourceType-selektorer. En selektor autentiserar exakt de relationer den väljer: resten av den .rels-delen kan ändras fritt medan signaturen förblir giltig

Anatomin hos OPC digital signatur i HotXLS för Delphi: en origin.sigs-del leder till XML-DSig-signaturdelar vars Manifest binder varje paketdel med en rå, kanonisk XML- eller RelationshipTransform-referens
Tre Reference-lägen är inte utbytbara: rå hasherar lagrade byte, kanonisk XML ignorerar attributstavning, och relationsselektorer lämnar de oselekterade .rels-posterna fria att ändras

Den sista punkten är varför HotXLS rapporterar täckning i stället för en enda grön bock. TXLSSignatureOpcIntegrityResult.Coverage är en array av TXLSSignatureOpcCoverage-poster, en per autentiserad Manifest-Reference, var och en med ReferenceUri, PartName, ContentType, Transforms, RelationshipSourceIds och RelationshipSourceTypes. Läser din import sheet3.xml och ingen beskrivare namnger den säger signaturen ingenting om de tal du är på väg att läsa in

Att verifiera signaturer från en oföränderlig ZIP-ögonblicksbild

HotXLS verifierar ett paket från en enda oföränderlig byteögonblicksbild, aldrig från filen på disk eller en live TWorkbook. XlsVerifySignatureOpcPackage och dess multi-signatur-syskon tar en TBytes-array, läser bara den arrayen och hämtar aldrig externa mål, certifikat, lokala filer eller nätverksresurser. Poängen är att de byte du verifierat är de byte du parsar efteråt: läses filen in igen senare kan en annan process ha ersatt den emellan

Kanonisering är där hemgjorda verifierare oftast går fel. SignedInfo och paket-Object:et måste kanoniseras i namnrymdskontexten hos det kompletta Signature-kuvertet. Ett isolerat fragment klippt ur dokumentet ärver andra namnrymdsdeklarationer än vad verifieraren ser på plats, så dess C14N-byte skiljer sig, digesten skiljer sig, och en fullt giltig signatur misslyckas. HotXLS använder full kuvertkontext på både verifierings- och utgivningssidan

För paket som kan bära mer än en signatur verifierar XlsVerifyAllSignaturesOpcPackage varje bifogad signatur och behåller de individuella utfallen. Aggregatstatusen är xsoiIntegrityVerified bara när varje godkänd signatur verifierar; annars är det det första felet i inventeringsordning, och varje rapport talar fortfarande om vilken signaturdel som misslyckades

uses
  System.SysUtils, System.IOUtils, lxSignatureOpcIntegrity;

function AllSignaturesIntact(const FileName: string): Boolean;
var
  Bytes: TBytes;
  Reports: TXLSSignatureOpcVerifications;
  Status: TXLSSignatureOpcIntegrityStatus;
  I, J: Integer;
begin
  Bytes := TFile.ReadAllBytes(FileName);  // en ögonblicksbild, verifierad och parsad
  Status := XlsVerifyAllSignaturesOpcPackage(Bytes, Reports);
  for I := 0 to High(Reports) do
  begin
    Writeln(String(Reports[I].SignaturePart), ': ', Ord(Reports[I].Status));
    if Reports[I].Status = xsoiIntegrityVerified then
      for J := 0 to High(Reports[I].Integrity.Coverage) do
        Writeln('  covers ', String(Reports[I].Integrity.Coverage[J].PartName));
  end;
  Result := Status = xsoiIntegrityVerified;  // xsoiUnsigned är inte ett godkänt
end;

Statusvärdena är tillräckligt specifika för att agera på. xsoiDigestMismatch betyder att en vald del ändrats efter signeringen, xsoiInvalidSignature betyder att RSA-värdet inte verifierar mot SignedInfo med det inbäddade certifikatets nyckel, xsoiPartUnavailable betyder att en vald del saknas, och xsoiUnsupportedProfile eller xsoiResourceLimit betyder att verifieraren vägrade i stället för att gissa. ZIP-lagret under tillämpar samma fail-closed-läsning som beskrivs i artikeln om ZIP EOCD-validering för obetrodda XLSX-filer, och deluppslagning följer reglerna i XLSX OPC-relationsupplösning

Varför gör redigering av en signerad arbetsbok signaturen ogiltig?

Redigering av en signerad arbetsbok gör signaturen ogiltig så snart en ändrad byte hamnar inne i ett signerat urval, eftersom den lagrade SHA-256-digesten för den Reference:n inte längre matchar. En kalkylarksskrivare som läser in arbetsboken och sparar den igen rör nästan alltid signerade delar: den serialiserar om XML, numrerar om delade strängar, skriver om workbook.xml och innehållstypmanifestet. Även när cellvärdena är identiska är byten det inte, och nästa verifiering returnerar xsoiDigestMismatch

HotXLS låter dig ställa frågan innan du binder dig vid en redigering. XlsEditVerifiedOpcPackage ersätter befintliga delar i ett verifierat paket och verifierar sedan kandidaten. Med xsoePreserve returnerar den xsoePreserved bara när signaturen fortfarande verifierar, vilket händer när de redigerade delarna ligger utanför varenda signerad urval, och xsoeWouldInvalidate annars. Med xsoeInvalidateChanged tar den bort signaturbindningen från rotrelationerna och [Content_Types].xml och returnerar ett explicit osignerat paket som xsoeInvalidated, i stället för att ge dig en fil som fortfarande ser signerad ut

function EditKeepsSignature(const Signed, NewSheetXml: TBytes;
  out Edited: TBytes): Boolean;
var
  Edits: TXLSSignatureOpcEdits;
  Details: TXLSSignatureOpcEditResult;
begin
  SetLength(Edits, 1);
  Edits[0].PartName := '/xl/worksheets/sheet2.xml';
  Edits[0].Data := NewSheetXml;
  case XlsEditVerifiedOpcPackage(Signed, Edits, xsoePreserve, Edited, Details) of
    xsoePreserved:       Result := True;   // sheet2 låg utanför det signerade urvalet
    xsoeWouldInvalidate: Result := False;  // en signerad digest täcker sheet2
  else
    raise Exception.CreateFmt('Edit refused, original integrity %d',
      [Ord(Details.OriginalIntegrity)]);
  end;
end;

Integritet kontra förtroende: vad bevisar en verifierad signatur?

En verifierad signatur bevisar att de valda delarna är oförändrade sedan någon med den privata nyckeln signerade dem, och ingenting om vem den någon är. HotXLS håller de två lagren isär med flit: varje TXLSSignatureOpcIntegrityResult bär Trust = xsstUntrusted, inklusive lyckade kontroller, och ClaimedSignatureTime är signerarlevererad data, inte en betrodd tidsstämpel. Vem som helst kan generera ett självsignerat certifikat, signera en arbetsbok och passera integritetskontrollen

HotXLS verifierar digitala signaturer i Excel OPC-paket i Delphi i två lager: integritetsdigestkontroller över en oföränderlig byteögonblicksbild som alltid rapporterar xsstUntrusted, sedan XlsVerifySignatureOpcPackageWithTrust med en anroparägd policy
En verifierad signatur bevisar att de valda delarna är oförändrade, medan förtroende kräver dina egna ankare, en UTC-verifieringstid och document-signing-EKU:n innan xsotTrustedSelection betyder något

Förtroende är ett separat anrop med en anroparägd policy. XlsDefaultSignatureOpcTrustPolicy returnerar en fail-closed-policy utan ankare, utan verifieringstid, med document-signing-EKU:n 1.3.6.1.4.1.311.10.3.12 och xsrvRequireCached-spärrning. XlsVerifySignatureOpcPackageWithTrust kör först den kompletta integritetsverifieraren och utvärderar sedan certifikatet som faktiskt passerade den; en anropare kan inte byta in ett annat lövcertifikat

uses
  System.SysUtils, System.DateUtils, lxSignatureOpcIntegrity;

function SignedUnderCorporateCA(const Bytes, RootDer, IssuingCaDer: TBytes): Boolean;
var
  Policy: TXLSSignatureOpcTrustPolicy;
  Trust: TXLSSignatureOpcTrustResult;
begin
  Policy := XlsDefaultSignatureOpcTrustPolicy;
  SetLength(Policy.TrustAnchors, 1);
  Policy.TrustAnchors[0] := RootDer;
  SetLength(Policy.Intermediates, 1);
  Policy.Intermediates[0] := IssuingCaDer;
  Policy.VerificationTimeUtc := TTimeZone.Local.ToUniversalTime(Now);

  Result := XlsVerifySignatureOpcPackageWithTrust(Bytes, Policy, Trust)
    = xsotTrustedSelection;
  if not Result then
    Writeln(Format('trust %d, integrity %d, policy error %.8x, chain errors %.8x',
      [Ord(Trust.Status), Ord(Trust.IntegrityStatus),
       Trust.NativePolicyError, Trust.NativeChainErrors]));
end;

Windows-backend har två fällor som är lätta att missa när du anropar WinCrypt själv. Att CertVerifyCertificateChainPolicy returnerar True betyder bara att anropet exekverade; acceptans kräver fortfarande en nolla i dwError i policystatusen, noll aggregerade kedjefel, en explicit digitalSignature-nyckelanvändning på lövet och den krävda EKU:n. HotXLS fann att ett certifikat som saknar nyckelanvändning kan passera den vanliga baspolicyn, så den kontrollerar DigitalSignatureUsage och RequiredEkuPresent själv. Den andra fällan: att kombinera en exklusiv rotbutik med hRestrictedOther i kedjemotorkonfigurationen returnerar E_INVALIDARG på aktuell backend, så HotXLS använder den exklusiva anroparrotbutiken och auktoriserar sedan varenda element i den returnerade primärkedjan mot de DER-byte du levererade. En utfärdare som bara råkar sitta i en lokal systembutik blir aldrig betrodd

xsotTrustedSelection beskriver fortfarande bara den signaturen och dess rapporterade täckning. Osignerade celler och oselekterade relationer kan ändras medan den valda signaturen förblir betrodd, vilket är varför förtroenderesultatet också returnerar PackageSha256 och CertificateSha256 för din revisionsspårning

Hur signerar eller mot-signerar man en arbetsbok utan Excel?

På byte-array-nivå signerar HotXLS om genom explicita, auktoriserade transaktioner, och arbetsbokens signerade-spar-vägar återanvänder samma verifierade pipelines. XlsReplaceVerifiedSignatureOpcPackage byter en verifierad signatur mot en ny, XlsEditAndReplaceSelectedSignatureOpcPackage applicerar delredigeringar och ersätter en vald signatur i samma transaktion, och XlsAppendVerifiedSignatureOpcPackage lägger till en signatur medan den behåller varje verifierad originalbyte för byte. Ersättningsanropen tar en TXLSSignatureOpcReplaceAuthorization, tilläggsanropet verifierar varje original först, och alla tar en TXLSSignatureOpcIssueOptions-urval och en signerar-callback; biblioteket rör aldrig din privata nyckel

Auktorisationsposten binder operationen till exakt det original du godkänt: ExpectedPackageSha256, ExpectedSignaturePart och ExpectedCertificateSha256, alla jämförda först efter att den gamla signaturen fullständigt verifierat. Varje redigerad del måste täckas av det nya urvalet, varje oselekterad signatur måste fortfarande verifiera mot det mellanlagrade paketet innan signeraren anropas, och anropet returnerar xspsWouldInvalidate annars

HotXLS signerar om ett Excel XLSX-paket i Delphi via XlsEditAndReplaceSelectedSignatureOpcPackage, som verifierar den gamla signaturen, kontrollerar förväntade paket-, del- och certifikathash och anropar en signerar-callback
Auktorisationsposten binder ersättningen till det godkända originalet, oselekterade signaturer måste fortfarande verifiera på det mellanlagrade paketet, och den privata nyckeln stannar i din CNG, HSM eller dina smartkort
uses
  System.SysUtils, System.DateUtils, lxHash, lxSignatureOpcIntegrity;

type
  TWorkbookResigner = class
  private
    function SignDigest(const Request: TXLSSignatureOpcSelectedReplaceRequest;
      var Signature: TBytes): TXLSSignatureOpcSignerStatus;
  public
    function Resign(const Approved, NewSheetXml, NewCertDer: TBytes;
      const OldCertSha256: WideString; var Output: TBytes): Boolean;
  end;

function TWorkbookResigner.SignDigest(
  const Request: TXLSSignatureOpcSelectedReplaceRequest;
  var Signature: TBytes): TXLSSignatureOpcSignerStatus;
begin
  // Request.NewSignature.Digest är den 32-byte SHA-256 biblioteket beräknade;
  // RsaSignSha256Pkcs1 står för din egen CNG-, HSM- eller smartkortswrapper och
  // måste returnera hela den big-endian RSASSA-PKCS1-v1_5-signaturen
  if RsaSignSha256Pkcs1(Request.NewSignature.Digest, Signature) then
    Result := xsosSigned
  else
    Result := xsosFailed;
end;

function TWorkbookResigner.Resign(const Approved, NewSheetXml, NewCertDer: TBytes;
  const OldCertSha256: WideString; var Output: TBytes): Boolean;
var
  Auth: TXLSSignatureOpcReplaceAuthorization;
  Options: TXLSSignatureOpcIssueOptions;
  Edits: TXLSSignatureOpcEdits;
  Details: TXLSSignatureOpcSelectedReplaceResult;
begin
  Auth.ExpectedPackageSha256 := XLSComputeSHA256Hex(Approved); // eller hashen lagrad vid godkännandet
  Auth.ExpectedSignaturePart := '/_xmlsignatures/sig1.xml';
  Auth.ExpectedCertificateSha256 := OldCertSha256;

  SetLength(Edits, 1);
  Edits[0].PartName := '/xl/worksheets/sheet1.xml';
  Edits[0].Data := NewSheetXml;

  SetLength(Options.References, 2);
  Options.References[0].PartName := '/xl/workbook.xml';
  Options.References[0].Mode := xsimCanonicalXml;
  Options.References[1].PartName := '/xl/worksheets/sheet1.xml';
  Options.References[1].Mode := xsimCanonicalXml;
  Options.ClaimedSignatureTimeUtc := FormatDateTime('yyyy"-"mm"-"dd"T"hh":"nn":"ss"Z"',
    TTimeZone.Local.ToUniversalTime(Now));

  Result := XlsEditAndReplaceSelectedSignatureOpcPackage(Approved, NewCertDer,
    Auth, Options, Edits, SignDigest, Output, Details) = xspsReplaced;
end;

Två detaljer i den koden är medvetna. Output och Details måste vara separata variabler; att skicka ett fält hos Details som Output är explicit ostödt, eftersom biblioteket inte kan publicera ett paket och hålla en certifikatrapport i samma lagring. Och den gamla täckningen ärvs inte: den nya signaturen autentiserar exakt de References du listar, så bestäm igen vilka delar en nedströmskonsument kommer att lita på

Motsignering använder samma tilläggsanrop. Sedan HotXLS 2.384.296 får TXLSSignatureOpcIssueOptions.References som skickas till XlsAppendVerifiedSignatureOpcPackage namnge delar från det verifierade originalsignaturens inventarie, så en granskare kan signera över en befintlig signaturdel, kedja flera motsignaturer, eller blanda signaturdelar och innehållsdelar i ett urval. Varje tillagd signatur registrerar bara sina direkta urval. Kvalificeringen för den releasen körde 222 gränskontroller och 122 oberoende kontroller med .NET:s WindowsBase-paketeringsverifierare över 7 utdatapaket, som bekräftade exakt en ny signatur och byte-identiska originalsignaturer och certifikat

Fallgropar och gränser värda att känna till

Det mest lärorika felet kom från ett tecken som är fullt lagligt. OPC Reference-URI:er bär innehållstypen som en fråga, till exempel /xl/worksheets/sheet1.xml?ContentType=application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml. En tidig HotXLS-utfärdare procentkoderade den bokstavliga +-tecknet som %2B. HotXLS:s egna RSA-, C14N- och Reference-kontroller accepterade resultatet, men WindowsBase rapporterade InvalidSignature; ett kontrollerat prov som ändrade bara den stavningen gick från InvalidSignature till Success. Utfärdaren behåller nu stödd bokstavlig ASCII-frågepunktering medan den fortfarande eskaperar XML-attribut, och lärdomen för varje signerare är att testa mot en oberoende nativ verifierare, inte bara din egen

  • Förtroende-backend är endast Windows; andra mål returnerar xsotTrustUnavailable efter att integriteten lyckats
  • Verifieraren accepterar paket upp till 64 MiB, 16 MiB per del, och högst 32 signaturdelar under ett ursprung
  • Utgivning accepterar upp till 256 References och 4096 kombinerade selektorer per signatur
  • Den stödda profilen är RSA-SHA256, SHA-256-digests och Canonical XML utan kommentarer; andra algoritmer, extra program-Object:er och externa References returnerar xsoiUnsupportedProfile
  • xsrvRequireCached använder cache-enbart spärrningsdata; saknad status avvisar, och xsrvNotChecked ger ingen spärrningshållning
  • Att signera en arbetsbok som Excel också måste acceptera stöter fortfarande på paketreglerna i varför Excel reparerar en giltig XLSX; en duplicerad Override eller relations-Id bryter filen innan någon signatur kontrolleras

FAQ

Betyder en HotXLS-integritetskontroll att signeraren är betrodd?

Nej. XlsVerifySignatureOpcPackage bevisar att de valda delarna matchar sina digests under det inbäddade certifikatets nyckel och rapporterar alltid xsstUntrusted. Använd XlsVerifySignatureOpcPackageWithTrust med dina egna ankare och en UTC-verifieringstid för att utvärdera certifikatkedjan

Varför visar Excel min arbetsboksignatur som ogiltig efter en Delphi-redigering?

En sparad arbetsbok skriver om signerade XML-delar, så SHA-256-digesten för minst en Reference ändras. Använd XlsEditVerifiedOpcPackage med xsoePreserve för att testa en redigering först, eller XlsEditAndReplaceSelectedSignatureOpcPackage för att redigera och signera om i en verifierad transaktion

Kan HotXLS signera med en nyckel lagrad på ett smartkort eller en HSM?

Ja, eftersom signering är en anropar-callback. Biblioteket skickar den kanoniska SignedInfo och dess 32-byte SHA-256-digest, din kod returnerar RSASSA-PKCS1-v1_5-signaturen, och den privata nyckeln lämnar aldrig providern du styr

OPC-signaturverifiering, förtroendeutvärdering, motsignering och verifierad omsignering är del av HotXLS, den nativa Excel-kalkylarkskomponenten för Delphi och C++Builder, som läser och skriver XLS, XLSX, XLSB och ODS utan Excel eller COM-automation