HotXLS verificerer og re-signerer Excel digitale signaturer i Delphi uden Excel installeret. Uniten lxSignatureOpcIntegrity tjekker ECMA-376 Part 2 package-signaturer direkte fra projektmappe-bytes: RSA-SHA256 over den kanoniske SignedInfo, hver SHA-256 Reference-digest og de præcise dele, hver signatur vælger. Certifikatkæde-trust er et separat, eksplicit trin, og redigering eller re-signering publicerer kun output, der verificerer igen
Den sædvanlige udløser er et dokumentworkflow, der voksede ud over desktoppen. En controller signerer en budgetprojektmappe i Excel, en Delphi-tjeneste skal senere bekræfte signaturen, før tallene importeres, og et tredje trin behøver at rette ét ark og signere igen under et servicecertifikat. At automatisere Excel på en server til det er skrøbeligt og ikke understøttet, og at behandle signaturen som et ja/nej-flag skjuler de to spørgsmål, der reelt betyder noget: hvilke bytes forpligtede signereren sig til, og stoler du på personen, der holder den nøgle
Hvad dækker en OPC package-signatur egentlig?
En OPC package-signatur dækker kun de dele, dens Manifest lister, ikke hele XLSX-filen. ECMA-376 Part 2, §13 (Digital Signatures) placerer en signature origin-part i pakken, typisk /_xmlsignatures/origin.sigs, med relationships til én eller flere signaturdele som /_xmlsignatures/sig1.xml. Hver signaturdel er en W3C XML-DSig Signature, hvis SignedInfo refererer et internt idPackageObject, og det Object bærer et Manifest af References til enkelte package-dele, hver med sin egen digest
HotXLS understøtter tre måder, en Reference kan binde en del på, og de er ikke udskiftelige. En rå Reference hasher de gemte part-bytes. En kanonisk XML-Reference hasher Canonical XML-formen (uden kommentarer), så attributrækkefølge og namespace-erklæringers stavning ikke længere betyder noget. En relationship-Reference anvender først OPC RelationshipTransform og kanoniserer derefter, og den transform beholder kun de relationships, dens SourceId- eller SourceType-selectors matcher. En selector autentificerer præcis de relationships, den vælger: resten af den .rels-del kan ændre sig frit, mens signaturen forbliver gyldig
Det sidste punkt er grunden til, at HotXLS rapporterer dækning i stedet for ét enkelt grønt hak. TXLSSignatureOpcIntegrityResult.Coverage er et array af TXLSSignatureOpcCoverage-poster, én pr. autentificeret Manifest-Reference, hver med ReferenceUri, PartName, ContentType, Transforms, RelationshipSourceIds og RelationshipSourceTypes. Læser din import sheet3.xml, og ingen deskriptor navngiver den, siger signaturen intet om de tal, du er ved at loade
Verificering af signaturer fra et uforanderligt ZIP-snapshot
HotXLS verificerer en package fra ét uforanderligt byte-snapshot, aldrig fra filen på disk eller en live TWorkbook. XlsVerifySignatureOpcPackage og dens multi-signatur-søskende tager et TBytes-array, læser kun det array og henter aldrig eksterne mål, certifikater, lokale filer eller netværksressourcer. Pointen er, at de bytes, du verificerede, er de bytes, du parser bagefter: genlæses filen senere, kan en anden proces have erstattet den i mellemtiden
Kanonisering er dér, hjemmedyrkede verifikatorer plejer at gå galt. SignedInfo og package-Object'et skal kanoniseres i namespace-konteksten af den komplette Signature-konvolut. Et isoleret fragment skåret ud af dokumentet arver andre namespace-erklæringer, end verifikatoren ser på stedet, så dets C14N-bytes adskiller sig, digesten adskiller sig, og en fuldt ud gyldig signatur fejler. HotXLS bruger den fulde konvolutkontekst på både verifikations- og udstedelsessiden
Til packages, der kan bære mere end én signatur, verificerer XlsVerifyAllSignaturesOpcPackage hver vedhæftet signatur og beholder de enkelte udfald. Aggregatstatussen er xsoiIntegrityVerified kun, når hver tilladt signatur verificerer; ellers er det den første fiasko i inventarrækkefølgen, og hver rapport fortæller stadig, hvilken signaturdel der fejlede
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); // ét snapshot, verificeret og parset
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 er ikke et bestået
end;
Statusværdierne er specifikke nok til at handle på. xsoiDigestMismatch betyder, at en valgt del ændrede sig efter signeringen, xsoiInvalidSignature betyder, at RSA-værdien ikke verificerer mod SignedInfo med det indlejrede certifikats nøgle, xsoiPartUnavailable betyder, at en valgt del mangler, og xsoiUnsupportedProfile eller xsoiResourceLimit betyder, at verifikatoren nægtede i stedet for at gætte. ZIP-laget nedenunder anvender samme fail-closed-læsning, som er beskrevet i artiklen om ZIP EOCD-validering for utroværdige XLSX-filer, og part-opslag følger reglerne i XLSX OPC relationship resolution
Hvorfor invaliderer redigering af en signeret projektmappe signaturen?
Redigering af en signeret projektmappe invaliderer signaturen, så snart en ændret byte falder inden for en signeret selektion, fordi den gemte SHA-256-digest for den Reference ikke længere matcher. En regnearkswriter, der loader projektmappen og gemmer den igen, rører næsten altid signede dele: den reserialiserer XML, omnummererer shared strings, omskriver workbook.xml og content type-manifestet. Selv når celleværdierne er identiske, er bytes det ikke, og næste verifikation returnerer xsoiDigestMismatch
HotXLS lader dig stille spørgsmålet, før du forpligter dig til en redigering. XlsEditVerifiedOpcPackage erstatter eksisterende dele i en verificeret package og verificerer derefter kandidaten. Med xsoePreserve returnerer den xsoePreserved kun, når signaturen stadig verificerer, hvilket sker, når de redigerede dele ligger uden for enhver signeret selektion, og xsoeWouldInvalidate ellers. Med xsoeInvalidateChanged fjerner den signaturbindingen fra rod-relationships'erne og [Content_Types].xml og returnerer en eksplicit usigneret package som xsoeInvalidated i stedet for at give dig en fil, der stadig ligner en signeret
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 var uden for den signede selektion
xsoeWouldInvalidate: Result := False; // en signeret digest dækker sheet2
else
raise Exception.CreateFmt('Edit refused, original integrity %d',
[Ord(Details.OriginalIntegrity)]);
end;
end;
Integritet versus trust: hvad beviser en verificeret signatur?
En verificeret signatur beviser, at de valgte dele er uændrede, siden en person med den private nøgle signede dem, og intet om, hvem den person er. HotXLS holder de to lag adskilt med vilje: hver TXLSSignatureOpcIntegrityResult bærer Trust = xsstUntrusted, inklusive vellykkede tjek, og ClaimedSignatureTime er signerer-leverede data, ikke et troværdigt tidsstempel. Enhver kan generere et self-signed certifikat, signere en projektmappe og bestå integritetstjekket
Trust er et separat kald med en kalder-ejet policy. XlsDefaultSignatureOpcTrustPolicy returnerer en fail-closed-policy uden ankre, uden verifikationstid, med document-signing-EKU'en 1.3.6.1.4.1.311.10.3.12 og xsrvRequireCached-tilbagekaldelse. XlsVerifySignatureOpcPackageWithTrust kører først den komplette integritetsverifikator og evaluerer derefter det certifikat, der reelt bestod den; en kalder kan ikke erstatte et andet bladcertifikat
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-backenden har to fælder, der er lette at overse, når du kalder WinCrypt selv. At CertVerifyCertificateChainPolicy returnerer True betyder kun, at kaldet blev udført; accept kræver stadig en nul dwError i policystatus, nul aggregerede kædefejl, en eksplicit digitalSignature key usage på bladet og den påkrævede EKU. HotXLS fandt, at et certifikat uden key usage kan bestå den almindelige basepolicy, så den tjekker DigitalSignatureUsage og RequiredEkuPresent selv. Den anden fælde: at kombinere et eksklusivt root store med hRestrictedOther i chain engine-konfigurationen returnerer E_INVALIDARG på den aktuelle backend, så HotXLS bruger det eksklusive kalder-root store og autoriserer derefter hvert element af den returnerede primære kæde mod de DER-bytes, du gav med. En udsteder, der blot tilfældigvis sidder i et lokalt system store, bliver aldrig troværdig
xsotTrustedSelection beskriver stadig kun den signatur og dens rapporterede dækning. Usignerede celler og ikke-valgte relationships kan ændre sig, mens den valgte signatur forbliver troværdig, hvilket er grunden til, at trust-resultatet også returnerer PackageSha256 og CertificateSha256 til dit auditlog
Hvordan re-signerer eller kontra-signerer man en projektmappe uden Excel?
På byte-array-niveau re-signerer HotXLS gennem eksplicitte, autoriserede transaktioner, og projektmappens signede save-stier genbruger de samme verificerede pipelines. XlsReplaceVerifiedSignatureOpcPackage bytter én verificeret signatur med en ny, XlsEditAndReplaceSelectedSignatureOpcPackage anvender partredigeringer og erstatter én valgt signatur i samme transaktion, og XlsAppendVerifiedSignatureOpcPackage tilføjer en signatur, mens den beholder hver verificeret original byte for byte. Replace-kaldene tager en TXLSSignatureOpcReplaceAuthorization, append-kaldet verificerer hver original først, og alle tager en TXLSSignatureOpcIssueOptions-selektion og en signerer-callback; biblioteket rører aldrig din private nøgle
Autorisationsposten binder operationen til den præcise original, du godkendte: ExpectedPackageSha256, ExpectedSignaturePart og ExpectedCertificateSha256, alle sammenlignet først, efter at den gamle signatur har verificeret fuldt ud. Hver redigeret del skal være dækket af den nye selektion, hver ikke-valgt signatur skal stadig verificere mod den stagede package, før signereren kaldes, og kaldet returnerer xspsWouldInvalidate ellers
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 er den 32-byte SHA-256, biblioteket beregnede;
// RsaSignSha256Pkcs1 står for din egen CNG-, HSM- eller smart card-wrapper og
// skal returnere den fulde big-endian RSASSA-PKCS1-v1_5-signatur
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 gemt ved godkendelsen
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;
To detaljer i den kode er bevidste. Output og Details skal være separate variable; at give et felt af Details med som Output er eksplicit ikke-understøttet, fordi biblioteket ikke kan publicere en package og beholde en certifikatrapport i samme lager. Og den gamle dækning nedarves ikke: den nye signatur autentificerer præcis de References, du lister, så beslut igen, hvilke dele en downstream-forbruger vil læne sig op ad
Kontra-signering bruger samme append-kald. Siden HotXLS 2.384.296 kan TXLSSignatureOpcIssueOptions.References givet til XlsAppendVerifiedSignatureOpcPackage navngive dele fra det verificerede original-signaturinventar, så en reviewer kan signere over en eksisterende signaturdel, kæde flere kontra-signaturer eller blande signaturdele og indholdsdele i én selektion. Hver tilføjet signatur registrerer kun sine direkte selektioner. Kvalificeringen for den release kørte 222 grænsetjek og 122 uafhængige tjek med .NET WindowsBase packaging-verifikatoren hen over 7 output-packages og bekræftede præcis én ny signatur og byte-identiske originale signaturer og certifikater
Fælder og grænser værd at kende
Den mest lærerige fejl kom fra et tegn, der er fuldt lovligt. OPC Reference-URI'er bærer content typen som en query, for eksempel /xl/worksheets/sheet1.xml?ContentType=application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml. En tidlig HotXLS-udsteder percent-encodel den literale + som %2B. HotXLS' egne RSA-, C14N- og Reference-tjek accepterede resultatet, men WindowsBase rapporterede InvalidSignature; en kontrolleret prøve, der kun ændrede den stavning, gik fra InvalidSignature til Success. Udstederen beholder nu understøttet literal ASCII query-tegnsætning, mens den stadig escaper XML-attributter, og lektien til enhver signerer er at teste mod en uafhængig native-verifikator, ikke kun din egen
- Trust-backenden er kun Windows; andre mål returnerer
xsotTrustUnavailable, efter integriteten er bestået - Verifikatoren accepterer packages op til 64 MiB, 16 MiB pr. del og højst 32 signaturdele under én origin
- Udstedelse accepterer op til 256 References og 4096 kombinerede selectors pr. signatur
- Den understøttede profil er RSA-SHA256, SHA-256-digests og Canonical XML uden kommentarer; andre algoritmer, ekstra application Objects og eksterne References returnerer
xsoiUnsupportedProfile xsrvRequireCachedbruger cache-only tilbagekaldelsesdata; manglende status afviser, ogxsrvNotCheckedgiver ingen tilbagekaldelsessikkerhed- At signere en projektmappe, som Excel også skal acceptere, støder stadig ind i package-reglerne i hvorfor Excel reparerer en gyldig XLSX; et duplikeret Override eller relationship Id knækker filen, før nogen signatur tjekkes
FAQ
Betyder et HotXLS integritetstjek, at signereren er troværdig?
Nej. XlsVerifySignatureOpcPackage beviser, at de valgte dele matcher deres digests under den indlejrede certifikatnøgle og rapporterer altid xsstUntrusted. Brug XlsVerifySignatureOpcPackageWithTrust med dine egne ankre og en UTC-verifikationstid til at evaluere certifikatkæden
Hvorfor viser Excel min projektmappesignatur som ugyldig efter en Delphi-redigering?
En gemt projektmappe omskriver signede XML-dele, så SHA-256-digesten af mindst én Reference ændres. Brug XlsEditVerifiedOpcPackage med xsoePreserve til at teste en redigering først, eller XlsEditAndReplaceSelectedSignatureOpcPackage til at redigere og re-signere i én verificeret transaktion
Kan HotXLS signere med en nøgle gemt på et smart card eller en HSM?
Ja, fordi signering er en kalder-callback. Biblioteket giver den kanoniske SignedInfo og dens 32-byte SHA-256-digest med, din kode returnerer RSASSA-PKCS1-v1_5-signaturen, og den private nøgle forlader aldrig den provider, du styrer
OPC-signaturverificering, trust-evaluering, kontra-signering og verificeret re-signering er en del af HotXLS, den native Excel-regnearkskomponent til Delphi og C++Builder, som læser og skriver XLS, XLSX, XLSB og ODS uden Excel eller COM-automatisering