git-svn-id: https://pdfsharp.svn.codeplex.com/svn@39620 56d0cb2f-6006-4f69-a5a2-94740...
[pdfsharp:pdfsharp.git] / PDFsharp / code / PdfSharp / PdfSharp.Pdf.Security / PdfStandardSecurityHandler.cs
1 #region PDFsharp - A .NET library for processing PDF\r
2 //\r
3 // Authors:\r
4 //   Stefan Lange (mailto:Stefan.Lange@pdfsharp.com)\r
5 //\r
6 // Copyright (c) 2005-2009 empira Software GmbH, Cologne (Germany)\r
7 //\r
8 // http://www.pdfsharp.com\r
9 // http://sourceforge.net/projects/pdfsharp\r
10 //\r
11 // Permission is hereby granted, free of charge, to any person obtaining a\r
12 // copy of this software and associated documentation files (the "Software"),\r
13 // to deal in the Software without restriction, including without limitation\r
14 // the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
15 // and/or sell copies of the Software, and to permit persons to whom the\r
16 // Software is furnished to do so, subject to the following conditions:\r
17 //\r
18 // The above copyright notice and this permission notice shall be included\r
19 // in all copies or substantial portions of the Software.\r
20 //\r
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
24 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
26 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER \r
27 // DEALINGS IN THE SOFTWARE.\r
28 #endregion\r
29 \r
30 using System;\r
31 using System.Collections.Generic;\r
32 using System.Diagnostics;\r
33 using System.Collections;\r
34 using PdfSharp.Drawing;\r
35 using PdfSharp.Internal;\r
36 using PdfSharp.Pdf;\r
37 using PdfSharp.Pdf.IO;\r
38 using PdfSharp.Pdf.Advanced;\r
39 using PdfSharp.Pdf.Internal;\r
40 using System.Security.Cryptography;\r
41 \r
42 namespace PdfSharp.Pdf.Security\r
43 {\r
44   /// <summary>\r
45   /// Represents the standard PDF security handler.\r
46   /// </summary>\r
47   public sealed class PdfStandardSecurityHandler : PdfSecurityHandler\r
48   {\r
49     internal PdfStandardSecurityHandler(PdfDocument document)\r
50       : base(document)\r
51     { }\r
52 \r
53     internal PdfStandardSecurityHandler(PdfDictionary dict)\r
54       : base(dict)\r
55     { }\r
56 \r
57     /// <summary>\r
58     /// Sets the user password of the document. Setting a password automatically sets the\r
59     /// PdfDocumentSecurityLevel to PdfDocumentSecurityLevel.Encrypted128Bit if its current\r
60     /// value is PdfDocumentSecurityLevel.None.\r
61     /// </summary>\r
62     public string UserPassword\r
63     {\r
64       set\r
65       {\r
66         if (this.document.securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.None)\r
67           this.document.securitySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.Encrypted128Bit;\r
68         this.userPassword = value;\r
69       }\r
70     }\r
71     internal string userPassword;\r
72 \r
73     /// <summary>\r
74     /// Sets the owner password of the document. Setting a password automatically sets the\r
75     /// PdfDocumentSecurityLevel to PdfDocumentSecurityLevel.Encrypted128Bit if its current\r
76     /// value is PdfDocumentSecurityLevel.None.\r
77     /// </summary>\r
78     public string OwnerPassword\r
79     {\r
80       set\r
81       {\r
82         if (this.document.securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.None)\r
83           this.document.securitySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.Encrypted128Bit;\r
84         this.ownerPassword = value;\r
85       }\r
86     }\r
87     internal string ownerPassword;\r
88 \r
89     /// <summary>\r
90     /// Gets or sets the user access permission represented as an integer in the P key.\r
91     /// </summary>\r
92     internal PdfUserAccessPermission Permission\r
93     {\r
94       get\r
95       {\r
96         PdfUserAccessPermission permission = (PdfUserAccessPermission)Elements.GetInteger(Keys.P);\r
97         if ((int)permission == 0)\r
98           permission = PdfUserAccessPermission.PermitAll;\r
99         return permission;\r
100       }\r
101       set { Elements.SetInteger(Keys.P, (int)value); }\r
102     }\r
103 \r
104     /// <summary>\r
105     /// Encrypts the whole document.\r
106     /// </summary>\r
107     public void EncryptDocument()\r
108     {\r
109       foreach (PdfReference iref in this.document.irefTable.AllReferences)\r
110       {\r
111         if (!ReferenceEquals(iref.Value, this))\r
112           EncryptObject(iref.Value);\r
113       }\r
114     }\r
115 \r
116     /// <summary>\r
117     /// Encrypts an indirect object.\r
118     /// </summary>\r
119     internal void EncryptObject(PdfObject value)\r
120     {\r
121       Debug.Assert(value.Reference != null);\r
122 \r
123       SetHashKey(value.ObjectID);\r
124 #if DEBUG\r
125       if (value.ObjectID.ObjectNumber == 10)\r
126         GetType();\r
127 #endif\r
128 \r
129       PdfDictionary dict;\r
130       PdfArray array;\r
131       PdfStringObject str;\r
132       if ((dict = value as PdfDictionary) != null)\r
133         EncryptDictionary(dict);\r
134       else if ((array = value as PdfArray) != null)\r
135         EncryptArray(array);\r
136       else if ((str = value as PdfStringObject) != null)\r
137       {\r
138         if (str.Length != 0)\r
139         {\r
140           byte[] bytes = str.EncryptionValue;\r
141           PrepareKey();\r
142           EncryptRC4(bytes);\r
143           str.EncryptionValue = bytes;\r
144         }\r
145       }\r
146     }\r
147 \r
148     /// <summary>\r
149     /// Encrypts a dictionary.\r
150     /// </summary>\r
151     void EncryptDictionary(PdfDictionary dict)\r
152     {\r
153       PdfName[] names = dict.Elements.KeyNames;\r
154       foreach (KeyValuePair<string, PdfItem> item in dict.Elements)\r
155       {\r
156         PdfString value1;\r
157         PdfDictionary value2;\r
158         PdfArray value3;\r
159         if ((value1 = item.Value as PdfString) != null)\r
160           EncryptString(value1);\r
161         else if ((value2 = item.Value as PdfDictionary) != null)\r
162           EncryptDictionary(value2);\r
163         else if ((value3 = item.Value as PdfArray) != null)\r
164           EncryptArray(value3);\r
165       }\r
166       if (dict.Stream != null)\r
167       {\r
168         byte[] bytes = dict.Stream.Value;\r
169         if (bytes.Length != 0)\r
170         {\r
171           PrepareKey();\r
172           EncryptRC4(bytes);\r
173           dict.Stream.Value = bytes;\r
174         }\r
175       }\r
176     }\r
177 \r
178     /// <summary>\r
179     /// Encrypts an array.\r
180     /// </summary>\r
181     void EncryptArray(PdfArray array)\r
182     {\r
183       int count = array.Elements.Count;\r
184       for (int idx = 0; idx < count; idx++)\r
185       {\r
186         PdfItem item = array.Elements[idx];\r
187         PdfString value1;\r
188         PdfDictionary value2;\r
189         PdfArray value3;\r
190         if ((value1 = item as PdfString) != null)\r
191           EncryptString(value1);\r
192         else if ((value2 = item as PdfDictionary) != null)\r
193           EncryptDictionary(value2);\r
194         else if ((value3 = item as PdfArray) != null)\r
195           EncryptArray(value3);\r
196       }\r
197     }\r
198 \r
199     /// <summary>\r
200     /// Encrypts a string.\r
201     /// </summary>\r
202     void EncryptString(PdfString value)\r
203     {\r
204       if (value.Length != 0)\r
205       {\r
206         byte[] bytes = value.EncryptionValue;\r
207         PrepareKey();\r
208         EncryptRC4(bytes);\r
209         value.EncryptionValue = bytes;\r
210       }\r
211     }\r
212 \r
213     /// <summary>\r
214     /// Encrypts an array.\r
215     /// </summary>\r
216     internal byte[] EncryptBytes(byte[] bytes)\r
217     {\r
218       if (bytes != null && bytes.Length != 0)\r
219       {\r
220         PrepareKey();\r
221         EncryptRC4(bytes);\r
222       }\r
223       return bytes;\r
224     }\r
225 \r
226     #region Encryption Algorithms\r
227 \r
228     /// <summary>\r
229     /// Checks the password.\r
230     /// </summary>\r
231     /// <param name="inputPassword">Password or null if no password is provided.</param>\r
232     public PasswordValidity ValidatePassword(string inputPassword)\r
233     {\r
234       // We can handle 40 and 128 bit standard encryption\r
235       string filter = Elements.GetName(PdfSecurityHandler.Keys.Filter);\r
236       int v = Elements.GetInteger(PdfSecurityHandler.Keys.V);\r
237       if (filter != "/Standard" || !(v >= 1 && v <= 3))\r
238         throw new PdfReaderException(PSSR.UnknownEncryption);\r
239 \r
240       byte[] documentID = PdfEncoders.RawEncoding.GetBytes(Owner.Internals.FirstDocumentID);\r
241       byte[] oValue = PdfEncoders.RawEncoding.GetBytes(Elements.GetString(Keys.O));\r
242       byte[] uValue = PdfEncoders.RawEncoding.GetBytes(Elements.GetString(Keys.U));\r
243       int pValue = Elements.GetInteger(Keys.P);\r
244       int rValue = Elements.GetInteger(Keys.R);\r
245 \r
246       if (inputPassword == null)\r
247         inputPassword = "";\r
248 \r
249       bool strongEncryption = rValue == 3;\r
250       int keyLength = strongEncryption ? 16 : 32;\r
251 \r
252 #if true\r
253       // Try owner password first\r
254       byte[] password = PdfEncoders.RawEncoding.GetBytes(inputPassword);\r
255       InitWidhOwnerPassword(documentID, inputPassword, oValue, pValue, strongEncryption);\r
256       if (EqualsKey(uValue, keyLength))\r
257       {\r
258         this.document.SecuritySettings.hasOwnerPermissions = true;\r
259         return PasswordValidity.OwnerPassword;\r
260       }\r
261       this.document.SecuritySettings.hasOwnerPermissions = false;\r
262 \r
263       // Now try user password\r
264       password = PdfEncoders.RawEncoding.GetBytes(inputPassword);\r
265       InitWidhUserPassword(documentID, inputPassword, oValue, pValue, strongEncryption);\r
266       if (!EqualsKey(uValue, keyLength))\r
267         return PasswordValidity.Invalid;\r
268       return PasswordValidity.UserPassword;\r
269 #else\r
270       password = PdfEncoders.RawEncoding.GetBytes(inputPassword);\r
271       InitWidhUserPassword(documentID, inputPassword, oValue, pValue, strongEncryption);\r
272 \r
273       this.document.SecuritySettings.hasOwnerPermissions = false;\r
274 \r
275       if (!EqualsKey(uValue, keyLength))\r
276       {\r
277         password = PdfEncoders.RawEncoding.GetBytes(inputPassword);\r
278 \r
279         // Compare owner password\r
280         InitWidhOwnerPassword(documentID, inputPassword, oValue, pValue, strongEncryption);\r
281 \r
282         if (!EqualsKey(uValue, keyLength))\r
283         {\r
284           //Compare user password\r
285           InitWidhUserPassword(documentID, inputPassword, oValue, pValue, strongEncryption);\r
286           if (!EqualsKey(uValue, keyLength))\r
287             return 0;\r
288           return 1;\r
289         }\r
290         this.document.SecuritySettings.hasOwnerPermissions = true;\r
291         return 2;\r
292       }\r
293       return 1;\r
294 #endif\r
295     }\r
296 \r
297     [Conditional("DEBUG")]\r
298     static void DumpBytes(string tag, byte[] bytes)\r
299     {\r
300       string dump = tag + ": ";\r
301       for (int idx = 0; idx < bytes.Length; idx++)\r
302         dump += String.Format("{0:X2}", bytes[idx]);\r
303       Debug.WriteLine(dump);\r
304     }\r
305 \r
306     /// <summary>\r
307     /// Pads a password to a 32 byte array.\r
308     /// </summary>\r
309     static byte[] PadPassword(string password)\r
310     {\r
311       byte[] padded = new byte[32];\r
312       if (password == null)\r
313         Array.Copy(passwordPadding, 0, padded, 0, 32);\r
314       else\r
315       {\r
316         int length = password.Length;\r
317         Array.Copy(PdfEncoders.RawEncoding.GetBytes(password), 0, padded, 0, Math.Min(length, 32));\r
318         if (length < 32)\r
319           Array.Copy(passwordPadding, 0, padded, length, 32 - length);\r
320       }\r
321       return padded;\r
322     }\r
323     static byte[] passwordPadding = // 32 bytes password padding defined by Adobe\r
324     {\r
325       0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,\r
326       0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A,\r
327     };\r
328 \r
329     /// <summary>\r
330     /// Generates the user key based on the padded user password.\r
331     /// </summary>\r
332     void InitWidhUserPassword(byte[] documentID, string userPassword, byte[] ownerKey, int permissions, bool strongEncryption)\r
333     {\r
334       InitEncryptionKey(documentID, PadPassword(userPassword), ownerKey, permissions, strongEncryption);\r
335       SetupUserKey(documentID);\r
336     }\r
337 \r
338     /// <summary>\r
339     /// Generates the user key based on the padded owner password.\r
340     /// </summary>\r
341     void InitWidhOwnerPassword(byte[] documentID, string ownerPassword, byte[] ownerKey, int permissions, bool strongEncryption)\r
342     {\r
343       byte[] userPad = ComputeOwnerKey(ownerKey, PadPassword(ownerPassword), strongEncryption);\r
344       InitEncryptionKey(documentID, userPad, ownerKey, permissions, strongEncryption);\r
345       SetupUserKey(documentID);\r
346     }\r
347 \r
348     /// <summary>\r
349     /// Computes the padded user password from the padded owner password.\r
350     /// </summary>\r
351     byte[] ComputeOwnerKey(byte[] userPad, byte[] ownerPad, bool strongEncryption)\r
352     {\r
353       byte[] ownerKey = new byte[32];\r
354 #if !SILVERLIGHT\r
355       byte[] digest = this.md5.ComputeHash(ownerPad);\r
356       if (strongEncryption)\r
357       {\r
358         byte[] mkey = new byte[16];\r
359         // Hash the pad 50 times\r
360         for (int idx = 0; idx < 50; idx++)\r
361           digest = this.md5.ComputeHash(digest);\r
362         Array.Copy(userPad, 0, ownerKey, 0, 32);\r
363         // Encrypt the key\r
364         for (int i = 0; i < 20; i++)\r
365         {\r
366           for (int j = 0; j < mkey.Length; ++j)\r
367             mkey[j] = (byte)(digest[j] ^ i);\r
368           PrepareRC4Key(mkey);\r
369           EncryptRC4(ownerKey);\r
370         }\r
371       }\r
372       else\r
373       {\r
374         PrepareRC4Key(digest, 0, 5);\r
375         EncryptRC4(userPad, ownerKey);\r
376       }\r
377 #endif\r
378       return ownerKey;\r
379     }\r
380 \r
381     /// <summary>\r
382     /// Computes the encryption key.\r
383     /// </summary>\r
384     void InitEncryptionKey(byte[] documentID, byte[] userPad, byte[] ownerKey, int permissions, bool strongEncryption)\r
385     {\r
386 #if !SILVERLIGHT\r
387       this.ownerKey = ownerKey;\r
388       this.encryptionKey = new byte[strongEncryption ? 16 : 5];\r
389 \r
390       this.md5.Initialize();\r
391       this.md5.TransformBlock(userPad, 0, userPad.Length, userPad, 0);\r
392       this.md5.TransformBlock(ownerKey, 0, ownerKey.Length, ownerKey, 0);\r
393 \r
394       // Split permission into 4 bytes\r
395       byte[] permission = new byte[4];\r
396       permission[0] = (byte)permissions;\r
397       permission[1] = (byte)(permissions >> 8);\r
398       permission[2] = (byte)(permissions >> 16);\r
399       permission[3] = (byte)(permissions >> 24);\r
400       this.md5.TransformBlock(permission, 0, 4, permission, 0);\r
401       this.md5.TransformBlock(documentID, 0, documentID.Length, documentID, 0);\r
402       this.md5.TransformFinalBlock(permission, 0, 0);\r
403       byte[] digest = this.md5.Hash;\r
404       this.md5.Initialize();\r
405       // Create the hash 50 times (only for 128 bit)\r
406       if (this.encryptionKey.Length == 16)\r
407       {\r
408         for (int idx = 0; idx < 50; idx++)\r
409         {\r
410           digest = this.md5.ComputeHash(digest);\r
411           this.md5.Initialize();\r
412         }\r
413       }\r
414       Array.Copy(digest, 0, this.encryptionKey, 0, this.encryptionKey.Length);\r
415 #endif\r
416     }\r
417 \r
418     /// <summary>\r
419     /// Computes the user key.\r
420     /// </summary>\r
421     void SetupUserKey(byte[] documentID)\r
422     {\r
423 #if !SILVERLIGHT\r
424       if (this.encryptionKey.Length == 16)\r
425       {\r
426         this.md5.TransformBlock(passwordPadding, 0, passwordPadding.Length, passwordPadding, 0);\r
427         this.md5.TransformFinalBlock(documentID, 0, documentID.Length);\r
428         byte[] digest = this.md5.Hash;\r
429         this.md5.Initialize();\r
430         Array.Copy(digest, 0, this.userKey, 0, 16);\r
431         for (int idx = 16; idx < 32; idx++)\r
432           this.userKey[idx] = 0;\r
433         //Encrypt the key\r
434         for (int i = 0; i < 20; i++)\r
435         {\r
436           for (int j = 0; j < this.encryptionKey.Length; j++)\r
437             digest[j] = (byte)(this.encryptionKey[j] ^ i);\r
438           PrepareRC4Key(digest, 0, this.encryptionKey.Length);\r
439           EncryptRC4(this.userKey, 0, 16);\r
440         }\r
441       }\r
442       else\r
443       {\r
444         PrepareRC4Key(this.encryptionKey);\r
445         EncryptRC4(passwordPadding, this.userKey);\r
446       }\r
447 #endif\r
448     }\r
449 \r
450     /// <summary>\r
451     /// Prepare the encryption key.\r
452     /// </summary>\r
453     void PrepareKey()\r
454     {\r
455       PrepareRC4Key(this.key, 0, this.keySize);\r
456     }\r
457 \r
458     /// <summary>\r
459     /// Prepare the encryption key.\r
460     /// </summary>\r
461     void PrepareRC4Key(byte[] key)\r
462     {\r
463       PrepareRC4Key(key, 0, key.Length);\r
464     }\r
465 \r
466     /// <summary>\r
467     /// Prepare the encryption key.\r
468     /// </summary>\r
469     void PrepareRC4Key(byte[] key, int offset, int length)\r
470     {\r
471       int idx1 = 0;\r
472       int idx2 = 0;\r
473       for (int idx = 0; idx < 256; idx++)\r
474         this.state[idx] = (byte)idx;\r
475       byte tmp;\r
476       for (int idx = 0; idx < 256; idx++)\r
477       {\r
478         idx2 = (key[idx1 + offset] + this.state[idx] + idx2) & 255;\r
479         tmp = this.state[idx];\r
480         this.state[idx] = this.state[idx2];\r
481         this.state[idx2] = tmp;\r
482         idx1 = (idx1 + 1) % length;\r
483       }\r
484     }\r
485 \r
486     /// <summary>\r
487     /// Encrypts the data.\r
488     /// </summary>\r
489     void EncryptRC4(byte[] data)\r
490     {\r
491       EncryptRC4(data, 0, data.Length, data);\r
492     }\r
493 \r
494     /// <summary>\r
495     /// Encrypts the data.\r
496     /// </summary>\r
497     void EncryptRC4(byte[] data, int offset, int length)\r
498     {\r
499       EncryptRC4(data, offset, length, data);\r
500     }\r
501 \r
502     /// <summary>\r
503     /// Encrypts the data.\r
504     /// </summary>\r
505     void EncryptRC4(byte[] inputData, byte[] outputData)\r
506     {\r
507       EncryptRC4(inputData, 0, inputData.Length, outputData);\r
508     }\r
509 \r
510     /// <summary>\r
511     /// Encrypts the data.\r
512     /// </summary>\r
513     void EncryptRC4(byte[] inputData, int offset, int length, byte[] outputData)\r
514     {\r
515       length += offset;\r
516       int x = 0, y = 0;\r
517       byte b;\r
518       for (int idx = offset; idx < length; idx++)\r
519       {\r
520         x = (x + 1) & 255;\r
521         y = (this.state[x] + y) & 255;\r
522         b = this.state[x];\r
523         this.state[x] = this.state[y];\r
524         this.state[y] = b;\r
525         outputData[idx] = (byte)(inputData[idx] ^ state[(this.state[x] + this.state[y]) & 255]);\r
526       }\r
527     }\r
528 \r
529     /// <summary>\r
530     /// Checks whether the calculated key correct.\r
531     /// </summary>\r
532     bool EqualsKey(byte[] value, int length)\r
533     {\r
534       for (int idx = 0; idx < length; idx++)\r
535       {\r
536         if (this.userKey[idx] != value[idx])\r
537           return false;\r
538       }\r
539       return true;\r
540     }\r
541 \r
542     /// <summary>\r
543     /// Set the hash key for the specified object.\r
544     /// </summary>\r
545     internal void SetHashKey(PdfObjectID id)\r
546     {\r
547 #if !SILVERLIGHT\r
548       byte[] objectId = new byte[5];\r
549       this.md5.Initialize();\r
550       // Split the object number and generation\r
551       objectId[0] = (byte)id.ObjectNumber;\r
552       objectId[1] = (byte)(id.ObjectNumber >> 8);\r
553       objectId[2] = (byte)(id.ObjectNumber >> 16);\r
554       objectId[3] = (byte)id.GenerationNumber;\r
555       objectId[4] = (byte)(id.GenerationNumber >> 8);\r
556       this.md5.TransformBlock(this.encryptionKey, 0, this.encryptionKey.Length, this.encryptionKey, 0);\r
557       this.md5.TransformFinalBlock(objectId, 0, objectId.Length);\r
558       this.key = this.md5.Hash;\r
559       this.md5.Initialize();\r
560       this.keySize = this.encryptionKey.Length + 5;\r
561       if (this.keySize > 16)\r
562         this.keySize = 16;\r
563 #endif\r
564     }\r
565 \r
566     /// <summary>\r
567     /// Prepares the security handler for encrypting the document.\r
568     /// </summary>\r
569     public void PrepareEncryption()\r
570     {\r
571 #if !SILVERLIGHT\r
572       Debug.Assert(this.document.securitySettings.DocumentSecurityLevel != PdfDocumentSecurityLevel.None);\r
573       int permissions = (int)this.Permission;\r
574       bool strongEncryption = this.document.securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128Bit;\r
575 \r
576       PdfInteger vValue;\r
577       PdfInteger length;\r
578       PdfInteger rValue;\r
579 \r
580       if (strongEncryption)\r
581       {\r
582         vValue = new PdfInteger(2);\r
583         length = new PdfInteger(128);\r
584         rValue = new PdfInteger(3);\r
585       }\r
586       else\r
587       {\r
588         vValue = new PdfInteger(1);\r
589         length = new PdfInteger(40);\r
590         rValue = new PdfInteger(2);\r
591       }\r
592 \r
593       if (String.IsNullOrEmpty(this.userPassword))\r
594         this.userPassword = "";\r
595       // Use user password twice if no owner password provided.\r
596       if (String.IsNullOrEmpty(this.ownerPassword))\r
597         this.ownerPassword = this.userPassword;\r
598 \r
599       // Correct permission bits\r
600       permissions |= (int)(strongEncryption ? (uint)0xfffff0c0 : (uint)0xffffffc0);\r
601       permissions &= unchecked((int)0xfffffffc);\r
602 \r
603       PdfInteger pValue = new PdfInteger(permissions);\r
604 \r
605       Debug.Assert(this.ownerPassword.Length > 0, "Empty owner password.");\r
606       byte[] userPad = PadPassword(this.userPassword);\r
607       byte[] ownerPad = PadPassword(this.ownerPassword);\r
608 \r
609       this.md5.Initialize();\r
610       this.ownerKey = ComputeOwnerKey(userPad, ownerPad, strongEncryption);\r
611       byte[] documentID = PdfEncoders.RawEncoding.GetBytes(this.document.Internals.FirstDocumentID);\r
612       InitWidhUserPassword(documentID, this.userPassword, this.ownerKey, permissions, strongEncryption);\r
613 \r
614       PdfString oValue = new PdfString(PdfEncoders.RawEncoding.GetString(this.ownerKey));\r
615       PdfString uValue = new PdfString(PdfEncoders.RawEncoding.GetString(this.userKey));\r
616 \r
617       Elements[Keys.Filter] = new PdfName("/Standard");\r
618       Elements[Keys.V] = vValue;\r
619       Elements[Keys.Length] = length;\r
620       Elements[Keys.R] = rValue;\r
621       Elements[Keys.O] = oValue;\r
622       Elements[Keys.U] = uValue;\r
623       Elements[Keys.P] = pValue;\r
624 #endif\r
625     }\r
626 \r
627     /// <summary>\r
628     /// The global encryption key.\r
629     /// </summary>\r
630     byte[] encryptionKey;\r
631 \r
632 #if !SILVERLIGHT\r
633     /// <summary>\r
634     /// The message digest algorithm MD5.\r
635     /// </summary>\r
636     MD5 md5 = new MD5CryptoServiceProvider();\r
637 #endif\r
638 \r
639     /// <summary>\r
640     /// Bytes used for RC4 encryption.\r
641     /// </summary>\r
642     byte[] state = new byte[256];\r
643 \r
644     /// <summary>\r
645     /// The encryption key for the owner.\r
646     /// </summary>\r
647     byte[] ownerKey = new byte[32];\r
648 \r
649     /// <summary>\r
650     /// The encryption key for the user.\r
651     /// </summary>\r
652     byte[] userKey = new byte[32];\r
653 \r
654     /// <summary>\r
655     /// The encryption key for a particular object/generation.\r
656     /// </summary>\r
657     byte[] key;\r
658 \r
659     /// <summary>\r
660     /// The encryption key length for a particular object/generation.\r
661     /// </summary>\r
662     int keySize;\r
663 \r
664     #endregion\r
665 \r
666     internal override void WriteObject(PdfWriter writer)\r
667     {\r
668       // Don't encypt myself\r
669       PdfStandardSecurityHandler securityHandler = writer.SecurityHandler;\r
670       writer.SecurityHandler = null;\r
671       base.WriteObject(writer);\r
672       writer.SecurityHandler = securityHandler;\r
673     }\r
674 \r
675     #region Keys\r
676     /// <summary>\r
677     /// Predefined keys of this dictionary.\r
678     /// </summary>\r
679     internal sealed new class Keys : PdfSecurityHandler.Keys\r
680     {\r
681       /// <summary>\r
682       /// (Required) A number specifying which revision of the standard security handler\r
683       /// should be used to interpret this dictionary:\r
684       /// \95 2 if the document is encrypted with a V value less than 2 and does not have any of\r
685       ///   the access permissions set (by means of the P entry, below) that are designated \r
686       ///   "Revision 3 or greater".\r
687       /// \95 3 if the document is encrypted with a V value of 2 or 3, or has any "Revision 3 or \r
688       ///   greater" access permissions set.\r
689       /// \95 4 if the document is encrypted with a V value of 4\r
690       /// </summary>\r
691       [KeyInfo(KeyType.Integer | KeyType.Required)]\r
692       public const string R = "/R";\r
693 \r
694       /// <summary>\r
695       /// (Required) A 32-byte string, based on both the owner and user passwords, that is\r
696       /// used in computing the encryption key and in determining whether a valid owner\r
697       /// password was entered.\r
698       /// </summary>\r
699       [KeyInfo(KeyType.String | KeyType.Required)]\r
700       public const string O = "/O";\r
701 \r
702       /// <summary>\r
703       /// (Required) A 32-byte string, based on the user password, that is used in determining\r
704       /// whether to prompt the user for a password and, if so, whether a valid user or owner \r
705       /// password was entered.\r
706       /// </summary>\r
707       [KeyInfo(KeyType.String | KeyType.Required)]\r
708       public const string U = "/U";\r
709 \r
710       /// <summary>\r
711       /// (Required) A set of flags specifying which operations are permitted when the document\r
712       /// is opened with user access.\r
713       /// </summary>\r
714       [KeyInfo(KeyType.Integer | KeyType.Required)]\r
715       public const string P = "/P";\r
716 \r
717       /// <summary>\r
718       /// (Optional; meaningful only when the value of V is 4; PDF 1.5) Indicates whether\r
719       /// the document-level metadata stream is to be encrypted. Applications should respect this value.\r
720       /// Default value: true.\r
721       /// </summary>\r
722       [KeyInfo(KeyType.Boolean | KeyType.Optional)]\r
723       public const string EncryptMetadata = "/EncryptMetadata";\r
724 \r
725       /// <summary>\r
726       /// Gets the KeysMeta for these keys.\r
727       /// </summary>\r
728       public static DictionaryMeta Meta\r
729       {\r
730         get\r
731         {\r
732           if (meta == null)\r
733             meta = CreateMeta(typeof(Keys));\r
734           return meta;\r
735         }\r
736       }\r
737       static DictionaryMeta meta;\r
738     }\r
739 \r
740     /// <summary>\r
741     /// Gets the KeysMeta of this dictionary type.\r
742     /// </summary>\r
743     internal override DictionaryMeta Meta\r
744     {\r
745       get { return Keys.Meta; }\r
746     }\r
747     #endregion\r
748   }\r
749 }\r