From 43b08409a5a3c8ee016b6044dc03ba17bfd89c1d Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 24 Oct 2018 15:05:55 -0600 Subject: [PATCH 01/26] Added a new enum value for AES-128 encryption --- src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs | 3 ++- src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index bdce51ff..7761ad1b 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -557,7 +557,8 @@ public void PrepareEncryption() //#if !SILVERLIGHT Debug.Assert(_document._securitySettings.DocumentSecurityLevel != PdfDocumentSecurityLevel.None); int permissions = (int)Permission; - bool strongEncryption = _document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128Bit; + bool strongEncryption = _document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128Bit + || _document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes; PdfInteger vValue; PdfInteger length; diff --git a/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs b/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs index 11b9fb36..a864a4c8 100644 --- a/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs +++ b/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs @@ -49,5 +49,10 @@ public enum PdfDocumentSecurityLevel /// Document is protected with 128-bit security. /// Encrypted128Bit, + + /// + /// Document is protected with 128-bit AES security. + /// + Encrypted128BitAes, } } From 68acb72d396cba55b21cf057a6de6672c3b8749f Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 24 Oct 2018 15:12:09 -0600 Subject: [PATCH 02/26] Added extra AES padding for encryption key generation --- .../Pdf.Security/PdfStandardSecurityHandler.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index 7761ad1b..cee9be97 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -530,7 +530,8 @@ internal void SetHashKey(PdfObjectID id) { #if !NETFX_CORE //#if !SILVERLIGHT - byte[] objectId = new byte[5]; + byte[] objectId = new byte[9]; + int objectIdLength = 5; _md5.Initialize(); // Split the object number and generation objectId[0] = (byte)id.ObjectNumber; @@ -538,8 +539,17 @@ internal void SetHashKey(PdfObjectID id) objectId[2] = (byte)(id.ObjectNumber >> 16); objectId[3] = (byte)id.GenerationNumber; objectId[4] = (byte)(id.GenerationNumber >> 8); + if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) + { + // Additional padding needed for AES encryption + objectIdLength = 9; + objectId[5] = 0x73; // 's' + objectId[6] = 0x41; // 'A' + objectId[7] = 0x6C; // 'l' + objectId[8] = 0x54; // 'T' + } _md5.TransformBlock(_encryptionKey, 0, _encryptionKey.Length, _encryptionKey, 0); - _md5.TransformFinalBlock(objectId, 0, objectId.Length); + _md5.TransformFinalBlock(objectId, 0, objectIdLength); _key = _md5.Hash; _md5.Initialize(); _keySize = _encryptionKey.Length + 5; From 89af79b9b452cf5925cbf6f0c637ebebc4f0a422 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 24 Oct 2018 15:12:42 -0600 Subject: [PATCH 03/26] Renamed PrepareKey to PrepareRC4Key for consistency --- .../Pdf.Security/PdfStandardSecurityHandler.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index cee9be97..ab2ce436 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -140,7 +140,7 @@ internal void EncryptObject(PdfObject value) if (str.Length != 0) { byte[] bytes = str.EncryptionValue; - PrepareKey(); + PrepareRC4Key(); EncryptRC4(bytes); str.EncryptionValue = bytes; } @@ -170,7 +170,7 @@ void EncryptDictionary(PdfDictionary dict) byte[] bytes = dict.Stream.Value; if (bytes.Length != 0) { - PrepareKey(); + PrepareRC4Key(); EncryptRC4(bytes); dict.Stream.Value = bytes; } @@ -206,7 +206,7 @@ void EncryptString(PdfString value) if (value.Length != 0) { byte[] bytes = value.EncryptionValue; - PrepareKey(); + PrepareRC4Key(); EncryptRC4(bytes); value.EncryptionValue = bytes; } @@ -219,7 +219,7 @@ internal byte[] EncryptBytes(byte[] bytes) { if (bytes != null && bytes.Length != 0) { - PrepareKey(); + PrepareRC4Key(); EncryptRC4(bytes); } return bytes; @@ -429,7 +429,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); From 614a1e2321cbaec50b6d68e0529a91152042326d Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 24 Oct 2018 15:38:12 -0600 Subject: [PATCH 04/26] Created a new method to encrypt with AES-128 --- .../PdfStandardSecurityHandler.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index ab2ce436..01760aff 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -510,6 +510,30 @@ 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 = 128; + aes.Key = _key; + using (ICryptoTransform encryptor = aes.CreateEncryptor()) + { + byte[] encrypted = encryptor.TransformFinalBlock(data, 0, data.Length); + byte[] result = new byte[aes.IV.Length + encrypted.Length]; + aes.IV.CopyTo(result, 0); + encrypted.CopyTo(result, aes.IV.Length); + return result; + } + } + } + /// /// Checks whether the calculated key correct. /// From 268790d7040c5c118421419ebc28d2a8adcd62ce Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 24 Oct 2018 15:49:34 -0600 Subject: [PATCH 05/26] Added AES encryption alongside existing RC4 encryption --- .../PdfStandardSecurityHandler.cs | 49 ++++++++++++++----- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index 01760aff..bac0c241 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -139,10 +139,15 @@ internal void EncryptObject(PdfObject value) { if (str.Length != 0) { - byte[] bytes = str.EncryptionValue; - PrepareRC4Key(); - EncryptRC4(bytes); - str.EncryptionValue = bytes; + if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) + { + str.EncryptionValue = EncryptAes(str.EncryptionValue); + } + else + { + PrepareRC4Key(); + EncryptRC4(str.EncryptionValue); + } } } } @@ -165,14 +170,16 @@ void EncryptDictionary(PdfDictionary dict) else if ((value3 = item.Value as PdfArray) != null) EncryptArray(value3); } - if (dict.Stream != null) + if (dict.Stream != null && dict.Stream.Value.Length != 0) { - byte[] bytes = dict.Stream.Value; - if (bytes.Length != 0) + if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) + { + dict.Stream.Value = EncryptAes(dict.Stream.Value); + } + else { PrepareRC4Key(); - EncryptRC4(bytes); - dict.Stream.Value = bytes; + EncryptRC4(dict.Stream.Value); } } } @@ -205,10 +212,15 @@ void EncryptString(PdfString value) { if (value.Length != 0) { - byte[] bytes = value.EncryptionValue; - PrepareRC4Key(); - EncryptRC4(bytes); - value.EncryptionValue = bytes; + if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) + { + value.EncryptionValue = EncryptAes(value.EncryptionValue); + } + else + { + PrepareRC4Key(); + EncryptRC4(value.EncryptionValue); + } } } @@ -221,6 +233,17 @@ internal byte[] EncryptBytes(byte[] bytes) { PrepareRC4Key(); EncryptRC4(bytes); + + if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) + { + return EncryptAes(bytes); + } + else + { + PrepareRC4Key(); + EncryptRC4(bytes); + return bytes; + } } return bytes; } From ccea8b389685211f9c6ef33281edd1d1ca30136a Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 24 Oct 2018 16:00:57 -0600 Subject: [PATCH 06/26] Removed duplicate code that unintentionally encrypted the data twice --- src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index bac0c241..e8e1c75e 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -231,9 +231,6 @@ internal byte[] EncryptBytes(byte[] bytes) { if (bytes != null && bytes.Length != 0) { - PrepareRC4Key(); - EncryptRC4(bytes); - if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) { return EncryptAes(bytes); From d8cb127b1e2f72cedb0e60309d9f6f1a79758da7 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 24 Oct 2018 16:19:44 -0600 Subject: [PATCH 07/26] Added AES decryption methods --- .../PdfStandardSecurityHandler.cs | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index e8e1c75e..a1497c84 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -141,7 +141,7 @@ internal void EncryptObject(PdfObject value) { if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) { - str.EncryptionValue = EncryptAes(str.EncryptionValue); + str.EncryptionValue = DecryptAes(str.EncryptionValue); } else { @@ -174,7 +174,7 @@ void EncryptDictionary(PdfDictionary dict) { if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) { - dict.Stream.Value = EncryptAes(dict.Stream.Value); + dict.Stream.Value = DecryptAes(dict.Stream.Value); } else { @@ -214,7 +214,7 @@ void EncryptString(PdfString value) { if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) { - value.EncryptionValue = EncryptAes(value.EncryptionValue); + value.EncryptionValue = DecryptAes(value.EncryptionValue); } else { @@ -554,6 +554,29 @@ byte[] EncryptAes(byte[] data) } } + /// + /// 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 = 128; + aes.Key = _key; + // Retrieve the IV from the encrypted data + Array.Copy(encryptedData, aes.IV, 16); + using (ICryptoTransform decryptor = aes.CreateDecryptor()) + { + byte[] decrypted = decryptor.TransformFinalBlock(encryptedData, 16, encryptedData.Length - 16); + return decrypted; + } + } + } + /// /// Checks whether the calculated key correct. /// From c06ef4b9cf2121a1b08bd3e29905552cb18e9625 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Fri, 26 Oct 2018 10:11:43 -0600 Subject: [PATCH 08/26] Renamed methods to match their function --- src/BuildAll-PdfSharp.sln | 33 +++++++- src/PdfSharp/Pdf.IO/PdfReader.cs | 2 +- .../PdfStandardSecurityHandler.cs | 83 +++++++++---------- src/SecurityTest/Program.cs | 44 ++++++++++ src/SecurityTest/Properties/AssemblyInfo.cs | 36 ++++++++ src/SecurityTest/SecurityTest.csproj | 50 +++++++++++ 6 files changed, 201 insertions(+), 47 deletions(-) create mode 100644 src/SecurityTest/Program.cs create mode 100644 src/SecurityTest/Properties/AssemblyInfo.cs create mode 100644 src/SecurityTest/SecurityTest.csproj diff --git a/src/BuildAll-PdfSharp.sln b/src/BuildAll-PdfSharp.sln index 51818cd7..a4cd8ddd 100644 --- a/src/BuildAll-PdfSharp.sln +++ b/src/BuildAll-PdfSharp.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.40629.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2046 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PDFsharp", "PdfSharp\PDFsharp.csproj", "{5A6055BC-BF86-4FDD-9F62-0109DB7A303B}" EndProject @@ -14,6 +14,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfSharp-wpf", "PdfSharp-wp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfSharp.Charting-wpf", "PdfSharp.Charting-wpf\PdfSharp.Charting-wpf.csproj", "{E6A2734E-0CD6-4210-8AEC-47EE348F8D78}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SecurityTest", "SecurityTest\SecurityTest.csproj", "{E5A6E921-FEDD-4564-9997-4A5D4588A555}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -126,8 +128,35 @@ Global {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|Win32.ActiveCfg = Release|Any CPU {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|x64.ActiveCfg = Release|Any CPU {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|x86.ActiveCfg = Release|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|ARM.ActiveCfg = Debug|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|ARM.Build.0 = Debug|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|Win32.ActiveCfg = Debug|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|Win32.Build.0 = Debug|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|x64.ActiveCfg = Debug|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|x64.Build.0 = Debug|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|x86.ActiveCfg = Debug|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|x86.Build.0 = Debug|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|Any CPU.Build.0 = Release|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|ARM.ActiveCfg = Release|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|ARM.Build.0 = Release|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|Win32.ActiveCfg = Release|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|Win32.Build.0 = Release|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|x64.ActiveCfg = Release|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|x64.Build.0 = Release|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|x86.ActiveCfg = Release|Any CPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B225FEBB-A4FF-4EAF-91A8-C737C60E8318} + EndGlobalSection EndGlobal diff --git a/src/PdfSharp/Pdf.IO/PdfReader.cs b/src/PdfSharp/Pdf.IO/PdfReader.cs index d2d59eb5..77c3df74 100644 --- a/src/PdfSharp/Pdf.IO/PdfReader.cs +++ b/src/PdfSharp/Pdf.IO/PdfReader.cs @@ -455,7 +455,7 @@ public static PdfDocument Open(Stream stream, string password, PdfDocumentOpenMo // Encrypt 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.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index a1497c84..1676777f 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); + 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,30 +132,22 @@ 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) { - if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) - { - str.EncryptionValue = DecryptAes(str.EncryptionValue); - } - else - { - PrepareRC4Key(); - EncryptRC4(str.EncryptionValue); - } + str.EncryptionValue = DecryptBytes(str.EncryptionValue); } } } /// - /// Encrypts a dictionary. + /// Decrypts a dictionary. /// - void EncryptDictionary(PdfDictionary dict) + void DecryptDictionary(PdfDictionary dict) { PdfName[] names = dict.Elements.KeyNames; foreach (KeyValuePair item in dict.Elements) @@ -164,30 +156,22 @@ void EncryptDictionary(PdfDictionary dict) 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 && dict.Stream.Value.Length != 0) { - if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) - { - dict.Stream.Value = DecryptAes(dict.Stream.Value); - } - else - { - PrepareRC4Key(); - EncryptRC4(dict.Stream.Value); - } + 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++) @@ -197,46 +181,57 @@ 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) + { + value.EncryptionValue = DecryptBytes(value.EncryptionValue); + } + } + + /// + /// Encrypts an array. + /// + internal byte[] EncryptBytes(byte[] bytes) + { + if (bytes != null && bytes.Length != 0) { if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) { - value.EncryptionValue = DecryptAes(value.EncryptionValue); + return EncryptAes(bytes); } else { PrepareRC4Key(); - EncryptRC4(value.EncryptionValue); + EncryptRC4(bytes); + return bytes; } } + return bytes; } - /// - /// Encrypts an array. - /// - internal byte[] EncryptBytes(byte[] bytes) + private byte[] DecryptBytes(byte[] bytes) { if (bytes != null && bytes.Length != 0) { if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) { - return EncryptAes(bytes); + return DecryptAes(bytes); } else { + // RC4 decryption is equivalent to RC4 encryption PrepareRC4Key(); EncryptRC4(bytes); return bytes; diff --git a/src/SecurityTest/Program.cs b/src/SecurityTest/Program.cs new file mode 100644 index 00000000..8302eee9 --- /dev/null +++ b/src/SecurityTest/Program.cs @@ -0,0 +1,44 @@ +using PdfSharp.Pdf; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Security; + +namespace SecurityTest { + class Program { + static void Main(string[] args) { + // Get a fresh copy of the sample PDF file + const string filenameSource = "C:\\Users\\bcallaghan\\Downloads\\Rx Design Guidelines.pdf"; + const string filenameDest = "C:\\Users\\bcallaghan\\Desktop\\Protected.pdf"; + const string filenameEnd = "C:\\Users\\bcallaghan\\Desktop\\Unprotected.pdf"; + + // Open an existing document. Providing an unrequired password is ignored. + PdfDocument document = PdfReader.Open(filenameSource, "some text"); + + // Setting one of the passwords automatically sets the security level to + // PdfDocumentSecurityLevel.Encrypted128Bit. + document.SecuritySettings.UserPassword = "user"; + document.SecuritySettings.OwnerPassword = "owner"; + document.SecuritySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.Encrypted128BitAes; + + // Don't use 40 bit encryption unless needed for compatibility + //securitySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.Encrypted40Bit; + + // Restrict some rights. + document.SecuritySettings.PermitAccessibilityExtractContent = false; + document.SecuritySettings.PermitAnnotations = false; + document.SecuritySettings.PermitAssembleDocument = false; + document.SecuritySettings.PermitExtractContent = false; + document.SecuritySettings.PermitFormsFill = true; + document.SecuritySettings.PermitFullQualityPrint = false; + document.SecuritySettings.PermitModifyDocument = true; + document.SecuritySettings.PermitPrint = false; + + // Save the document... + document.Save(filenameDest); + + // Round-trip to unprotected + //PdfDocument secure = PdfReader.Open(filenameDest, "owner"); + //secure.SecuritySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.None; + //secure.Save(filenameEnd); + } + } +} diff --git a/src/SecurityTest/Properties/AssemblyInfo.cs b/src/SecurityTest/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..441b5b96 --- /dev/null +++ b/src/SecurityTest/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SecurityTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SecurityTest")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e5a6e921-fedd-4564-9997-4a5d4588a555")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/SecurityTest/SecurityTest.csproj b/src/SecurityTest/SecurityTest.csproj new file mode 100644 index 00000000..959325b0 --- /dev/null +++ b/src/SecurityTest/SecurityTest.csproj @@ -0,0 +1,50 @@ + + + + + Debug + AnyCPU + {E5A6E921-FEDD-4564-9997-4A5D4588A555} + Exe + SecurityTest + SecurityTest + v2.0 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + {5384ce57-1f94-4d22-860d-2e9c1ac12ddf} + PdfSharp-gdi + + + + \ No newline at end of file From 1c4c18b1ec574f9fc20512e27faa3f7160b2b4e7 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Fri, 26 Oct 2018 11:15:35 -0600 Subject: [PATCH 09/26] Added CryptFilterDictionary keys --- .../PdfStandardSecurityHandler.cs | 75 +++++++++++++++---- 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index 1676777f..f416cb1f 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -632,21 +632,17 @@ public void PrepareEncryption() bool strongEncryption = _document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128Bit || _document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes; - PdfInteger vValue; - PdfInteger length; - PdfInteger rValue; - if (strongEncryption) { - vValue = new PdfInteger(2); - length = new PdfInteger(128); - rValue = new PdfInteger(3); + 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); + Elements[Keys.V] = new PdfInteger(1); + Elements[Keys.Length] = new PdfInteger(40); + Elements[Keys.R] = new PdfInteger(2); } if (String.IsNullOrEmpty(_userPassword)) @@ -674,9 +670,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; @@ -798,6 +791,62 @@ public static DictionaryMeta Meta static DictionaryMeta _meta; } + /// + /// Predefined keys of crypt filter dictionaries. + /// + internal sealed class CryptFilterKeys + { + /// + /// (Optional) If present, shall be CryptFilter for a crypt filter dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Optional, FixedValue = "CryptFilter")] + public const string Type = "/Type"; + + /// + /// (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. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string CFM = "/CFM"; + + /// + /// (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. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string AuthEvent = "/AuthEvent"; + + /// + /// (Optional) The bit length of the encryption key. It shall be a multiple of 8 in the range of 40 to 128. + /// Security handleres 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) + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string Length = "/Length"; + } + /// /// Gets the KeysMeta of this dictionary type. /// From eedd02bd763a39eee975648e391142fe815cee61 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Fri, 26 Oct 2018 13:06:34 -0600 Subject: [PATCH 10/26] Added crypt filter dictionary in the case of AES-128 encryption --- .../PdfStandardSecurityHandler.cs | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index f416cb1f..e50bc0be 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -629,17 +629,35 @@ public void PrepareEncryption() //#if !SILVERLIGHT Debug.Assert(_document._securitySettings.DocumentSecurityLevel != PdfDocumentSecurityLevel.None); int permissions = (int)Permission; - bool strongEncryption = _document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128Bit - || _document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes; + bool strongEncryption; - if (strongEncryption) + if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) + { + strongEncryption = true; + Elements[Keys.V] = new PdfInteger(4); + // Length is not included for V=4 + Elements[Keys.R] = new PdfInteger(4); + Elements[Keys.StmF] = new PdfName(CryptFilterKeys.StdCF); + Elements[Keys.StrF] = new PdfName(CryptFilterKeys.StdCF); + Elements[Keys.EFF] = new PdfName(CryptFilterKeys.StdCF); + PdfDictionary aesCryptFilter = new PdfDictionary(); + aesCryptFilter.Elements[CryptFilterKeys.Type] = new PdfName("CryptFilter"); + aesCryptFilter.Elements[CryptFilterKeys.CFM] = new PdfName("AESV2"); + aesCryptFilter.Elements[CryptFilterKeys.Length] = new PdfInteger(16); // 128 bits + PdfDictionary cryptFilters = new PdfDictionary(); + cryptFilters.Elements[CryptFilterKeys.StdCF] = aesCryptFilter; + Elements[Keys.CF] = cryptFilters; + } + else if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128Bit) { + strongEncryption = true; Elements[Keys.V] = new PdfInteger(2); Elements[Keys.Length] = new PdfInteger(128); Elements[Keys.R] = new PdfInteger(3); } else { + strongEncryption = false; Elements[Keys.V] = new PdfInteger(1); Elements[Keys.Length] = new PdfInteger(40); Elements[Keys.R] = new PdfInteger(2); @@ -845,6 +863,11 @@ internal sealed class CryptFilterKeys /// [KeyInfo(KeyType.Integer | KeyType.Optional)] public const string Length = "/Length"; + + /// + /// The required crypt filter name for use with the standard security handler. + /// + public const string StdCF = "StdCF"; } /// From 8bb64f7691e4ab6f0415796e69b20a8948347439 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Fri, 26 Oct 2018 13:10:29 -0600 Subject: [PATCH 11/26] Added support for V=4 and R=4 during password validation --- src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index e50bc0be..aaf3aecb 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -251,7 +251,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); @@ -263,8 +263,8 @@ public PasswordValidity ValidatePassword(string inputPassword) if (inputPassword == null) inputPassword = ""; - bool strongEncryption = rValue == 3; - int keyLength = strongEncryption ? 16 : 32; + bool strongEncryption = rValue == 3 || rValue == 4; + int keyLength = strongEncryption ? 16 : 5; // Try owner password first. //byte[] password = PdfEncoders.RawEncoding.GetBytes(inputPassword); From a280114ff82f62523f5408f0088343a42cf6e7e3 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Fri, 26 Oct 2018 13:17:30 -0600 Subject: [PATCH 12/26] Removed test files --- src/SecurityTest/Program.cs | 44 ------------------ src/SecurityTest/Properties/AssemblyInfo.cs | 36 --------------- src/SecurityTest/SecurityTest.csproj | 50 --------------------- 3 files changed, 130 deletions(-) delete mode 100644 src/SecurityTest/Program.cs delete mode 100644 src/SecurityTest/Properties/AssemblyInfo.cs delete mode 100644 src/SecurityTest/SecurityTest.csproj diff --git a/src/SecurityTest/Program.cs b/src/SecurityTest/Program.cs deleted file mode 100644 index 8302eee9..00000000 --- a/src/SecurityTest/Program.cs +++ /dev/null @@ -1,44 +0,0 @@ -using PdfSharp.Pdf; -using PdfSharp.Pdf.IO; -using PdfSharp.Pdf.Security; - -namespace SecurityTest { - class Program { - static void Main(string[] args) { - // Get a fresh copy of the sample PDF file - const string filenameSource = "C:\\Users\\bcallaghan\\Downloads\\Rx Design Guidelines.pdf"; - const string filenameDest = "C:\\Users\\bcallaghan\\Desktop\\Protected.pdf"; - const string filenameEnd = "C:\\Users\\bcallaghan\\Desktop\\Unprotected.pdf"; - - // Open an existing document. Providing an unrequired password is ignored. - PdfDocument document = PdfReader.Open(filenameSource, "some text"); - - // Setting one of the passwords automatically sets the security level to - // PdfDocumentSecurityLevel.Encrypted128Bit. - document.SecuritySettings.UserPassword = "user"; - document.SecuritySettings.OwnerPassword = "owner"; - document.SecuritySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.Encrypted128BitAes; - - // Don't use 40 bit encryption unless needed for compatibility - //securitySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.Encrypted40Bit; - - // Restrict some rights. - document.SecuritySettings.PermitAccessibilityExtractContent = false; - document.SecuritySettings.PermitAnnotations = false; - document.SecuritySettings.PermitAssembleDocument = false; - document.SecuritySettings.PermitExtractContent = false; - document.SecuritySettings.PermitFormsFill = true; - document.SecuritySettings.PermitFullQualityPrint = false; - document.SecuritySettings.PermitModifyDocument = true; - document.SecuritySettings.PermitPrint = false; - - // Save the document... - document.Save(filenameDest); - - // Round-trip to unprotected - //PdfDocument secure = PdfReader.Open(filenameDest, "owner"); - //secure.SecuritySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.None; - //secure.Save(filenameEnd); - } - } -} diff --git a/src/SecurityTest/Properties/AssemblyInfo.cs b/src/SecurityTest/Properties/AssemblyInfo.cs deleted file mode 100644 index 441b5b96..00000000 --- a/src/SecurityTest/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("SecurityTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("SecurityTest")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("e5a6e921-fedd-4564-9997-4a5d4588a555")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/SecurityTest/SecurityTest.csproj b/src/SecurityTest/SecurityTest.csproj deleted file mode 100644 index 959325b0..00000000 --- a/src/SecurityTest/SecurityTest.csproj +++ /dev/null @@ -1,50 +0,0 @@ - - - - - Debug - AnyCPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555} - Exe - SecurityTest - SecurityTest - v2.0 - 512 - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - {5384ce57-1f94-4d22-860d-2e9c1ac12ddf} - PdfSharp-gdi - - - - \ No newline at end of file From 55ac8c1a49250044e0076b2e69d4313ee1aef8b2 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Fri, 26 Oct 2018 13:19:52 -0600 Subject: [PATCH 13/26] Fixed the names of new elements --- src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index aaf3aecb..c1afa8ce 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -641,8 +641,8 @@ public void PrepareEncryption() Elements[Keys.StrF] = new PdfName(CryptFilterKeys.StdCF); Elements[Keys.EFF] = new PdfName(CryptFilterKeys.StdCF); PdfDictionary aesCryptFilter = new PdfDictionary(); - aesCryptFilter.Elements[CryptFilterKeys.Type] = new PdfName("CryptFilter"); - aesCryptFilter.Elements[CryptFilterKeys.CFM] = new PdfName("AESV2"); + aesCryptFilter.Elements[CryptFilterKeys.Type] = new PdfName("/CryptFilter"); + aesCryptFilter.Elements[CryptFilterKeys.CFM] = new PdfName("/AESV2"); aesCryptFilter.Elements[CryptFilterKeys.Length] = new PdfInteger(16); // 128 bits PdfDictionary cryptFilters = new PdfDictionary(); cryptFilters.Elements[CryptFilterKeys.StdCF] = aesCryptFilter; @@ -867,7 +867,7 @@ internal sealed class CryptFilterKeys /// /// The required crypt filter name for use with the standard security handler. /// - public const string StdCF = "StdCF"; + public const string StdCF = "/StdCF"; } /// From b5780443aef76675c56c40cedc3306086c77a5a6 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Fri, 26 Oct 2018 16:18:14 -0600 Subject: [PATCH 14/26] Refactored code style --- .../PdfStandardSecurityHandler.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index c1afa8ce..9c5fcbb7 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -536,14 +536,14 @@ byte[] EncryptAes(byte[] data) aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; aes.BlockSize = 128; // 16 bytes - aes.KeySize = 128; + aes.KeySize = _keySize * 8; aes.Key = _key; using (ICryptoTransform encryptor = aes.CreateEncryptor()) { byte[] encrypted = encryptor.TransformFinalBlock(data, 0, data.Length); byte[] result = new byte[aes.IV.Length + encrypted.Length]; - aes.IV.CopyTo(result, 0); - encrypted.CopyTo(result, aes.IV.Length); + Array.Copy(aes.IV, 0, result, 0, aes.IV.Length); + Array.Copy(encrypted, 0, result, aes.IV.Length, encrypted.Length); return result; } } @@ -560,7 +560,7 @@ byte[] DecryptAes(byte[] encryptedData) aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; aes.BlockSize = 128; // 16 bytes - aes.KeySize = 128; + aes.KeySize = _keySize * 8; aes.Key = _key; // Retrieve the IV from the encrypted data Array.Copy(encryptedData, aes.IV, 16); @@ -592,26 +592,26 @@ internal void SetHashKey(PdfObjectID id) { #if !NETFX_CORE //#if !SILVERLIGHT - byte[] objectId = new byte[9]; - int objectIdLength = 5; - _md5.Initialize(); + byte[] objectId = new byte[5]; // 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.TransformBlock(objectId, 0, objectId.Length, objectId, 0); if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128BitAes) { // Additional padding needed for AES encryption - objectIdLength = 9; - objectId[5] = 0x73; // 's' - objectId[6] = 0x41; // 'A' - objectId[7] = 0x6C; // 'l' - objectId[8] = 0x54; // 'T' + byte[] aesPadding = new byte[] { 0x73, 0x41, 0x6C, 0x54 }; // 'sAlT' + _md5.TransformFinalBlock(aesPadding, 0, aesPadding.Length); + } + else + { + _md5.TransformFinalBlock(objectId, 0, 0); } - _md5.TransformBlock(_encryptionKey, 0, _encryptionKey.Length, _encryptionKey, 0); - _md5.TransformFinalBlock(objectId, 0, objectIdLength); _key = _md5.Hash; _md5.Initialize(); _keySize = _encryptionKey.Length + 5; @@ -639,10 +639,10 @@ public void PrepareEncryption() Elements[Keys.R] = new PdfInteger(4); Elements[Keys.StmF] = new PdfName(CryptFilterKeys.StdCF); Elements[Keys.StrF] = new PdfName(CryptFilterKeys.StdCF); - Elements[Keys.EFF] = new PdfName(CryptFilterKeys.StdCF); PdfDictionary aesCryptFilter = new PdfDictionary(); aesCryptFilter.Elements[CryptFilterKeys.Type] = new PdfName("/CryptFilter"); aesCryptFilter.Elements[CryptFilterKeys.CFM] = new PdfName("/AESV2"); + aesCryptFilter.Elements[CryptFilterKeys.AuthEvent] = new PdfName("/DocOpen"); aesCryptFilter.Elements[CryptFilterKeys.Length] = new PdfInteger(16); // 128 bits PdfDictionary cryptFilters = new PdfDictionary(); cryptFilters.Elements[CryptFilterKeys.StdCF] = aesCryptFilter; From c72f9787bc24c08a09cfe661a8d15997f89a1516 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Mon, 29 Oct 2018 13:34:09 -0600 Subject: [PATCH 15/26] Created a standalone CryptFilterDictionary for convenience --- src/PdfSharp-gdi/PdfSharp-gdi.csproj | 3 + src/PdfSharp-wpf/PdfSharp-wpf.csproj | 3 + .../Pdf.Security/CryptFilterDictionary.cs | 254 ++++++++++++++++++ .../PdfStandardSecurityHandler.cs | 76 +----- src/PdfSharp/PdfSharp.csproj | 1 + 5 files changed, 267 insertions(+), 70 deletions(-) create mode 100644 src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs diff --git a/src/PdfSharp-gdi/PdfSharp-gdi.csproj b/src/PdfSharp-gdi/PdfSharp-gdi.csproj index bc2a24bc..5aa31eea 100644 --- a/src/PdfSharp-gdi/PdfSharp-gdi.csproj +++ b/src/PdfSharp-gdi/PdfSharp-gdi.csproj @@ -870,6 +870,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 123e3bb8..56840b6b 100644 --- a/src/PdfSharp-wpf/PdfSharp-wpf.csproj +++ b/src/PdfSharp-wpf/PdfSharp-wpf.csproj @@ -816,6 +816,9 @@ Pdf.Printing\PdfFilePrinter.cs + + Pdf.Security\CryptFilterDictionary.cs + Pdf.Security\enums\PdfDocumentSecurity.cs diff --git a/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs b/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs new file mode 100644 index 00000000..af4301b4 --- /dev/null +++ b/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs @@ -0,0 +1,254 @@ +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 + { + public CryptFilterDictionary() + { + Type = new PdfName("/CryptFilter"); + } + + /// + /// (Optional) If present, shall be CryptFilter for a crypt filter dictionary. + /// + public PdfName Type + { + get { return Elements[Keys.Type] as PdfName; } + private set { Elements[Keys.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 + { + PdfName cfmName = Elements[Keys.CFM] as PdfName; + switch (cfmName.Value) + { + case "/None": + return CFM.None; + case "/V2": + return CFM.V2; + case "/AESV2": + return CFM.AESV2; + default: + return CFM.Unknown; + } + } + set + { + PdfName cfmName; + switch (value) + { + case CFM.None: + cfmName = new PdfName("/None"); + break; + case CFM.V2: + cfmName = new PdfName("/V2"); + break; + case CFM.AESV2: + cfmName = new PdfName("/AESV2"); + break; + case CFM.Unknown: + default: + throw new ArgumentOutOfRangeException("value", "The CFM must be a valid value."); + } + Elements[Keys.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 + { + PdfName authEventName = Elements[Keys.CFM] as PdfName; + switch (authEventName.Value) + { + case "/DocOpen": + return AuthEvent.DocOpen; + case "/EFOpen": + return AuthEvent.EFOpen; + default: + return AuthEvent.DocOpen; + } + } + set + { + PdfName authEventName; + switch (value) + { + case AuthEvent.DocOpen: + authEventName = new PdfName("/DocOpen"); + break; + case AuthEvent.EFOpen: + authEventName = new PdfName("/EFOpen"); + break; + default: + throw new ArgumentOutOfRangeException("value", "The AuthEvent must be a valid value."); + } + Elements[Keys.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 handleres 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[Keys.Length] as PdfInteger).Value; } + 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[Keys.Length] = new PdfInteger(value); + } + } + + /// + /// Predefined keys of crypt filter dictionaries. + /// + private sealed class Keys + { + /// + /// (Optional) If present, shall be CryptFilter for a crypt filter dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Optional, FixedValue = "CryptFilter")] + public const string Type = "/Type"; + + /// + /// (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. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string CFM = "/CFM"; + + /// + /// (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. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string AuthEvent = "/AuthEvent"; + + /// + /// (Optional) The bit length of the encryption key. It shall be a multiple of 8 in the range of 40 to 128. + /// Security handleres 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) + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string Length = "/Length"; + } + } +} diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index 9c5fcbb7..7306f0ba 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -635,18 +635,15 @@ public void PrepareEncryption() { strongEncryption = true; Elements[Keys.V] = new PdfInteger(4); - // Length is not included for V=4 Elements[Keys.R] = new PdfInteger(4); - Elements[Keys.StmF] = new PdfName(CryptFilterKeys.StdCF); - Elements[Keys.StrF] = new PdfName(CryptFilterKeys.StdCF); - PdfDictionary aesCryptFilter = new PdfDictionary(); - aesCryptFilter.Elements[CryptFilterKeys.Type] = new PdfName("/CryptFilter"); - aesCryptFilter.Elements[CryptFilterKeys.CFM] = new PdfName("/AESV2"); - aesCryptFilter.Elements[CryptFilterKeys.AuthEvent] = new PdfName("/DocOpen"); - aesCryptFilter.Elements[CryptFilterKeys.Length] = new PdfInteger(16); // 128 bits + CryptFilterDictionary aesCryptFilter = new CryptFilterDictionary(); + aesCryptFilter.CFM = CFM.AESV2; + aesCryptFilter.Length = 16; PdfDictionary cryptFilters = new PdfDictionary(); - cryptFilters.Elements[CryptFilterKeys.StdCF] = aesCryptFilter; + 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) { @@ -809,67 +806,6 @@ public static DictionaryMeta Meta static DictionaryMeta _meta; } - /// - /// Predefined keys of crypt filter dictionaries. - /// - internal sealed class CryptFilterKeys - { - /// - /// (Optional) If present, shall be CryptFilter for a crypt filter dictionary. - /// - [KeyInfo(KeyType.Name | KeyType.Optional, FixedValue = "CryptFilter")] - public const string Type = "/Type"; - - /// - /// (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. - /// - [KeyInfo(KeyType.Name | KeyType.Optional)] - public const string CFM = "/CFM"; - - /// - /// (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. - /// - [KeyInfo(KeyType.Name | KeyType.Optional)] - public const string AuthEvent = "/AuthEvent"; - - /// - /// (Optional) The bit length of the encryption key. It shall be a multiple of 8 in the range of 40 to 128. - /// Security handleres 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) - /// - [KeyInfo(KeyType.Integer | KeyType.Optional)] - public const string Length = "/Length"; - - /// - /// The required crypt filter name for use with the standard security handler. - /// - public const string StdCF = "/StdCF"; - } - /// /// Gets the KeysMeta of this dictionary type. /// diff --git a/src/PdfSharp/PdfSharp.csproj b/src/PdfSharp/PdfSharp.csproj index 6b370019..0c6d030c 100644 --- a/src/PdfSharp/PdfSharp.csproj +++ b/src/PdfSharp/PdfSharp.csproj @@ -295,6 +295,7 @@ + From aec1814f2d4d5c2b15e10e5b2d62f475133266ee Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Mon, 29 Oct 2018 13:48:09 -0600 Subject: [PATCH 16/26] Simplified the implementation of the crypt filter dictionary --- .../Pdf.Security/CryptFilterDictionary.cs | 97 +++++-------------- 1 file changed, 22 insertions(+), 75 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs b/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs index af4301b4..a812070d 100644 --- a/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs +++ b/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs @@ -53,18 +53,21 @@ internal enum AuthEvent /// internal class CryptFilterDictionary : PdfDictionary { + /// + /// Constructs a new CryptFilterDictionary with the required Type name. + /// public CryptFilterDictionary() { - Type = new PdfName("/CryptFilter"); + Type = "CryptFilter"; } /// /// (Optional) If present, shall be CryptFilter for a crypt filter dictionary. /// - public PdfName Type + public string Type { - get { return Elements[Keys.Type] as PdfName; } - private set { Elements[Keys.Type] = value; } + get { return Elements.GetName("/Type"); } + private set { Elements.SetName("/Type", value); } } /// @@ -91,8 +94,8 @@ public CFM CFM { get { - PdfName cfmName = Elements[Keys.CFM] as PdfName; - switch (cfmName.Value) + string cfmName = Elements.GetName("/CFM"); + switch (cfmName) { case "/None": return CFM.None; @@ -106,23 +109,23 @@ public CFM CFM } set { - PdfName cfmName; + string cfmName; switch (value) { case CFM.None: - cfmName = new PdfName("/None"); + cfmName = "None"; break; case CFM.V2: - cfmName = new PdfName("/V2"); + cfmName = "V2"; break; case CFM.AESV2: - cfmName = new PdfName("/AESV2"); + cfmName = "AESV2"; break; case CFM.Unknown: default: throw new ArgumentOutOfRangeException("value", "The CFM must be a valid value."); } - Elements[Keys.CFM] = cfmName; + Elements.SetName("/CFM", cfmName); } } @@ -139,8 +142,8 @@ public AuthEvent AuthEvent { get { - PdfName authEventName = Elements[Keys.CFM] as PdfName; - switch (authEventName.Value) + string authEventName = Elements.GetName("/AuthEvent"); + switch (authEventName) { case "/DocOpen": return AuthEvent.DocOpen; @@ -152,19 +155,19 @@ public AuthEvent AuthEvent } set { - PdfName authEventName; + string authEventName; switch (value) { case AuthEvent.DocOpen: - authEventName = new PdfName("/DocOpen"); + authEventName = "DocOpen"; break; case AuthEvent.EFOpen: - authEventName = new PdfName("/EFOpen"); + authEventName = "EFOpen"; break; default: throw new ArgumentOutOfRangeException("value", "The AuthEvent must be a valid value."); } - Elements[Keys.AuthEvent] = authEventName; + Elements.SetName("/AuthEvent", authEventName); } } @@ -176,7 +179,7 @@ public AuthEvent AuthEvent /// public int Length { - get { return (Elements[Keys.Length] as PdfInteger).Value; } + get { return Elements.GetInteger("/Length"); } set { int valueToTest = value; @@ -191,64 +194,8 @@ public int Length if (valueToTest % 8 != 0) throw new ArgumentException("The Length must be a multiple of 8 bits.", "value"); - Elements[Keys.Length] = new PdfInteger(value); + Elements.SetInteger("/Length", value); } } - - /// - /// Predefined keys of crypt filter dictionaries. - /// - private sealed class Keys - { - /// - /// (Optional) If present, shall be CryptFilter for a crypt filter dictionary. - /// - [KeyInfo(KeyType.Name | KeyType.Optional, FixedValue = "CryptFilter")] - public const string Type = "/Type"; - - /// - /// (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. - /// - [KeyInfo(KeyType.Name | KeyType.Optional)] - public const string CFM = "/CFM"; - - /// - /// (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. - /// - [KeyInfo(KeyType.Name | KeyType.Optional)] - public const string AuthEvent = "/AuthEvent"; - - /// - /// (Optional) The bit length of the encryption key. It shall be a multiple of 8 in the range of 40 to 128. - /// Security handleres 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) - /// - [KeyInfo(KeyType.Integer | KeyType.Optional)] - public const string Length = "/Length"; - } } } From 83cea9afb860b117f8b5e09268418a63af2d4f8d Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Mon, 29 Oct 2018 16:46:15 -0600 Subject: [PATCH 17/26] Automatically set Stream length using actual length --- src/PdfSharp/Pdf.IO/PdfReader.cs | 2 +- src/PdfSharp/Pdf.IO/PdfWriter.cs | 11 ++++++----- .../Pdf.Security/PdfStandardSecurityHandler.cs | 2 ++ .../Pdf.Security/enums/PdfDocumentSecurity.cs | 2 +- src/PdfSharp/Pdf/PdfDictionary.cs | 12 ++++++++++-- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/PdfSharp/Pdf.IO/PdfReader.cs b/src/PdfSharp/Pdf.IO/PdfReader.cs index 77c3df74..da3353ce 100644 --- a/src/PdfSharp/Pdf.IO/PdfReader.cs +++ b/src/PdfSharp/Pdf.IO/PdfReader.cs @@ -452,7 +452,7 @@ public static PdfDocument Open(Stream stream, string password, PdfDocumentOpenMo iref.ObjectNumber); } - // Encrypt all objects. + // Decrypt all objects. if (xrefEncrypt != null) { document.SecurityHandler.DecryptDocument(); diff --git a/src/PdfSharp/Pdf.IO/PdfWriter.cs b/src/PdfSharp/Pdf.IO/PdfWriter.cs index 71bec330..58b69e92 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/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index 7306f0ba..65c881bd 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -538,6 +538,8 @@ byte[] EncryptAes(byte[] data) aes.BlockSize = 128; // 16 bytes aes.KeySize = _keySize * 8; aes.Key = _key; + // 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()) { byte[] encrypted = encryptor.TransformFinalBlock(data, 0, data.Length); diff --git a/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs b/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs index a864a4c8..1ba4f15b 100644 --- a/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs +++ b/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs @@ -46,7 +46,7 @@ public enum PdfDocumentSecurityLevel Encrypted40Bit, /// - /// Document is protected with 128-bit security. + /// Document is protected with 128-bit RC4 security. /// Encrypted128Bit, diff --git a/src/PdfSharp/Pdf/PdfDictionary.cs b/src/PdfSharp/Pdf/PdfDictionary.cs index 9788be3e..e07ee7fd 100644 --- a/src/PdfSharp/Pdf/PdfDictionary.cs +++ b/src/PdfSharp/Pdf/PdfDictionary.cs @@ -193,9 +193,17 @@ internal override void WriteObject(PdfWriter writer) 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."); + { + 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); + } #endif #if DEBUG From c51820be576ca6bffc687e679ef9e2c7b109f824 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 31 Oct 2018 10:40:22 -0600 Subject: [PATCH 18/26] Disabled the debug-mode IV --- src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index 65c881bd..b6e688a8 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -539,7 +539,7 @@ byte[] EncryptAes(byte[] data) aes.KeySize = _keySize * 8; aes.Key = _key; // 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 }; + // 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()) { byte[] encrypted = encryptor.TransformFinalBlock(data, 0, data.Length); From 3cdae47d8d6cf078cfa0a7882fa372a9affa7546 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 31 Oct 2018 11:11:22 -0600 Subject: [PATCH 19/26] Added a new error message for unsupported crypt filters --- src/PdfSharp/Resources/Messages.de.restext | 1 + src/PdfSharp/Resources/Messages.restext | 1 + src/PdfSharp/root/PSSR.cs | 5 +++++ src/PdfSharp/root/enums/PSMsgID.cs | 5 +++++ 4 files changed, 12 insertions(+) diff --git a/src/PdfSharp/Resources/Messages.de.restext b/src/PdfSharp/Resources/Messages.de.restext index c463242b..2e2e5554 100644 --- a/src/PdfSharp/Resources/Messages.de.restext +++ b/src/PdfSharp/Resources/Messages.de.restext @@ -18,3 +18,4 @@ 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. diff --git a/src/PdfSharp/Resources/Messages.restext b/src/PdfSharp/Resources/Messages.restext index e47d27ff..7026b0f3 100644 --- a/src/PdfSharp/Resources/Messages.restext +++ b/src/PdfSharp/Resources/Messages.restext @@ -18,3 +18,4 @@ 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. diff --git a/src/PdfSharp/root/PSSR.cs b/src/PdfSharp/root/PSSR.cs index 5ea520d5..ec002770 100644 --- a/src/PdfSharp/root/PSSR.cs +++ b/src/PdfSharp/root/PSSR.cs @@ -334,6 +334,11 @@ 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); } + } + #endregion #region Resource manager diff --git a/src/PdfSharp/root/enums/PSMsgID.cs b/src/PdfSharp/root/enums/PSMsgID.cs index c4f8df64..cedfa119 100644 --- a/src/PdfSharp/root/enums/PSMsgID.cs +++ b/src/PdfSharp/root/enums/PSMsgID.cs @@ -72,5 +72,10 @@ enum PSMsgID /// PSMsgID. /// UnknownEncryption, + + /// + /// PSMsgID. + /// + UnsupportedCryptFilter, } } \ No newline at end of file From 8f2a8aac58c9af6fc2433bd8baeb2ed396b2130e Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 31 Oct 2018 11:15:17 -0600 Subject: [PATCH 20/26] Added a new error message for an unsupported revision number --- src/PdfSharp/Resources/Messages.de.restext | 1 + src/PdfSharp/Resources/Messages.restext | 1 + src/PdfSharp/root/PSSR.cs | 5 +++++ src/PdfSharp/root/enums/PSMsgID.cs | 7 ++++++- 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/PdfSharp/Resources/Messages.de.restext b/src/PdfSharp/Resources/Messages.de.restext index 2e2e5554..c4433a41 100644 --- a/src/PdfSharp/Resources/Messages.de.restext +++ b/src/PdfSharp/Resources/Messages.de.restext @@ -19,3 +19,4 @@ 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 ist mit einer unbekannten Version des Standardsicherheitshandlers geschützt. diff --git a/src/PdfSharp/Resources/Messages.restext b/src/PdfSharp/Resources/Messages.restext index 7026b0f3..c4a467cc 100644 --- a/src/PdfSharp/Resources/Messages.restext +++ b/src/PdfSharp/Resources/Messages.restext @@ -19,3 +19,4 @@ 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 unknown revision of the standard security handler. diff --git a/src/PdfSharp/root/PSSR.cs b/src/PdfSharp/root/PSSR.cs index ec002770..baeb6cc8 100644 --- a/src/PdfSharp/root/PSSR.cs +++ b/src/PdfSharp/root/PSSR.cs @@ -339,6 +339,11 @@ 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 cedfa119..c3951332 100644 --- a/src/PdfSharp/root/enums/PSMsgID.cs +++ b/src/PdfSharp/root/enums/PSMsgID.cs @@ -77,5 +77,10 @@ enum PSMsgID /// PSMsgID. /// UnsupportedCryptFilter, - } + + /// + /// PSMsgID. + /// + UnsupportedRevisionNumber, + } } \ No newline at end of file From 22ec785fdf23efcb1cc898ce36fa5cb2169f84d9 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 31 Oct 2018 11:20:37 -0600 Subject: [PATCH 21/26] Added support for reading documents that use V=4 and crypt filters --- .../Pdf.Security/CryptFilterDictionary.cs | 8 ++++- .../PdfStandardSecurityHandler.cs | 29 +++++++++++++++++-- src/PdfSharp/Resources/Messages.de.restext | 2 +- src/PdfSharp/Resources/Messages.restext | 2 +- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs b/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs index a812070d..9ec50bb8 100644 --- a/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs +++ b/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs @@ -54,13 +54,19 @@ internal enum AuthEvent internal class CryptFilterDictionary : PdfDictionary { /// - /// Constructs a new CryptFilterDictionary with the required Type name. + /// 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. /// diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index b6e688a8..2adea80f 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -263,8 +263,31 @@ public PasswordValidity ValidatePassword(string inputPassword) if (inputPassword == null) inputPassword = ""; - bool strongEncryption = rValue == 3 || rValue == 4; - int keyLength = strongEncryption ? 16 : 5; + 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); @@ -565,7 +588,7 @@ byte[] DecryptAes(byte[] encryptedData) aes.KeySize = _keySize * 8; aes.Key = _key; // Retrieve the IV from the encrypted data - Array.Copy(encryptedData, aes.IV, 16); + Array.Copy(encryptedData, 0, aes.IV, 0, 16); using (ICryptoTransform decryptor = aes.CreateDecryptor()) { byte[] decrypted = decryptor.TransformFinalBlock(encryptedData, 16, encryptedData.Length - 16); diff --git a/src/PdfSharp/Resources/Messages.de.restext b/src/PdfSharp/Resources/Messages.de.restext index c4433a41..150cdf22 100644 --- a/src/PdfSharp/Resources/Messages.de.restext +++ b/src/PdfSharp/Resources/Messages.de.restext @@ -19,4 +19,4 @@ 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 ist mit einer unbekannten Version des Standardsicherheitshandlers geschützt. +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 c4a467cc..79efdc80 100644 --- a/src/PdfSharp/Resources/Messages.restext +++ b/src/PdfSharp/Resources/Messages.restext @@ -19,4 +19,4 @@ 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 unknown revision of the standard security handler. +UnsupportedRevisionNumber = The PDF document is protected with an unsupported revision of the standard security handler. From 8895db7a85757c1583f9bb78eccfc8c77c553ba9 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 31 Oct 2018 11:35:51 -0600 Subject: [PATCH 22/26] Fixed the retrieval of the IV from encrypted data --- src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index 2adea80f..7530b057 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -560,10 +560,9 @@ byte[] EncryptAes(byte[] data) aes.Padding = PaddingMode.PKCS7; aes.BlockSize = 128; // 16 bytes aes.KeySize = _keySize * 8; - aes.Key = _key; // 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()) + 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]; @@ -586,10 +585,10 @@ byte[] DecryptAes(byte[] encryptedData) aes.Padding = PaddingMode.PKCS7; aes.BlockSize = 128; // 16 bytes aes.KeySize = _keySize * 8; - aes.Key = _key; // Retrieve the IV from the encrypted data - Array.Copy(encryptedData, 0, aes.IV, 0, 16); - using (ICryptoTransform decryptor = aes.CreateDecryptor()) + 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; From 85be61c95ba754c91be5f7bb860ea79990173d20 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 31 Oct 2018 13:02:25 -0600 Subject: [PATCH 23/26] Added a guard to avoid decrypting the XRef stream --- src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs index 7530b057..e7cd1613 100644 --- a/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs +++ b/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -110,7 +110,7 @@ public void DecryptDocument() { foreach (PdfReference iref in _document._irefTable.AllReferences) { - if (!ReferenceEquals(iref.Value, this)) + if (iref.ObjectID != this.ObjectID) DecryptObject(iref.Value); } } @@ -149,7 +149,9 @@ internal void DecryptObject(PdfObject value) /// 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; From 05ee3722b85bc816418c73e3127b445cd57ad20a Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 31 Oct 2018 13:43:17 -0600 Subject: [PATCH 24/26] Reset the solution file back to its original state --- src/BuildAll-PdfSharp.sln | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/src/BuildAll-PdfSharp.sln b/src/BuildAll-PdfSharp.sln index a4cd8ddd..51818cd7 100644 --- a/src/BuildAll-PdfSharp.sln +++ b/src/BuildAll-PdfSharp.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28010.2046 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PDFsharp", "PdfSharp\PDFsharp.csproj", "{5A6055BC-BF86-4FDD-9F62-0109DB7A303B}" EndProject @@ -14,8 +14,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfSharp-wpf", "PdfSharp-wp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfSharp.Charting-wpf", "PdfSharp.Charting-wpf\PdfSharp.Charting-wpf.csproj", "{E6A2734E-0CD6-4210-8AEC-47EE348F8D78}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SecurityTest", "SecurityTest\SecurityTest.csproj", "{E5A6E921-FEDD-4564-9997-4A5D4588A555}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -128,35 +126,8 @@ Global {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|Win32.ActiveCfg = Release|Any CPU {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|x64.ActiveCfg = Release|Any CPU {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|x86.ActiveCfg = Release|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|ARM.ActiveCfg = Debug|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|ARM.Build.0 = Debug|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|Win32.ActiveCfg = Debug|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|Win32.Build.0 = Debug|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|x64.ActiveCfg = Debug|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|x64.Build.0 = Debug|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|x86.ActiveCfg = Debug|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Debug|x86.Build.0 = Debug|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|Any CPU.Build.0 = Release|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|ARM.ActiveCfg = Release|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|ARM.Build.0 = Release|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|Win32.ActiveCfg = Release|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|Win32.Build.0 = Release|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|x64.ActiveCfg = Release|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|x64.Build.0 = Release|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|x86.ActiveCfg = Release|Any CPU - {E5A6E921-FEDD-4564-9997-4A5D4588A555}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {B225FEBB-A4FF-4EAF-91A8-C737C60E8318} - EndGlobalSection EndGlobal From c4c55f34174da74a87883b3937e56dbfa2623119 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Mon, 5 Nov 2018 14:27:42 -0700 Subject: [PATCH 25/26] Fixed a typo in the comments --- src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs b/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs index 9ec50bb8..9b8f85b3 100644 --- a/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs +++ b/src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs @@ -179,9 +179,9 @@ public AuthEvent AuthEvent /// /// (Optional) The bit length of the encryption key. It shall be a multiple of 8 in the range of 40 to 128. - /// Security handleres may define their own use of the Length entry and should use it to define the bit length + /// 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) + /// and public-key security handler express it as is (128 means 128). /// public int Length { From 8850ad39bddc3e01a08e82f27d86a1a97db17ae5 Mon Sep 17 00:00:00 2001 From: Ben Callaghan Date: Wed, 24 Jul 2019 13:20:20 -0600 Subject: [PATCH 26/26] Removed incorrect #if DEBUG --- src/PdfSharp/Pdf/PdfDictionary.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/PdfSharp/Pdf/PdfDictionary.cs b/src/PdfSharp/Pdf/PdfDictionary.cs index ecbd007b..538e9df2 100644 --- a/src/PdfSharp/Pdf/PdfDictionary.cs +++ b/src/PdfSharp/Pdf/PdfDictionary.cs @@ -192,7 +192,6 @@ internal override void WriteObject(PdfWriter writer) //int count = Elements.Count; PdfName[] keys = Elements.KeyNames; -#if DEBUG if (_stream != null) { if (writer.SecurityHandler != null) @@ -204,7 +203,6 @@ internal override void WriteObject(PdfWriter writer) } Elements[PdfStream.Keys.Length] = new PdfInteger(_stream.Length); } -#endif #if DEBUG // Sort keys for debugging purposes. Comparing PDF files with for example programs like