Technical Article

Excel Şifrelenmiş Çalışma Kitabınızı Neden Reddediyor: ECB ve RC4

Bir çalışma kitabı yazarsınız, onu bir şifreyle şifrelersiniz, dosyayı bir meslektaşınıza verirsiniz ve meslektaşınız onu Excel'de açar. Excel şifreyi sorar. Meslektaşınız yazar ve Excel kabul eder. Şimdiye kadar şifreleme doğru görünüyor. Ardından Excel, dosyanın bozuk olduğunu ve açılamayacağını belirten bir iletişim kutusu görüntüler veya anlamsız hücrelerden oluşan bir sayfaya açılır. Şifre doğruydu. Dosya yine de bozuk. Bu, Office şifrelemesindeki en kafa karıştırıcı başarısızlık modudur, çünkü size şifrenin doğru olduğunu söyleyen kısım ile verilerinizi tutan kısım iki farklı işlemle korunur ve birini doğru yapmak diğerini garanti etmek için hiçbir şey yapmaz

Burada açıklanan her iki hata da tam olarak bu şekle sahipti. Her durumda doğrulayıcı geçti ve gövde geçmedi, bu da sizi orada olmayan bir şifre veya anahtar türetme hatası aramaya yönlendirir. Gerçek hata, paket baytlarının nasıl dönüştürüldüğünde, akış yönündeydi. İki hata birbirinden bağımsızdır (biri AES yolunda ve diğeri RC4 yolunda) ancak bir teşhis sorununu paylaşırlar, bu nedenle yarı doğru bir sonucun neden okunması en zor tür olduğunu görmek değerlidir

Geçen bir şifre neden gövde hakkında hiçbir şeyi kanıtlamaz

Modern şifrelenmiş XLSX'in kullandığı biçim ECMA-376 Standart Şifrelemedir ve iki şifrelenmiş şeyi yan yana saklar. Biri EncryptionVerifier'dır: rastgele bir değer ve bu değerin karmasını (hash) tutan, şifreden türetilen anahtarla şifrelenmiş küçük bir blok. Diğeri ise EncryptedPackage'dır: çalışma kitabının aynı anahtarla şifrelenmiş tüm zip kapsayıcısı. Doğrulayıcı, bir okuyucunun megabaytlarca gövdeye çaba harcamadan önce bir şifreyi onaylayabilmesi için mevcuttur. Doğrulayıcının şifresini çözün, rastgele değeri karıştırın, saklanan karma ile karşılaştırın ve eşleşiyorlarsa şifre doğrudur

Tuzak, doğrulayıcının ve paketin ayrı arabellekler üzerinden ayrı çağrılarla şifrelenmesidir. Doğru türetilen bir anahtar, daha sonra pakete ne olursa olsun doğrulayıcının şifresini doğru şekilde çözecektir. Dolayısıyla, anahtar türetmeniz doğruysa ancak paket dönüşümünüz yanlışsa, Excel şifreyi doğrulayıcıdan onaylar ve ardından gövdede başarısız olur. Belirti "doğru şifre, bozuk dosya" olarak okunur; bu da incelemeyi, hiçbir zaman bozulmamış olan tek kısım olan şifre yoluna yönlendirir. Aynı ayrım eski RC4 durumu için de geçerlidir: önce doğrulayıcı karması kontrol edilir ve senkronizasyon dışına kayan bir gövde yine de bu kontrolü sağlam bırakır

Hata bir: CBC değil, ECB'de AES

[MS-OFFCRYPTO] §2.3.4.15, Standart Şifrelemenin paketi Elektronik Kod Defteri (ECB) modunda AES ile şifrelediğini belirtir. Padded paketin her 16 baytlık bloğu bağımsız olarak aynı anahtarla şifrelenir. Bloklar arasında zincirleme yoktur ve başlatma vektörü (IV) yoktur. Bu, ECB'nin normalde kaçınıldığı modern standartlara göre sıra dışı bir seçimdir ancak birlikte çalışabilirlik, spesifikasyonu sorgulayacağınız bir yer değildir. Excel paketi ECB olarak çözer, bu nedenle bir üretici bunu ECB olarak şifrelemelidir veya ikisi anlaşamayacaktır

Hata, paketin tamamen sıfır başlatma vektörü kullanılarak CBC modunda AES ile şifrelenmesiydi. İşte bunun neden neredeyse çalıştığı ve neredeyse kelimesinin neden inilebilecek en kötü yer olduğu. CBC'de ilk düz metin bloğu şifrelemeden önce IV ile XORlanır. IV tamamen sıfır olduğunda, bu XOR hiçbir şeyi değiştirmez, bu nedenle sıfır IV'lü CBC'nin ilk bloğu ECB ile tam olarak aynı şifreli metni üretir. İkinci bloktan itibaren CBC, önceki şifreli metin bloğunu bir sonrakine besler, böylece ilk bloktan sonraki her blok ECB'den sapar

Şimdi bunu yapının üzerine bindirin. Paket düzeni en başta 8 baytlık bir little-endian uzunluk öneki yerleştirir, bu nedenle Excel'in en erken kontrol ettiği dosya parçaları ilk blokta veya ikincisinde oturur. Eşleşen ilk blok, en erken doğrulamanın geçtiği anlamına gelirken, daha sonraki her blok gürültüye çözülür. Çözüm, mod adlandırıldıktan sonra karmaşık değildir: her 16 baytlık bloğu ECB ile şifreleyin ve zincirlemeyi durdurun. Motor içinde XlsEncryptStdPackage dolgulu arabelleği 16 baytlık adımlarla dolaşır ve her birinde, verici blokları için zaten kullanılan aynı ilkel olan AESEncryptECB128Block'ı çağırır. Kaynak, döngüde kuralı açıkça belirten bir yorum taşır: sıfır IV'lü CBC yalnızca ilk blok için ECB ile eşleşir, bu nedenle paketin geri kalanı çöpe çözülecektir ve Excel bunu reddedecektir

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create(nil);
  try
    Book.Open('report.xlsx');
    // SaveAsEncrypted serializes the workbook, then runs the
    // ECMA-376 Standard Encryption pipeline: AES-128 ECB over the
    // package per [MS-OFFCRYPTO] 2.3.4.15. Returns 1 on success.
    if Book.SaveAsEncrypted('report_secure.xlsx', 'S3cret!') <> 1 then
      raise Exception.Create('Encryption failed');
  finally
    Book.Free;
  end;
end;

Hata iki: RC4 yeniden anahtarlama adımı senkronizasyon dışına kayıyor

Eski .xls yolu RC4 CryptoAPI şemasını kullanır ve kuralı farklıdır. [MS-OFFCRYPTO] §2.3.6, şifrenin her 1024 baytlık blok sınırında yeniden anahtarlanacağını (re-keyed) belirtir. Akış 1024 baytlık bloklara bölünür, 0, 1, 2 vb. blok numaraları için taze bir RC4 anahtarı türetilir ve her blok içinde anahtar akışı bayttan bayta sürekli olarak tüketilir. İki değişmezin birlikte tutulması gerekir: her sınırda yeniden anahtarlayın ve bir blok içinde boşluk bırakmadan anahtar akışını tüketin. RC4 bir akış şifresidir, bu nedenle anahtar akışı tek bir sıralı dizidir; çektiğiniz n. bayt, daha önce kaç bayt çektiğinizle belirlenir. Şifre çözme, aynı diziye karşı aynı XOR işlemidir; bu da üretici ve tüketicinin tam olarak aynı konumlarda tam olarak aynı baytları çekmesi gerektiği anlamına gelir

Tüm zorluk budur. Bir akış şifresinin yeniden senkronizasyonu yoktur. Bir baytlık anahtar akışını boşa harcarsanız, ondan sonraki her bayt yanlış anahtar akışı baytına karşı XORlanır ve hata asla kendini düzeltmez; bloğun sonuna ve konum yanlış olduğunda ondan sonraki her bloğa basamaklanır. Buradaki hata tam olarak bunu yaptı. Blok sayacı negatif bir başlangıç değerinden başladı ve atlama (skip) rutini sayacın zaten mevcut blokla eşleştiğini varsaydı. Bu başlangıç değerinden başlayarak, yeniden anahtarladı ve hiç tüketilmemesi gereken tam bir 1024 baytlık anahtar akışı bloğu tüketti; bu süreçte kalan sayıyı negatife sürükledi. O noktadan itibaren şifre çözücü bir blok faz dışındaydı. Bundan önce kontrol edilen doğrulayıcı hala geçiyordu, bu nedenle her veri hücresi çöp olarak çıkarken şifre doğru görünüyordu

Düzeltilmiş mantık TXLSDecrypterRC4 içinde yaşar. Hem Skip hem de Decrypt tek bir döngüyü paylaşır: yalnızca çalışan konum REKEY_BLOCK_SIZE (1024) değerine bölünen blok dizini olan yeni bir bloğa geçtiğinde yeniden anahtarlayın, ardından geçerli bloğun kalanına kadar tüketin ve daha fazlasını tüketmeyin. MakeKey eski veya sentinel bir dizinle değil, blok diziniyle çağrılır ve konum işlenen tam bayt sayısı kadar ilerler; böylece Skip ve Decrypt üreticiyle faz uyumlu kalır. Çıkarılması gereken ders en küçük birimdedir: tek bir boşa harcanan bayt, bir akış şifresinde küçük bir hata değildir, akış yönündeki her şeyin tamamen kaybıdır

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create(nil);
  try
    // CanReadEncrypted checks the Compound File (OLE2) signature so
    // you can branch before attempting a normal Open. OpenEncrypted
    // routes plain files to Open and handles the encrypted container.
    if Book.CanReadEncrypted('legacy.xls') then
      Book.OpenEncrypted('legacy.xls', 'S3cret!')
    else
      Book.Open('legacy.xls');
    // read cells here
  finally
    Book.Free;
  end;
end;

Dondurulmuş bir spesifikasyonla birlikte çalışabilirlik bayta kadar eşleşmektir

Her iki hata da aynı temel ilkeye indirgenir ve bunu kendi başına belirtmeye değer çünkü tasarım seçimlerini nasıl tarttığınızı değiştirir. Çıktınızın tüketicisi değiştiremeyeceğiniz sabit bir harici program olduğunda, şifreleme modu ve yeniden anahtarlama sıklığı basitleştireceğiniz veya optimize edeceğiniz uygulama ayrıntıları değildir. Kablo sözleşmesinin bir parçasıdırlar. Excel, bu seçimler sizi memnun etsin ya da etmesin, ECB ile çözecek ve 1024 baytlık sınırlarda yeniden anahtarlayacaktır; sizin tek işiniz o prosedür altında orijinaline çözülen baytlar üretmektir. Daha modern görünen bir mod, zararsız görünen bir IV, doğal hissettiren bir yerden başlayan bir sayaç; bunlardan herhangi biri, okuyucunun beklediğinden saptığı anda bir kusurdur. Dondurulmuş bir spesifikasyona karşı birlikte çalışabilirlik yaklaşık değildir. Bayt-kesin veya bozuktur

Doğrulayıcının kendi başına zayıf bir duman testi olmasının nedeni de budur. Size anahtar türetmenin çalıştığını söyler, bu gereklidir ancak kesinlikle yeterli değildir. Yalnızca şifrelenmiş bir dosyayı açan ve şifrenin geçtiğini onaylayan bir test, gövde okunamaz durumdayken başarı bildirecektir. Gerçek bir test paketin şifresini çözer ve kurtarılan baytları orijinal girdiyle karşılaştırır veya bir çalışma kitabını şifreleme ve şifre çözme yoluyla gidiş-dönüş çalıştırıp hücreleri geri okur. Doğrulayıcı şifreyi kanıtlar; yalnızca gövde şifrelemeyi kanıtlar

Korumalı çalışma kitaplarını okumanın ve yazmanın desteklenen yolu

Genel arayüz küçüktür. Parola korumalı modern bir çalışma kitabı yazmak için bir TXLSXWorkbook doldurun veya açın ve bir dosya adı ile şifreyle SaveAsEncrypted'ı çağırın; çalışma kitabını serileştirir ve ilk düzeltmenin düzelttiği Standart Şifreleme boru hattını çalıştırarak başarı durumunda 1 döndürür. Okumak için, bir dosyanın şifrelenmiş bir Bileşik Dosya kapsayıcısı olup olmadığını test etmek üzere CanReadEncrypted'ı çağırın, ardından dallanın: OpenEncrypted şifrelenmiş yolu işler ve düz dosyalar için Open'a geri döner; şifreli Open ise doğrudan kullanılabilir. Yukarıda açıklanan mod işleme ve yeniden anahtarlama döngüsü bu çağrıların altında yer alır; siz şifreyi ve dosya adını sağlarsınız ve motor sizin adınıza spesifikasyonla eşleşir

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create(nil);
  try
    Book.Open('quarterly.xlsx');
    Book.SaveAsEncrypted('quarterly_locked.xlsx', 'P@ssphrase');
    // Reopen on the consumer side
    Book.OpenEncrypted('quarterly_locked.xlsx', 'P@ssphrase');
  finally
    Book.Free;
  end;
end;

Korumalı çıktının şekli, EncryptionInfo akışı, doğrulayıcı blokları ve paket düzeni, AES korumalı XLSX çıktısı kılavuzumuzda ele alınmıştır. Sayfa düzeyinde kilitleme ve korumanın sayfa yapısı ve yazdırma ile nasıl etkileşime girdiğine dair ayrı soru için, koruma, sayfa yapısı ve yazdırma hakkındaki makaleye bakın. Her ikisi de, bu blogun başka yerlerinde ele alınan okuma, yazma ve işleme API'lerinin yanı sıra Delphi and C++Builder için HotXLS elektronik tablo bileşeni'nde sunulan burada açıklanan şifreleme yolu üzerine inşa edilmiştir