PdfSharpCore
PdfSharpCore copied to clipboard
Null Reference exception when Encrypting a PDF document
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.
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.
Actually someone pretty much copied this project XD. They found the same issue: https://github.com/Didstopia/PDFSharp/issues/3 Let's see how they solve it ^^
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();
Well, not enough...
https://github.com/empira/PDFsharp/issues/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;
// }
//}
https://github.com/groege/PdfSharpCore/blob/a429b03519d4290096118642ebf5e536652aba74/PdfSharpCore/Pdf.Security/PdfStandardSecurityHandler.cs#L168
I'm not familiar with that part of the code - feel free to send a pull request if you have a good solution.