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