Skip to content

Bug fix for encryption with .NET Framework

ThomasHoevel edited this page Apr 24, 2025 · 3 revisions

There is a bug affecting PDFsharp 6.2.0 Preview 2 and Preview 3, that prevents PDFsharp using .NET Framework 4.6.2 from creating correct encrypted files. When using .NET 6 or higher, the encrypted files are correct.

Bug will be fixed with PDFsharp 6.2.0 final version.

If you need a quick fix, download the source from the repository, apply the fix described here, and compile your own DLLs.

Problem description:
.NET 6 and higher re-initalize the HashAlgorithm instance after executing TransformFinalBlock, but .NET 4.6.2 does not.
Fix is calling Initialize manually after calling TransformFinalBlock.

Open the file "PdfEncryptionV1To4.cs", locate method "void ComputeAndStoreEncryptionKey(byte[] documentId, byte[] paddedPassword, byte[] ownerValue, uint permissions)".

        void ComputeAndStoreEncryptionKey(byte[] documentId, byte[] paddedPassword, byte[] ownerValue, uint permissions)
        {
            _md5.Initialize();

            // Skipped code.

            if (RevisionValue >= 4 && !EncryptMetadata)
            {
                var additionalBytes = new Byte[] { 0xFF, 0xFF, 0xFF, 0xFF };
                _md5.TransformBlock(additionalBytes, 0, additionalBytes.Length, additionalBytes, 0);
            }

            // Finalize Hash by calling TransformFinalBlock() with an input count of 0.
            _md5.TransformFinalBlock(permission, 0, 0);

            var hash = _md5.Hash!;

            int keyLength;
            if (RevisionValue >= 3)
            {
                // The encryption and MD5 hashing key length (in bytes) shall depend on the Length value (in bits).
                keyLength = ActualLength!.Value / 8;

                // *** Begin of new code. ***
#if !NET6_0_OR_GREATER
                // We have to call Initialize here for .NET 4.6.2.
                // .NET 6/8 include Initialize in "_md5.TransformFinalBlock()".
                _md5.Initialize();
#endif
                // *** End of new code. ***

                // Create the hash 50 times (only for 128 bit).
                for (var idx = 0; idx < 50; idx++)
                {
                    // Only use hash with a length of keyLength.
                    hash = _md5.ComputeHash(hash, 0, keyLength);
                }
            }
            else
            {
                // The encryption key length for revision 2 shall be 5.
                keyLength = 5;
            }

            // Skipped code.
        }