در بخشهای حقوقی، مالی و مراقبتهای بهداشتی، تولید حجم زیادی از اسناد PDF یک رویه استاندارد است. با این حال، تولید اسناد تنها نیمی از مسیر است؛ تامین امنیت آنها به همان اندازه حیاتی است. هنگامی که شما یک خط لوله بایگانی دارید که روزانه صدها گیگابایت فایل PDF را پردازش میکند، اعمال رمزنگاری AES-256 به سرعت میتواند به یک گلوگاه عملکردی تبدیل شود.
در این مقاله، بررسی خواهیم کرد که چگونه با جلوگیری از اتمام حافظه و بهینهسازی حلقههای رمزنگاری، به سرعتهای بالای رمزنگاری AES-256 در فایلهای PDF در دلفی دست یابیم.
مشخصات رمزنگاری در PDF
امنیت PDF تحول قابل توجهی یافته است. نسخههای اولیه از RC4 با کلید ۴۰ بیتی استفاده میکردند که امروزه به راحتی قابل شکستن است. استاندارد فعلی (PDF 1.7 Extension Level 3 و PDF 2.0) رمزنگاری AES-256 را الزامی میکند.
در یک فایل PDF، شما کل بلوک فایل را رمزنگاری نمیکنید. ساختار سند (جدول XRef و ساختار دیکشنریها) به صورت متن ساده (plaintext) باقی میماند. در عوض، شما جریانها (Streams) (دادههای خام برای تصاویر و محتوای صفحه) و رشتهها (Strings) (مانند متن متادیتا) را رمزنگاری میکنید. این کار مستلزم آن است که تجزیهکننده دادهها را استخراج کند، AES CBC (Cipher Block Chaining) را اعمال کند و آن را دوباره بنویسد.
گلوگاه: بارگذاری در حافظه
یک اشتباه رایج هنگام رمزنگاری یک فایل PDF عظیم (مثلاً یک بایگانی اسکن شده دو گیگابایتی) بارگذاری کل جریان در TMemoryStream قبل از ارسال آن به موتور رمزنگاری است. این کار منجر به خطاهای کمبود حافظه (OOM) در فرآیندهای ۳۲ بیتی و خطاهای شدید صفحه حافظه (page faulting) در فرآیندهای ۶۴ بیتی میشود.
رمزنگاری جریانی در دلفی
راهحل استفاده از رویکرد جریانی و قطعهقطعه (chunked) است. با استفاده از Windows Cryptography API: Next Generation (CNG) یا یک کتابخانه مانند OpenSSL، میتوانید جریان PDF را در بلوکهای ۶۴ کیلوبایتی بخوانید، بلوک را رمزنگاری کنید و آن را مستقیماً به جریان دیسک خروجی بنویسید.
در اینجا یک مثال مفهومی در دلفی آورده شده است که حلقه رمزنگاری بافر شده را برای یک جریان نشان میدهد:
uses
System.Classes, System.SysUtils;
const
BUFFER_SIZE = 65536; // 64KB chunks
// This represents your AES-256 encryption routine
procedure EncryptStreamChunk(const InBuffer; var OutBuffer; BytesRead: Integer; const Key: TBytes; const IV: TBytes);
begin
// Call to Windows CNG (BCryptEncrypt) or OpenSSL (EVP_EncryptUpdate)
// ...
end;
procedure EncryptLargePDFStream(InputStream, OutputStream: TStream; const Key, IV: TBytes);
var
InBuffer, OutBuffer: array of Byte;
BytesRead: Integer;
begin
SetLength(InBuffer, BUFFER_SIZE);
// AES CBC requires padding, so the output buffer must be slightly larger
SetLength(OutBuffer, BUFFER_SIZE + 16);
InputStream.Position := 0;
OutputStream.Position := 0;
repeat
BytesRead := InputStream.Read(InBuffer[0], BUFFER_SIZE);
if BytesRead > 0 then
begin
EncryptStreamChunk(InBuffer[0], OutBuffer[0], BytesRead, Key, IV);
// Write the encrypted cipher text directly to disk
OutputStream.Write(OutBuffer[0], BytesRead); // Note: padding logic omitted for brevity
end;
until BytesRead < BUFFER_SIZE;
end;بهینهسازی سیستم پسزمینه رمزنگاری
توسعهدهندگان دلفی چندین انتخاب برای سیستم بکاند AES دارند:
- پیادهسازیهای بومی دلفی: برای استقرار آسان هستند، اما اغلب کندترند زیرا صرفاً به صورت نرمافزاری اجرا میشوند.
- Windows CNG (BCrypt): بسیار بهینهسازی شده است و میتواند از شتابدهندههای سختافزاری (دستورالعملهای AES-NI در پردازندههای مدرن اینتل و ایامدی) استفاده کند.
- OpenSSL (libcrypto): استاندارد صنعتی، بسیار سریع، اما نیازمند ارسال فایلهای DLL خارجی به همراه برنامه است.
برای برنامههای سرور با توان عملیاتی بالا، شتابدهنده سختافزاری AES-NI الزامی است. هنگام استفاده از Windows CNG در دلفی، نگاشت تابع BCryptEncrypt به برنامه شما امکان میدهد تا کارهای سنگین را به سیلیکون رمزنگاری اختصاصی CPU واگذار کنید و عملاً سربار رمزنگاری را به نزدیک صفر کاهش دهید.
نتیجهگیری
هنگام رمزنگاری فایلهای PDF در مقیاس گیگابایت، به جای بافر کردن کامل در حافظه، به تقطیع جریانها تکیه کنید و اطمینان حاصل کنید که سیستم رمزنگاری شما از شتابدهنده سختافزاری AES-NI بهره میبرد. این ترکیب تضمین میکند که خط لوله بایگانی شما به جای محدود شدن توسط CPU، با سرعت درایوهای NVMe شما اجرا شود.
توجه: رمزنگاری پرسرعت AES-256 با بهرهگیری از جریانهای تقطیع شده، به صورت بومی در کامپوننت HotPDF VCL پشتیبانی میشود.