HotXLS, Excel kurulu olmadan Delphi'de Excel dijital imzalarını doğrular ve yeniden imzalar. lxSignatureOpcIntegrity ünitesi, ECMA-376 Part 2 paket imzalarını doğrudan çalışma kitabı baytlarından kontrol eder: kanonik SignedInfo üzerinde RSA-SHA256, her SHA-256 Reference özeti ve her imzanın seçtiği tam parçalar. Sertifika zinciri güveni ayrı ve açık bir adımdır; düzenleme ya da yeniden imzalama ise yalnızca yeniden doğrulanan çıktıyı yayımlar
Olağan tetikleyici, masaüstünü aşan bir belge iş akışıdır. Bir kontrolör Excel'de bir bütçe çalışma kitabını imzalar; bir Delphi servisi sayıları içe aktarmadan önce imzayı teyit etmek zorundadır; üçüncü bir adım ise bir sayfayı düzeltip bir servis sertifikası altında yeniden imzalamak ister. Bunun için sunucuda Excel otomasyonu kırılgan ve desteklenmeyendir; imzayı bir evet/hayır bayrağı saymak da gerçekte önemli iki soruyu gizler: imzalayan hangi baytlara söz verdi ve o anahtarı tutan kişiye güveniyor musunuz
Bir OPC paket imzası gerçekte neyi kapsar?
Bir OPC paket imzası yalnızca Manifest'inin listelediği parçaları kapsar; XLSX dosyasının tamamını değil. ECMA-376 Part 2, §13 (Digital Signatures), pakete bir imza kaynak parçası koyar; tipik olarak /_xmlsignatures/origin.sigs ve /_xmlsignatures/sig1.xml gibi bir ya da daha çok imza parçasına ilişkilerle. Her imza parçası, SignedInfo'su dahili bir idPackageObject'e referans veren bir W3C XML-DSig Signature'dır; o Object ise ayrı paket parçalarına yapılan Reference'lardan oluşan bir Manifest taşır ve her birinin kendi özeti vardır
HotXLS bir Reference'ın bir parçayı bağlamasının üç yolunu destekler ve bunlar birbirinin yerine geçmez. Ham bir Reference, saklanan parça baytlarını özetler. Kanonik bir XML Reference, Canonical XML (yorumlar olmadan) biçimini özetler; öznitelik sırası ile ad alanı bildiriminin yazımı artık önemli değildir. Bir ilişki Reference'ı önce OPC RelationshipTransform uygular sonra kanonikleştirir ve o dönüşüm yalnızca SourceId ya da SourceType seçicileriyle eşleşen ilişkileri tutar. Bir seçici tam olarak seçtiği ilişkileri kimliklendirir: o .rels parçasının geri kalanı, imza geçerli kalırken serbestçe değişebilir
Son nokta, HotXLS'in tek bir yeşil tik yerine kapsam bildirmesinin nedenidir. TXLSSignatureOpcIntegrityResult.Coverage, kimliklenen her Manifest Reference için bir tane olmak üzere TXLSSignatureOpcCoverage kayıtlarından oluşan bir dizidir; her biri ReferenceUri, PartName, ContentType, Transforms, RelationshipSourceIds ve RelationshipSourceTypes taşır. İçe aktarımınız sheet3.xml'i okuyorsa ve hiçbir tanımlayıcı onu adlandırmıyorsa imza, yüklemek üzere olduğunuz sayılar hakkında hiçbir şey söylemez
Değişmez bir ZIP anlık görüntüsünden imzaları doğrulamak
HotXLS bir paketi tek bir değişmez bayt anlık görüntüsünden doğrular; diskteki dosyadan ya da canlı bir TWorkbook'tan asla. XlsVerifySignatureOpcPackage ile çoklu imza kardeşleri bir TBytes dizisi alır, yalnızca o diziyi okur ve dış hedefleri, sertifikaları, yerel dosyaları ya da ağ kaynaklarını asla getirmez. Önemli olan şu: doğruladığınız baytlar, sonra ayrıştırdığınız baytlardır; dosya sonra yeniden okunursa arada başka bir süreç onu değiştirmiş olabilir
Kanonikleştirme, ev yapımı doğrulayıcıların genellikle yanıldığı yerdir. SignedInfo ile paket Object, eksiksiz Signature zarfının ad alanı bağlamında kanonikleştirilmelidir. Belgeden kesilip alınmış izole bir parça, doğrulayıcının yerinde gördüğünden farklı ad alanı bildirimlerini miras alır; C14N baytları farklılaşır, özet farklılaşır ve tümüyle geçerli bir imza başarısız olur. HotXLS, hem doğrulama hem üretim tarafında tam zarf bağlamını kullanır
Birden çok imza taşıyabilen paketler için XlsVerifyAllSignaturesOpcPackage iliştirilmiş her imzayı doğrular ve bireysel sonuçları saklar. Toplam durum, kabul edilen her imza doğrulandığında xsoiIntegrityVerified olur; aksi hâlde envanter sırasındaki ilk başarısızlıktır ve her rapor hangi imza parçasının başarısız olduğunu yine söyler
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); // tek anlık görüntü; doğrulanır ve ayrıştırılır
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 bir geçiş değildir
end;
Durum değerleri işlem yapmaya yetecek kadar özeldir. xsoiDigestMismatch, imzadan sonra seçilmiş bir parçanın değiştiği anlamına gelir; xsoiInvalidSignature, RSA değerinin gömülü sertifika anahtarıyla SignedInfo'ya karşı doğrulanamadığı; xsoiPartUnavailable, seçilmiş bir parçanın eksik olduğu; xsoiUnsupportedProfile ile xsoiResourceLimit ise doğrulayıcının tahmin etmek yerine reddettiği anlamına gelir. Alttaki ZIP katmanı, güvenilmeyen XLSX dosyaları için ZIP EOCD doğrulaması makalesinde anlatılan fail-closed okumayı uygular; parça araması XLSX OPC ilişki çözümlemesindeki kuralları izler
İmzalı bir çalışma kitabını düzenlemek imzayı neden geçersiz kılar?
İmzalı bir çalışma kitabını düzenlemek, değişen bir bayt imzalı bir seçimin içine düştüğü anda imzayı geçersiz kılar; çünkü o Reference'ın saklanan SHA-256 özeti artık eşleşmez. Çalışma kitabını yükleyip yeniden kaydeden bir elektronik tablo yazıcısı neredeyse daima imzalı parçalara dokunur: XML'i yeniden serileştirir, paylaşılan dizgeleri yeniden numaralandırır, workbook.xml'i ve içerik türü manifestosunu yeniden yazar. Hücre değerleri özdeş olduğunda bile baytlar özdeş değildir ve sonraki doğrulama xsoiDigestMismatch döndürür
HotXLS, soruyu düzenlemeye karar vermeden sormanızı sağlar. XlsEditVerifiedOpcPackage, doğrulanmış bir paketteki mevcut parçaları değiştirir ve sonra adayı doğrular. xsoePreserve ile imza hâlâ doğrulanıyorsa — düzenlenen parçalar imzalı her seçimin dışında yattığında olur — xsoePreserved döndürür; aksi hâlde xsoeWouldInvalidate. xsoeInvalidateChanged ile imza bağını kök ilişkilerden ve [Content_Types].xml'den kaldırır ve hâlâ imzalı görünen bir dosya vermek yerine açıkça imzasız bir paketi xsoeInvalidated olarak döndürür
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 imzalı seçimin dışındaydı
xsoeWouldInvalidate: Result := False; // imzalı bir özet sheet2'yi kapsıyor
else
raise Exception.CreateFmt('Edit refused, original integrity %d',
[Ord(Details.OriginalIntegrity)]);
end;
end;
Bütünlük ile güven: doğrulanmış bir imza neyi kanıtlar?
Doğrulanmış bir imza, seçili parçaların özel anahtarı elinde tutan birinin onları imzalamasından bu yana değişmediğini kanıtlar; o birinin kim olduğu hakkında hiçbir şey söylemez. HotXLS iki katmanı bilerek ayrı tutar: her TXLSSignatureOpcIntegrityResult başarılı kontroller dahil Trust = xsstUntrusted taşır ve ClaimedSignatureTime imzalayanın verdiği veridir, güvenilir bir zaman damgası değil. Herkes kendinden imzalı bir sertifika üretebilir, bir çalışma kitabı imzalayabilir ve bütünlük kontrolünü geçebilir
Güven, çağıranın sahibi olduğu bir politikayla yapılan ayrı bir çağrıdır. XlsDefaultSignatureOpcTrustPolicy, bağlantı noktası olmayan, doğrulama zamanı olmayan, belge imzalama EKU'su 1.3.6.1.4.1.311.10.3.12 olan ve xsrvRequireCached iptali isteyen fail-closed bir politika döndürür. XlsVerifySignatureOpcPackageWithTrust önce eksiksiz bütünlük doğrulayıcısını çalıştırır ve sonra ondan gerçekten geçen sertifikayı değerlendirir; çağıran başka bir yaprak sertifika ikame edemez
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 arka ucunun, WinCrypt'i kendiniz çağırırken gözden kaçması kolay iki tuzağı vardır. CertVerifyCertificateChainPolicy'in True döndürmesi yalnızca çağrının icra edildiği anlamına gelir; kabul için yine de politika durumunda sıfır dwError, sıfır toplam zincir hatası, yaprak üzerinde açık bir digitalSignature anahtar kullanımı ve zorunlu EKU gerekir. HotXLS, anahtar kullanımı eksik bir sertifikanın sıradan temel politikayı geçebildiğini gördü; bu yüzden DigitalSignatureUsage ile RequiredEkuPresent'i kendisi kontrol eder. İkinci tuzak: zincir motoru yapılandırmasında hariç (exclusive) bir kök deposunu hRestrictedOther ile birleştirmek, geçerli arka uçta E_INVALIDARG döndürür; HotXLS bu yüzden hariç çağıran kök deposunu kullanır ve sonra döndürülen birincil zincirin her öğesini verdiğiniz DER baytlarına karşı yetkilendirir. Yalnızca yerel bir sistem deposunda oturuyorsa hiçbir üretici güvenilir olmaz
xsotTrustedSelection hâlâ yalnızca o imzayı ve bildirdiği kapsamı anlatır. Seçili imza güvenilir kalırken imzasız hücreler ile seçilmemiş ilişkiler değişebilir; güven sonucu bu yüzden denetim günlüğünüz için PackageSha256 ile CertificateSha256'yi de döndürür
Excel olmadan bir çalışma kitabını nasıl yeniden imzalar ya da karşıt imzasını eklersiniz?
Bayt dizisi düzeyinde HotXLS yeniden imzalamayı açık ve yetkilendirilmiş işlemlerle yapar ve Workbook imzalı kaydetme yolları aynı doğrulanmış hatları yeniden kullanır. XlsReplaceVerifiedSignatureOpcPackage doğrulanmış bir imzayı yenisiyle değiştirir; XlsEditAndReplaceSelectedSignatureOpcPackage parça düzenlemelerini uygular ve seçili bir imzayı aynı işlemde değiştirir; XlsAppendVerifiedSignatureOpcPackage ise doğrulanmış her özgünü bayt bayt koruyarak bir imza ekler. Değiştirme çağrıları bir TXLSSignatureOpcReplaceAuthorization alır; ekleme çağrısı önce her özgünü doğrular; hepsi bir TXLSSignatureOpcIssueOptions seçimi ile bir imzalayıcı callback'i alır; kütüphane özel anahtarınıza asla dokunmaz
Yetkilendirme kaydı işlemi, onayladığınız tam özgüne bağlar: ExpectedPackageSha256, ExpectedSignaturePart ile ExpectedCertificateSha256; hepsi yalnızca eski imza tümüyle doğrulandıktan sonra karşılaştırılır. Düzenlenen her parça yeni seçimce kapsanmalı, seçilmemiş her imza imzalayıcı çağrılmadan önce sahnelenmiş pakete karşı hâlâ doğrulanmalıdır; aksi hâlde çağrı xspsWouldInvalidate döndürür
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, kütüphanenin hesapladığı 32 baytlık SHA-256'dır;
// RsaSignSha256Pkcs1, kendi CNG, HSM ya da akıllı kart sarmalayıcınızı temsil eder ve
// tam big-endian RSASSA-PKCS1-v1_5 imzasını döndürmelidir
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); // ya da onay anında saklanan özet
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;
O koddaki iki ayrıntı bilinçlidir. Output ile Details ayrı değişkenler olmalıdır; Details'in bir alanını Output olarak geçmek açıkça desteklenmez, çünkü kütüphane bir paketi yayımlayıp sertifika raporunu aynı depoda tutamaz. Ve eski kapsam miras alınmaz: yeni imza tam olarak listelediğiniz Reference'ları kimliklendirir; bir aşağı akış tüketicisinin hangi parçalara güveneceğini yeniden belirleyin
Karşıt imzalama aynı ekleme çağrısını kullanır. HotXLS 2.384.296'dan bu yana XlsAppendVerifiedSignatureOpcPackage'e verilen TXLSSignatureOpcIssueOptions.References, doğrulanmış özgün imza envanterinden parçaları adlandırabilir; bir inceleyici var olan bir imza parçası üzerinden imzalayabilir, birkaç karşıt imzayı zincirleyebilir ya da tek seçimde imza parçaları ile içerik parçalarını karıştırabilir. Eklenen her imza yalnızca kendi doğrudan seçimlerini kaydeder. O sürümün yeterlilik çalıştırması, 7 çıktı paketi üzerinde .NET WindowsBase paketleme doğrulayıcısıyla 222 sınır kontrolü ve 122 bağımsız kontrol yürüttü; tam olarak bir yeni imza ile bayt bayt özdeş özgün imzalar ve sertifikalar doğrulandı
Bilmeye değer tuzaklar ve sınırlar
En öğretici hata tümüyle yasal bir karakterden geldi. OPC Reference URI'ları içerik türünü sorgu olarak taşır; örneğin /xl/worksheets/sheet1.xml?ContentType=application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml. Erken bir HotXLS üreticisi, sabit + karakterini %2B olarak yüzde kodluyordu. HotXLS'in kendi RSA, C14N ve Reference kontrolleri sonucu kabul ediyordu ama WindowsBase InvalidSignature bildiriyordu; yalnızca o yazımı değiştiren kontrollü bir örnek InvalidSignature'den Success'e geçti. Üretici artık desteklenen sabit ASCII sorgu noktalamasını korurken XML özniteliklerini yine kaçırıyor; her imzalayıcı için ders, yalnızca kendi doğrulayıcınıza değil bağımsız yerli bir doğrulayıcıya karşı test etmektir
- Güven arka ucu yalnızca Windows'tur; öteki hedefler bütünlük başarılı olduktan sonra
xsotTrustUnavailabledöndürür - Doğrulayıcı 64 MiB'ye kadar paketleri, parça başına 16 MiB'yi ve tek bir kaynak altında en fazla 32 imza parçasını kabul eder
- Üretim, imza başına 256 Reference'a kadar ve 4096 birleşik seçiciyi kabul eder
- Desteklenen profil RSA-SHA256, SHA-256 özetleri ve yorumsuz Canonical XML'dir; öteki algoritmalar, ek uygulama Object'leri ve dış Reference'lar
xsoiUnsupportedProfiledöndürür xsrvRequireCachedyalnızca önbellek iptal verisi kullanır; eksik durum reddettirir vexsrvNotCheckedhiçbir iptal güvencesi vermez- Excel'in de kabul etmesi gereken bir çalışma kitabını imzalamak yine Excel'in geçerli bir XLSX'i neden onardığındaki paket kurallarına takılır; yinelenmiş bir Override ya da ilişki Id, hiçbir imza kontrol edilmeden dosyayı bozar
SSS
HotXLS bütünlük kontrolü imzalayanın güvenilir olduğu anlamına gelir mi?
Hayır. XlsVerifySignatureOpcPackage, seçili parçaların gömülü sertifika anahtarı altında özetleriyle eşleştiğini kanıtlar ve daima xsstUntrusted bildirir. Sertifika zincirini değerlendirmek için XlsVerifySignatureOpcPackageWithTrust'u kendi bağlantı noktalarınızla ve bir UTC doğrulama zamanıyla kullanın
Excel, Delphi düzenlemesinden sonra çalışma kitabı imzamı neden geçersiz gösterir?
Kaydedilen bir çalışma kitabı imzalı XML parçalarını yeniden yazar; en az bir Reference'ın SHA-256 özeti bu yüzden değişir. Düzenlemeyi önce denemek için XlsEditVerifiedOpcPackage'i xsoePreserve ile kullanın ya da tek doğrulanmış işlemde düzenleyip yeniden imzalamak için XlsEditAndReplaceSelectedSignatureOpcPackage
HotXLS, akıllı kartta ya da HSM'de saklı bir anahtarla imzalayabilir mi?
Evet, çünkü imzalama bir çağıran callback'idir. Kütüphane kanonik SignedInfo'yu ile 32 baytlık SHA-256 özetini geçirir; sizin kodunuz RSASSA-PKCS1-v1_5 imzasını döndürür ve özel anahtar, denetlediğiniz sağlayıcıdan asla çıkmaz
OPC imza doğrulaması, güven değerlendirmesi, karşıt imzalama ve doğrulanmış yeniden imzalama, Delphi ve C++Builder için yerel Excel elektronik tablo bileşeni HotXLS'in parçasıdır; bileşen XLS, XLSX, XLSB ve ODS'i Excel ya da COM otomasyonu olmadan okur ve yazar