diff --git a/src/PdfSharp-gdi/PdfSharp-gdi.csproj b/src/PdfSharp-gdi/PdfSharp-gdi.csproj
index 941eee61..47245677 100644
--- a/src/PdfSharp-gdi/PdfSharp-gdi.csproj
+++ b/src/PdfSharp-gdi/PdfSharp-gdi.csproj
@@ -902,6 +902,9 @@
Pdf.Printing\PdfFilePrinter.cs
+
+ Pdf.Security\CryptFilterDictionary.cs
+
Pdf.Security\enums\PdfDocumentSecurity.cs
diff --git a/src/PdfSharp-wpf/PdfSharp-wpf.csproj b/src/PdfSharp-wpf/PdfSharp-wpf.csproj
index 21e1c3b1..d77514f5 100644
--- a/src/PdfSharp-wpf/PdfSharp-wpf.csproj
+++ b/src/PdfSharp-wpf/PdfSharp-wpf.csproj
@@ -847,6 +847,9 @@
Pdf.Printing\PdfFilePrinter.cs
+
+ Pdf.Security\CryptFilterDictionary.cs
+
Pdf.Security\enums\PdfDocumentSecurity.cs
diff --git a/src/PdfSharp/Pdf.IO/PdfReader.cs b/src/PdfSharp/Pdf.IO/PdfReader.cs
index 08a9f965..3fc9edd4 100644
--- a/src/PdfSharp/Pdf.IO/PdfReader.cs
+++ b/src/PdfSharp/Pdf.IO/PdfReader.cs
@@ -452,10 +452,10 @@ public static PdfDocument Open(Stream stream, string password, PdfDocumentOpenMo
iref.ObjectNumber);
}
- // Encrypt all objects.
+ // Decrypt all objects.
if (xrefEncrypt != null)
{
- document.SecurityHandler.EncryptDocument();
+ document.SecurityHandler.DecryptDocument();
}
// Fix references of trailer values and then objects and irefs are consistent.
diff --git a/src/PdfSharp/Pdf.IO/PdfWriter.cs b/src/PdfSharp/Pdf.IO/PdfWriter.cs
index 08071c5c..1c5ac2dc 100644
--- a/src/PdfSharp/Pdf.IO/PdfWriter.cs
+++ b/src/PdfSharp/Pdf.IO/PdfWriter.cs
@@ -427,11 +427,12 @@ public void WriteStream(PdfDictionary value, bool omitStream)
byte[] bytes = value.Stream.Value;
if (bytes.Length != 0)
{
- if (_securityHandler != null)
- {
- bytes = (byte[])bytes.Clone();
- bytes = _securityHandler.EncryptBytes(bytes);
- }
+ // Dictionary stream has already been encrypted
+ // if (_securityHandler != null)
+ // {
+ // bytes = (byte[])bytes.Clone();
+ // bytes = _securityHandler.EncryptBytes(bytes);
+ // }
Write(bytes);
if (_lastCat != CharCat.NewLine)
WriteRaw('\n');
diff --git a/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs b/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs
new file mode 100644
index 00000000..9b8f85b3
--- /dev/null
+++ b/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs
@@ -0,0 +1,207 @@
+using System;
+
+namespace PdfSharp.Pdf.Security
+{
+ ///
+ /// The method that should be used when decrypting data.
+ ///
+ internal enum CFM
+ {
+ ///
+ /// Applications that encounter this value shall report that the file is encrypted with an unsupported algorithm.
+ ///
+ Unknown,
+
+ ///
+ /// The application shall not decrypt data but shall direct the input stream to the security handler for decryption.
+ ///
+ None,
+
+ ///
+ /// The application shall ask the security handler for the encryption key and shall implicitly decrypt data with
+ /// "Algorithm 1: Encryption of data using the RC4 or AES algorithms", using the RC4 algorithm.
+ ///
+ V2,
+
+ ///
+ /// (PDF 1.6) The application shall ask the security handler for the encryption key and shall implicitly decrypt data with
+ /// "Algorithm 1: Encryption of data using the RC4 or AES algorithms", using the AES algorithm in Cipher Block Chaining (CBC)
+ /// mode with a 16-byte block size and an initialization vector that shall be randomly generated and placed as the first
+ /// 16 bytes in the stream or string.
+ ///
+ AESV2
+ }
+
+ ///
+ /// The event that will trigger authorization.
+ ///
+ internal enum AuthEvent
+ {
+ ///
+ /// Authorization shall be required when a document is opened.
+ ///
+ DocOpen,
+
+ ///
+ /// Authorization shall be required when accessing embedded files.
+ ///
+ EFOpen
+ }
+
+ ///
+ /// Represents a crypt filter dictionary used for providing granular control of encryption.
+ ///
+ internal class CryptFilterDictionary : PdfDictionary
+ {
+ ///
+ /// Initializes a new CryptFilterDictionary with the required Type name.
+ ///
+ public CryptFilterDictionary()
+ {
+ Type = "CryptFilter";
+ }
+
+ ///
+ /// Initializes a new CryptFilterDictionary from an existing dictionary. Used for object type transformation.
+ ///
+ /// An existing dictionary to read as a crypt filter dictionary.
+ public CryptFilterDictionary(PdfDictionary cryptFilterDictionary) : base(cryptFilterDictionary) { }
+
+ ///
+ /// (Optional) If present, shall be CryptFilter for a crypt filter dictionary.
+ ///
+ public string Type
+ {
+ get { return Elements.GetName("/Type"); }
+ private set { Elements.SetName("/Type", value); }
+ }
+
+ ///
+ /// (Optional) The method used, if any, by the conforming reader to decrypt data. The following
+ /// values shall be supported:
+ /// • None The application shall not decrypt data but shall direct the input stream to the security
+ /// handler for decryption.
+ /// • V2 The application shall ask the security handler for the encryption key and shall implicity
+ /// decrypt data with "Algorithm 1: Encryption of data using the RC4 or AES algorithms", using the
+ /// RC4 algorithm.
+ /// • AESV2 (PDF 1.6) The application shall ask the security handler for the encryption key and shall
+ /// implicitly decrypt data with "Algorithm 1: Encryption of data using the RC4 or AES algorithms",
+ /// using the AES algorithm in Cipher Block Chaining (CBC) mode with a 16-byte block size and an
+ /// initialization vector that shall be randomly generated and placed as the first 16 bytes in the
+ /// stream or string.
+ /// When the value is V2 or AESV2, the application may ask once for this encryption key and cache the key
+ /// for subsequent use for streams that use the same crypt filter. Therefore, there shall be a one-to-one
+ /// relationship between a crypt filter name and the corresponding encryption key.
+ /// Only the values listed here shall be supported. Applications that encounter other values shall report
+ /// that the file is encrypted with an unsupported algorithm.
+ /// Default value: None.
+ ///
+ public CFM CFM
+ {
+ get
+ {
+ string cfmName = Elements.GetName("/CFM");
+ switch (cfmName)
+ {
+ case "/None":
+ return CFM.None;
+ case "/V2":
+ return CFM.V2;
+ case "/AESV2":
+ return CFM.AESV2;
+ default:
+ return CFM.Unknown;
+ }
+ }
+ set
+ {
+ string cfmName;
+ switch (value)
+ {
+ case CFM.None:
+ cfmName = "None";
+ break;
+ case CFM.V2:
+ cfmName = "V2";
+ break;
+ case CFM.AESV2:
+ cfmName = "AESV2";
+ break;
+ case CFM.Unknown:
+ default:
+ throw new ArgumentOutOfRangeException("value", "The CFM must be a valid value.");
+ }
+ Elements.SetName("/CFM", cfmName);
+ }
+ }
+
+ ///
+ /// (Optional) The event to be used to trigger the authorization that is required to access encryption
+ /// keys used by this filter. If authorization fails, the event shall fail. Valid values shall be:
+ /// • DocOpen: Authorization shall be required when a document is opened.
+ /// • EFOpen: Authorization shall be required when accessing embedded files.
+ /// Default value: DocOpen.
+ /// If this filter is used as the value of StrF or StmF in the encryption dictionary, the conforming
+ /// reader shall ignore this key and behave as if the value is DocOpen.
+ ///
+ public AuthEvent AuthEvent
+ {
+ get
+ {
+ string authEventName = Elements.GetName("/AuthEvent");
+ switch (authEventName)
+ {
+ case "/DocOpen":
+ return AuthEvent.DocOpen;
+ case "/EFOpen":
+ return AuthEvent.EFOpen;
+ default:
+ return AuthEvent.DocOpen;
+ }
+ }
+ set
+ {
+ string authEventName;
+ switch (value)
+ {
+ case AuthEvent.DocOpen:
+ authEventName = "DocOpen";
+ break;
+ case AuthEvent.EFOpen:
+ authEventName = "EFOpen";
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("value", "The AuthEvent must be a valid value.");
+ }
+ Elements.SetName("/AuthEvent", authEventName);
+ }
+ }
+
+ ///
+ /// (Optional) The bit length of the encryption key. It shall be a multiple of 8 in the range of 40 to 128.
+ /// Security handlers may define their own use of the Length entry and should use it to define the bit length
+ /// of the encryption key. Standard security handler expresses the length in multiples of 8 (16 means 128)
+ /// and public-key security handler express it as is (128 means 128).
+ ///
+ public int Length
+ {
+ get { return Elements.GetInteger("/Length"); }
+ set
+ {
+ int valueToTest = value;
+ if (value < 40)
+ {
+ // Value can be in bits or bytes.
+ valueToTest = value * 8;
+ }
+
+ if (valueToTest < 40 || valueToTest > 128)
+ throw new ArgumentOutOfRangeException("value", "The Length must be between 40 and 128 bits inclusive.");
+ if (valueToTest % 8 != 0)
+ throw new ArgumentException("The Length must be a multiple of 8 bits.", "value");
+
+ Elements.SetInteger("/Length", value);
+ }
+ }
+ }
+}
diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs
index 4bebef22..67aff488 100644
--- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs
+++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs
@@ -104,21 +104,21 @@ internal PdfUserAccessPermission Permission
}
///
- /// Encrypts the whole document.
+ /// Decrypts the whole document.
///
- public void EncryptDocument()
+ public void DecryptDocument()
{
foreach (PdfReference iref in _document._irefTable.AllReferences)
{
- if (!ReferenceEquals(iref.Value, this))
- EncryptObject(iref.Value);
+ if (iref.ObjectID != this.ObjectID)
+ DecryptObject(iref.Value);
}
}
///
- /// Encrypts an indirect object.
+ /// Decrypts an indirect object.
///
- internal void EncryptObject(PdfObject value)
+ internal void DecryptObject(PdfObject value)
{
Debug.Assert(value.Reference != null);
@@ -132,55 +132,48 @@ internal void EncryptObject(PdfObject value)
PdfArray array;
PdfStringObject str;
if ((dict = value as PdfDictionary) != null)
- EncryptDictionary(dict);
+ DecryptDictionary(dict);
else if ((array = value as PdfArray) != null)
- EncryptArray(array);
+ DecryptArray(array);
else if ((str = value as PdfStringObject) != null)
{
if (str.Length != 0)
{
- byte[] bytes = str.EncryptionValue;
- PrepareKey();
- EncryptRC4(bytes);
- str.EncryptionValue = bytes;
+ str.EncryptionValue = DecryptBytes(str.EncryptionValue);
}
}
}
///
- /// Encrypts a dictionary.
+ /// Decrypts a dictionary.
///
- void EncryptDictionary(PdfDictionary dict)
+ void DecryptDictionary(PdfDictionary dict)
{
- PdfName[] names = dict.Elements.KeyNames;
+ // The Cross-Reference stream is not encrypted.
+ if (dict.Elements.GetName("/Type") == "/XRef") return;
+
foreach (KeyValuePair item in dict.Elements)
{
PdfString value1;
PdfDictionary value2;
PdfArray value3;
if ((value1 = item.Value as PdfString) != null)
- EncryptString(value1);
+ DecryptString(value1);
else if ((value2 = item.Value as PdfDictionary) != null)
- EncryptDictionary(value2);
+ DecryptDictionary(value2);
else if ((value3 = item.Value as PdfArray) != null)
- EncryptArray(value3);
+ DecryptArray(value3);
}
- if (dict.Stream != null)
+ if (dict.Stream != null && dict.Stream.Value.Length != 0)
{
- byte[] bytes = dict.Stream.Value;
- if (bytes.Length != 0)
- {
- PrepareKey();
- EncryptRC4(bytes);
- dict.Stream.Value = bytes;
- }
+ dict.Stream.Value = DecryptBytes(dict.Stream.Value);
}
}
///
- /// Encrypts an array.
+ /// Decrypts an array.
///
- void EncryptArray(PdfArray array)
+ void DecryptArray(PdfArray array)
{
int count = array.Elements.Count;
for (int idx = 0; idx < count; idx++)
@@ -190,25 +183,22 @@ void EncryptArray(PdfArray array)
PdfDictionary value2;
PdfArray value3;
if ((value1 = item as PdfString) != null)
- EncryptString(value1);
+ DecryptString(value1);
else if ((value2 = item as PdfDictionary) != null)
- EncryptDictionary(value2);
+ DecryptDictionary(value2);
else if ((value3 = item as PdfArray) != null)
- EncryptArray(value3);
+ DecryptArray(value3);
}
}
///
- /// Encrypts a string.
+ /// Decrypt a string.
///
- void EncryptString(PdfString value)
+ void DecryptString(PdfString value)
{
if (value.Length != 0)
{
- byte[] bytes = value.EncryptionValue;
- PrepareKey();
- EncryptRC4(bytes);
- value.EncryptionValue = bytes;
+ value.EncryptionValue = DecryptBytes(value.EncryptionValue);
}
}
@@ -219,8 +209,35 @@ internal byte[] EncryptBytes(byte[] bytes)
{
if (bytes != null && bytes.Length != 0)
{
- PrepareKey();
- EncryptRC4(bytes);
+ if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes)
+ {
+ return EncryptAes(bytes);
+ }
+ else
+ {
+ PrepareRC4Key();
+ EncryptRC4(bytes);
+ return bytes;
+ }
+ }
+ return bytes;
+ }
+
+ private byte[] DecryptBytes(byte[] bytes)
+ {
+ if (bytes != null && bytes.Length != 0)
+ {
+ if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes)
+ {
+ return DecryptAes(bytes);
+ }
+ else
+ {
+ // RC4 decryption is equivalent to RC4 encryption
+ PrepareRC4Key();
+ EncryptRC4(bytes);
+ return bytes;
+ }
}
return bytes;
}
@@ -236,7 +253,7 @@ public PasswordValidity ValidatePassword(string inputPassword)
// We can handle 40 and 128 bit standard encryption.
string filter = Elements.GetName(PdfSecurityHandler.Keys.Filter);
int v = Elements.GetInteger(PdfSecurityHandler.Keys.V);
- if (filter != "/Standard" || !(v >= 1 && v <= 3))
+ if (filter != "/Standard" || !(v >= 1 && v <= 4))
throw new PdfReaderException(PSSR.UnknownEncryption);
byte[] documentID = PdfEncoders.RawEncoding.GetBytes(Owner.Internals.FirstDocumentID);
@@ -248,8 +265,31 @@ public PasswordValidity ValidatePassword(string inputPassword)
if (inputPassword == null)
inputPassword = "";
- bool strongEncryption = rValue == 3;
- int keyLength = strongEncryption ? 16 : 32;
+ bool strongEncryption;
+ int keyLength;
+ switch (rValue)
+ {
+ case 2:
+ strongEncryption = false;
+ keyLength = 5;
+ break;
+ case 3:
+ strongEncryption = true;
+ keyLength = Elements.GetInteger(Keys.Length) / 8;
+ break;
+ case 4:
+ CryptFilterDictionary cryptFilter = new CryptFilterDictionary(Elements.GetDictionary(Keys.CF).Elements.GetDictionary("/StdCF"));
+ if (cryptFilter.CFM != CFM.V2 && cryptFilter.CFM != CFM.AESV2 && cryptFilter.AuthEvent != AuthEvent.DocOpen)
+ throw new PdfReaderException(PSSR.UnsupportedCryptFilter);
+
+ strongEncryption = true;
+ keyLength = cryptFilter.Length;
+ if (cryptFilter.CFM == CFM.AESV2)
+ _document.SecuritySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.Encrypted128BitAes;
+ break;
+ default:
+ throw new PdfReaderException(PSSR.UnsupportedRevisionNumber);
+ }
// Try owner password first.
//byte[] password = PdfEncoders.RawEncoding.GetBytes(inputPassword);
@@ -429,7 +469,7 @@ void SetupUserKey(byte[] documentID)
///
/// Prepare the encryption key.
///
- void PrepareKey()
+ void PrepareRC4Key()
{
if (_key != null && _keySize > 0) //!!!mod 2017-11-06 Added "if" because PrepareRC4Key fails if _key is null. But _key appears to be always null, so maybe PrepareKey() is obsolete.
PrepareRC4Key(_key, 0, _keySize);
@@ -510,6 +550,54 @@ void EncryptRC4(byte[] inputData, int offset, int length, byte[] outputData)
}
}
+ ///
+ /// Encrypts the data and returns the result, which will be larger than the original data.
+ ///
+ byte[] EncryptAes(byte[] data)
+ {
+ using (Rijndael aes = Rijndael.Create())
+ {
+ // Settings defined in PDF 32000 spec
+ aes.Mode = CipherMode.CBC;
+ aes.Padding = PaddingMode.PKCS7;
+ aes.BlockSize = 128; // 16 bytes
+ aes.KeySize = _keySize * 8;
+ // Enable for debugging only! Provides a consistent IV when testing the encryption
+ // aes.IV = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+ using (ICryptoTransform encryptor = aes.CreateEncryptor(_key, aes.IV))
+ {
+ byte[] encrypted = encryptor.TransformFinalBlock(data, 0, data.Length);
+ byte[] result = new byte[aes.IV.Length + encrypted.Length];
+ Array.Copy(aes.IV, 0, result, 0, aes.IV.Length);
+ Array.Copy(encrypted, 0, result, aes.IV.Length, encrypted.Length);
+ return result;
+ }
+ }
+ }
+
+ ///
+ /// Decrypts the data and returns the result, which will be smaller than the encrypted data.
+ ///
+ byte[] DecryptAes(byte[] encryptedData)
+ {
+ using (Rijndael aes = Rijndael.Create())
+ {
+ // Settings defined in PDF 32000 spec
+ aes.Mode = CipherMode.CBC;
+ aes.Padding = PaddingMode.PKCS7;
+ aes.BlockSize = 128; // 16 bytes
+ aes.KeySize = _keySize * 8;
+ // Retrieve the IV from the encrypted data
+ byte[] iv = new byte[16];
+ Array.Copy(encryptedData, 0, iv, 0, 16);
+ using (ICryptoTransform decryptor = aes.CreateDecryptor(_key, iv))
+ {
+ byte[] decrypted = decryptor.TransformFinalBlock(encryptedData, 16, encryptedData.Length - 16);
+ return decrypted;
+ }
+ }
+ }
+
///
/// Checks whether the calculated key correct.
///
@@ -531,15 +619,25 @@ internal void SetHashKey(PdfObjectID id)
#if !NETFX_CORE && !DNC10
//#if !SILVERLIGHT
byte[] objectId = new byte[5];
- _md5.Initialize();
// Split the object number and generation
objectId[0] = (byte)id.ObjectNumber;
objectId[1] = (byte)(id.ObjectNumber >> 8);
objectId[2] = (byte)(id.ObjectNumber >> 16);
objectId[3] = (byte)id.GenerationNumber;
objectId[4] = (byte)(id.GenerationNumber >> 8);
+ _md5.Initialize();
_md5.TransformBlock(_encryptionKey, 0, _encryptionKey.Length, _encryptionKey, 0);
- _md5.TransformFinalBlock(objectId, 0, objectId.Length);
+ _md5.TransformBlock(objectId, 0, objectId.Length, objectId, 0);
+ if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes)
+ {
+ // Additional padding needed for AES encryption
+ byte[] aesPadding = new byte[] { 0x73, 0x41, 0x6C, 0x54 }; // 'sAlT'
+ _md5.TransformFinalBlock(aesPadding, 0, aesPadding.Length);
+ }
+ else
+ {
+ _md5.TransformFinalBlock(objectId, 0, 0);
+ }
_key = _md5.Hash;
_md5.Initialize();
_keySize = _encryptionKey.Length + 5;
@@ -557,23 +655,35 @@ public void PrepareEncryption()
//#if !SILVERLIGHT
Debug.Assert(_document._securitySettings.DocumentSecurityLevel != PdfDocumentSecurityLevel.None);
int permissions = (int)Permission;
- bool strongEncryption = _document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128Bit;
-
- PdfInteger vValue;
- PdfInteger length;
- PdfInteger rValue;
+ bool strongEncryption;
- if (strongEncryption)
+ if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes)
+ {
+ strongEncryption = true;
+ Elements[Keys.V] = new PdfInteger(4);
+ Elements[Keys.R] = new PdfInteger(4);
+ CryptFilterDictionary aesCryptFilter = new CryptFilterDictionary();
+ aesCryptFilter.CFM = CFM.AESV2;
+ aesCryptFilter.Length = 16;
+ PdfDictionary cryptFilters = new PdfDictionary();
+ cryptFilters.Elements["/StdCF"] = aesCryptFilter;
+ Elements[Keys.CF] = cryptFilters;
+ Elements[Keys.StmF] = new PdfName("/StdCF");
+ Elements[Keys.StrF] = new PdfName("/StdCF");
+ }
+ else if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128Bit)
{
- vValue = new PdfInteger(2);
- length = new PdfInteger(128);
- rValue = new PdfInteger(3);
+ strongEncryption = true;
+ Elements[Keys.V] = new PdfInteger(2);
+ Elements[Keys.Length] = new PdfInteger(128);
+ Elements[Keys.R] = new PdfInteger(3);
}
else
{
- vValue = new PdfInteger(1);
- length = new PdfInteger(40);
- rValue = new PdfInteger(2);
+ strongEncryption = false;
+ Elements[Keys.V] = new PdfInteger(1);
+ Elements[Keys.Length] = new PdfInteger(40);
+ Elements[Keys.R] = new PdfInteger(2);
}
if (String.IsNullOrEmpty(_userPassword))
@@ -601,9 +711,6 @@ public void PrepareEncryption()
PdfString uValue = new PdfString(PdfEncoders.RawEncoding.GetString(_userKey, 0, _userKey.Length));
Elements[Keys.Filter] = new PdfName("/Standard");
- Elements[Keys.V] = vValue;
- Elements[Keys.Length] = length;
- Elements[Keys.R] = rValue;
Elements[Keys.O] = oValue;
Elements[Keys.U] = uValue;
Elements[Keys.P] = pValue;
diff --git a/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs b/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs
index 90150c77..c06df245 100644
--- a/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs
+++ b/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs
@@ -46,8 +46,13 @@ public enum PdfDocumentSecurityLevel
Encrypted40Bit,
///
- /// Document is protected with 128-bit security.
+ /// Document is protected with 128-bit RC4 security.
///
Encrypted128Bit,
+
+ ///
+ /// Document is protected with 128-bit AES security.
+ ///
+ Encrypted128BitAes,
}
}
diff --git a/src/PdfSharp/Pdf/PdfDictionary.cs b/src/PdfSharp/Pdf/PdfDictionary.cs
index 428140a1..538e9df2 100644
--- a/src/PdfSharp/Pdf/PdfDictionary.cs
+++ b/src/PdfSharp/Pdf/PdfDictionary.cs
@@ -192,11 +192,17 @@ internal override void WriteObject(PdfWriter writer)
//int count = Elements.Count;
PdfName[] keys = Elements.KeyNames;
-#if DEBUG
- // TODO: automatically set length
if (_stream != null)
- Debug.Assert(Elements.ContainsKey(PdfStream.Keys.Length), "Dictionary has a stream but no length is set.");
-#endif
+ {
+ if (writer.SecurityHandler != null)
+ {
+ // Encryption could change the size of the stream.
+ // Encrypt the bytes before writing the dictionary to get the actual size.
+ byte[] bytes = (byte[])_stream.Value.Clone();
+ _stream.Value = writer.SecurityHandler.EncryptBytes(bytes);
+ }
+ Elements[PdfStream.Keys.Length] = new PdfInteger(_stream.Length);
+ }
#if DEBUG
// Sort keys for debugging purposes. Comparing PDF files with for example programs like
diff --git a/src/PdfSharp/PdfSharp.csproj b/src/PdfSharp/PdfSharp.csproj
index cdae9caf..586b5a15 100644
--- a/src/PdfSharp/PdfSharp.csproj
+++ b/src/PdfSharp/PdfSharp.csproj
@@ -308,6 +308,7 @@
+
diff --git a/src/PdfSharp/Resources/Messages.de.restext b/src/PdfSharp/Resources/Messages.de.restext
index c463242b..150cdf22 100644
--- a/src/PdfSharp/Resources/Messages.de.restext
+++ b/src/PdfSharp/Resources/Messages.de.restext
@@ -18,3 +18,5 @@ UserOrOwnerPasswordRequired = Zum Verschlüsseln des Dokuments muss ein Kennwort
UnexpectedToken = Token '{0}' wird an dieser Stelle nicht erwartet.
UnknownEncryption = Das PDF-Dokument ist mit einer von PDFsharp nicht unterstützten Verschlüsselung geschützt.
+UnsupportedCryptFilter = Das PDF-Dokument verwendet einen Verschlüsselungsfilter, der vom Standardsicherheitshandler nicht unterstützt wird.
+UnsupportedRevisionNumber = Das PDF-Dokument wird durch eine nicht unterstützte Version des Standardsicherheitshandlers geschützt.
diff --git a/src/PdfSharp/Resources/Messages.restext b/src/PdfSharp/Resources/Messages.restext
index e47d27ff..79efdc80 100644
--- a/src/PdfSharp/Resources/Messages.restext
+++ b/src/PdfSharp/Resources/Messages.restext
@@ -18,3 +18,5 @@ UserOrOwnerPasswordRequired = At least a user or an owner password is required t
UnexpectedToken = Token '{0}' was not expected.
UnknownEncryption = The PDF document is protected with an encryption not supported by PDFsharp.
+UnsupportedCryptFilter = The PDF document uses a crypt filter that is not supported by the standard security handler.
+UnsupportedRevisionNumber = The PDF document is protected with an unsupported revision of the standard security handler.
diff --git a/src/PdfSharp/root/PSSR.cs b/src/PdfSharp/root/PSSR.cs
index 232a5a84..7612a087 100644
--- a/src/PdfSharp/root/PSSR.cs
+++ b/src/PdfSharp/root/PSSR.cs
@@ -334,6 +334,16 @@ public static string UnknownEncryption
//get { return "The PDF document is protected with an encryption not supported by PDFsharp."; }
}
+ public static string UnsupportedCryptFilter
+ {
+ get { return GetString(PSMsgID.UnsupportedCryptFilter); }
+ }
+
+ public static string UnsupportedRevisionNumber
+ {
+ get { return GetString(PSMsgID.UnsupportedRevisionNumber); }
+ }
+
#endregion
#region Resource manager
diff --git a/src/PdfSharp/root/enums/PSMsgID.cs b/src/PdfSharp/root/enums/PSMsgID.cs
index 375d2e3e..16ebc545 100644
--- a/src/PdfSharp/root/enums/PSMsgID.cs
+++ b/src/PdfSharp/root/enums/PSMsgID.cs
@@ -72,5 +72,15 @@ enum PSMsgID
/// PSMsgID.
///
UnknownEncryption,
- }
+
+ ///
+ /// PSMsgID.
+ ///
+ UnsupportedCryptFilter,
+
+ ///
+ /// PSMsgID.
+ ///
+ UnsupportedRevisionNumber,
+ }
}
\ No newline at end of file