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
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
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
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
xsotTrustUnavailableefter 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 xsrvRequireCachedanvänder cache-enbart spärrningsdata; saknad status avvisar, ochxsrvNotCheckedger 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