Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Null Reference exception when Encrypting a PDF document #7

Open
elroyheynes opened this issue Jun 20, 2017 · 5 comments
Open

Null Reference exception when Encrypting a PDF document #7

elroyheynes opened this issue Jun 20, 2017 · 5 comments

Comments

@elroyheynes
Copy link

PfdSharpCore throws a SystemNullReferenceException when I try to render a document that has had encryption enabled via the PdfSecuritySettings.DocumentSecurityLevel setting.

Stack trace points at
PdfSharpCore.Pdf.Security.PdfStandardSecurityHandler.ComputeOwnerKey(Byte[] userPad, Byte[] ownerPad, Boolean strongEncryption) for 128 bit encryption, and
PdfSharpCore.Pdf.Security.PdfStandardSecurityHandler.PrepareRC4Key(Byte[] key, Int32 offset, Int32 length) for 40 bit encryption (1 level deeper).

Project is a UWP application targeting Windows 10.

Is there any pre-configuration that is required (similar to Fonts) for document encryption, or is this an issue that merits investigation?

Thanks.

@groege
Copy link
Owner

groege commented Jun 28, 2017

This is probably an issue, since I didn't try this before. I have no idea if this is implemented properly. Will look into it withing the next couple of weeks.

@groege
Copy link
Owner

groege commented Jun 28, 2017

Actually someone pretty much copied this project XD.
They found the same issue: Didstopia/PDFSharp#3
Let's see how they solve it ^^

@highfield
Copy link

I have a working solution (although I'm not involved in the spinned-off Didstopia project).
I simply surrogated the HashAlgorithm class, because the existent MD5 calculation is not working. Here is the code to replace the existent:

    abstract class HashAlgorithm
    {
        private bool _disposed;
        protected int HashSizeValue;
        protected internal byte[] HashValue;
        protected int State = 0;

        public virtual int HashSize => HashSizeValue;

        public virtual byte[] Hash
        {
            get
            {
                if (_disposed)
                    throw new ObjectDisposedException(null);
                if (State != 0)
                    throw new InvalidOperationException("Hash not yet finalized.");

                return (byte[])HashValue.Clone();
            }
        }

        public byte[] ComputeHash(byte[] buffer)
        {
            if (_disposed)
                throw new ObjectDisposedException(null);
            if (buffer == null)
                throw new ArgumentNullException(nameof(buffer));

            HashCore(buffer, 0, buffer.Length);
            return CaptureHashCodeAndReinitialize();
        }

        public byte[] ComputeHash(byte[] buffer, int offset, int count)
        {
            if (buffer == null)
                throw new ArgumentNullException(nameof(buffer));
            if (offset < 0)
                throw new ArgumentOutOfRangeException(nameof(offset), "Must be non-negative.");
            if (count < 0 || (count > buffer.Length))
                throw new ArgumentOutOfRangeException(nameof(count), "Invalid value.");
            if ((buffer.Length - count) < offset)
                throw new ArgumentOutOfRangeException(nameof(offset), "Invalid value.");

            if (_disposed)
                throw new ObjectDisposedException(null);

            HashCore(buffer, offset, count);
            return CaptureHashCodeAndReinitialize();
        }

        private byte[] CaptureHashCodeAndReinitialize()
        {
            HashValue = HashFinal();

            // Clone the hash value prior to invoking Initialize in case the user-defined Initialize
            // manipulates the array.
            byte[] tmp = (byte[])HashValue.Clone();
            Initialize();
            return tmp;
        }

        public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
        {
            ValidateTransformBlock(inputBuffer, inputOffset, inputCount);

            // Change the State value
            State = 1;

            HashCore(inputBuffer, inputOffset, inputCount);
            if ((outputBuffer != null) && ((inputBuffer != outputBuffer) || (inputOffset != outputOffset)))
            {
                // We let BlockCopy do the destination array validation
                Buffer.BlockCopy(inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount);
            }
            return inputCount;
        }

        public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
        {
            ValidateTransformBlock(inputBuffer, inputOffset, inputCount);

            HashCore(inputBuffer, inputOffset, inputCount);
            HashValue = CaptureHashCodeAndReinitialize();
            byte[] outputBytes;
            if (inputCount != 0)
            {
                outputBytes = new byte[inputCount];
                Buffer.BlockCopy(inputBuffer, inputOffset, outputBytes, 0, inputCount);
            }
            else
            {
                outputBytes = Array.Empty<byte>();
            }

            // Reset the State value
            State = 0;

            return outputBytes;
        }

        private void ValidateTransformBlock(byte[] inputBuffer, int inputOffset, int inputCount)
        {
            if (inputBuffer == null)
                throw new ArgumentNullException(nameof(inputBuffer));
            if (inputOffset < 0)
                throw new ArgumentOutOfRangeException(nameof(inputOffset), "Must be non-negative.");
            if (inputCount < 0 || inputCount > inputBuffer.Length)
                throw new ArgumentOutOfRangeException(nameof(inputCount), "Invalid value.");
            if ((inputBuffer.Length - inputCount) < inputOffset)
                throw new ArgumentOutOfRangeException(nameof(inputOffset), "Invalid value.");

            if (_disposed)
                throw new ObjectDisposedException(null);
        }

        protected abstract void HashCore(byte[] array, int ibStart, int cbSize);
        protected abstract byte[] HashFinal();
        public abstract void Initialize();
    }

Before saving the generated document, you should set some properties like the following:

            pdf.SecuritySettings.DocumentSecurityLevel = PdfSharpCore.Pdf.Security.PdfDocumentSecurityLevel.Encrypted128Bit;
            pdf.SecuritySettings.OwnerPassword = "abc";
            pdf.SecurityHandler.PrepareEncryption();
            pdf.SecurityHandler.EncryptDocument();

@highfield
Copy link

Well, not enough...

empira/PDFsharp#19

For me is working by commenting those lines out:

            //if (dict.Stream != null)
            //{
            //    byte[] bytes = dict.Stream.Value;
            //    if (bytes.Length != 0)
            //    {
            //        PrepareKey();
            //        EncryptRC4(bytes);
            //        dict.Stream.Value = bytes;
            //    }
            //}

@groege
Copy link
Owner

groege commented Oct 20, 2017

I'm not familiar with that part of the code - feel free to send a pull request if you have a good solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants