1
#region PDFsharp - A .NET library for processing PDF
4
// Stefan Lange (mailto:Stefan.Lange@pdfsharp.com)
6
// Copyright (c) 2005-2008 empira Software GmbH, Cologne (Germany)
8
// http://www.pdfsharp.com
9
// http://sourceforge.net/projects/pdfsharp
11
// Permission is hereby granted, free of charge, to any person obtaining a
12
// copy of this software and associated documentation files (the "Software"),
13
// to deal in the Software without restriction, including without limitation
14
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
// and/or sell copies of the Software, and to permit persons to whom the
16
// Software is furnished to do so, subject to the following conditions:
18
// The above copyright notice and this permission notice shall be included
19
// in all copies or substantial portions of the Software.
21
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
// DEALINGS IN THE SOFTWARE.
31
using System.Diagnostics;
32
using System.Collections;
33
using PdfSharp.Drawing;
34
using PdfSharp.Internal;
36
using PdfSharp.Pdf.IO;
37
using PdfSharp.Pdf.Advanced;
38
using PdfSharp.Pdf.Internal;
39
using System.Security.Cryptography;
41
namespace PdfSharp.Pdf.Security
44
/// Represents the standard PDF security handler.
46
public sealed class PdfStandardSecurityHandler : PdfSecurityHandler
48
internal PdfStandardSecurityHandler(PdfDocument document)
53
internal PdfStandardSecurityHandler(PdfDictionary dict)
59
/// Sets the user password of the document. Setting a password automatically sets the
60
/// PdfDocumentSecurityLevel to PdfDocumentSecurityLevel.Encrypted128Bit if its current
61
/// value is PdfDocumentSecurityLevel.None.
63
public string UserPassword
67
if (this.document.securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.None)
68
this.document.securitySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.Encrypted128Bit;
69
this.userPassword = value;
72
internal string userPassword;
75
/// Sets the owner password of the document. Setting a password automatically sets the
76
/// PdfDocumentSecurityLevel to PdfDocumentSecurityLevel.Encrypted128Bit if its current
77
/// value is PdfDocumentSecurityLevel.None.
79
public string OwnerPassword
83
if (this.document.securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.None)
84
this.document.securitySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.Encrypted128Bit;
85
this.ownerPassword = value;
88
internal string ownerPassword;
91
/// Gets or sets the user access permission represented as an integer in the P key.
93
internal PdfUserAccessPermission Permission
97
PdfUserAccessPermission permission = (PdfUserAccessPermission)Elements.GetInteger(Keys.P);
98
if ((int)permission == 0)
99
permission = PdfUserAccessPermission.PermitAll;
102
set { Elements.SetInteger(Keys.P, (int)value); }
106
/// Encrypts the whole document.
108
public void EncryptDocument()
110
foreach (PdfReference iref in this.document.irefTable.AllReferences)
112
if (!Object.ReferenceEquals(iref.Value, this))
113
EncryptObject(iref.Value);
118
/// Encrypts an indirect object.
120
internal void EncryptObject(PdfObject value)
122
Debug.Assert(value.Reference != null);
124
SetHashKey(value.ObjectID);
126
if (value.ObjectID.ObjectNumber == 10)
133
if ((dict = value as PdfDictionary) != null)
134
EncryptDictionary(dict);
135
else if ((array = value as PdfArray) != null)
137
else if ((str = value as PdfStringObject) != null)
141
byte[] bytes = str.EncryptionValue;
144
str.EncryptionValue = bytes;
150
/// Encrypts a dictionary.
152
void EncryptDictionary(PdfDictionary dict)
154
PdfName[] names = dict.Elements.KeyNames;
155
foreach (DictionaryEntry item in dict.Elements)
158
PdfDictionary value2;
160
if ((value1 = item.Value as PdfString) != null)
161
EncryptString(value1);
162
else if ((value2 = item.Value as PdfDictionary) != null)
163
EncryptDictionary(value2);
164
else if ((value3 = item.Value as PdfArray) != null)
165
EncryptArray(value3);
167
if (dict.Stream != null)
169
byte[] bytes = dict.Stream.Value;
170
if (bytes.Length != 0)
174
dict.Stream.Value = bytes;
180
/// Encrypts an array.
182
void EncryptArray(PdfArray array)
184
int count = array.Elements.Count;
185
for (int idx = 0; idx < count; idx++)
187
PdfItem item = array.Elements[idx];
189
PdfDictionary value2;
191
if ((value1 = item as PdfString) != null)
192
EncryptString(value1);
193
else if ((value2 = item as PdfDictionary) != null)
194
EncryptDictionary(value2);
195
else if ((value3 = item as PdfArray) != null)
196
EncryptArray(value3);
201
/// Encrypts a string.
203
void EncryptString(PdfString value)
205
if (value.Length != 0)
207
byte[] bytes = value.EncryptionValue;
210
value.EncryptionValue = bytes;
215
/// Encrypts an array.
217
internal byte[] EncryptBytes(byte[] bytes)
219
if (bytes != null && bytes.Length != 0)
227
#region Encryption Algorithms
230
/// Checks the password.
232
/// <param name="inputPassword">Password or null if no password is provided.</param>
234
/// 0: inputPassword is neither user nor owner password.
235
/// 1: inputPassword is user password.
236
/// 2: inputPassword is owner password.
238
public int ValidatePassword(string inputPassword)
240
// We can handle 40 and 128 bit standard encryption
241
string filter = Elements.GetName(PdfSecurityHandler.Keys.Filter);
242
int v = Elements.GetInteger(Keys.V);
243
if (filter != "/Standard" || !(v >= 1 && v <= 3))
244
throw new PdfReaderException(PSSR.UnknownEncryption);
246
byte[] documentID = PdfEncoders.RawEncoding.GetBytes(Owner.Internals.FirstDocumentID);
247
byte[] oValue = PdfEncoders.RawEncoding.GetBytes(Elements.GetString(Keys.O));
248
byte[] uValue = PdfEncoders.RawEncoding.GetBytes(Elements.GetString(Keys.U));
249
int pValue = Elements.GetInteger(Keys.P);
250
int rValue = Elements.GetInteger(Keys.R);
253
if (inputPassword == null)
256
bool strongEncryption = rValue == 3;
257
int keyLength = strongEncryption ? 16 : 32;
259
password = PdfEncoders.RawEncoding.GetBytes(inputPassword);
260
InitWidhUserPassword(documentID, inputPassword, oValue, pValue, strongEncryption);
262
this.document.SecuritySettings.hasOwnerPermissions = false;
264
if (!EqualsKey(uValue, keyLength))
266
password = PdfEncoders.RawEncoding.GetBytes(inputPassword);
268
//Compare owner password
269
InitWidhOwnerPassword(documentID, inputPassword, oValue, pValue, strongEncryption);
271
if (!EqualsKey(uValue, keyLength))
273
//Compare user password
274
InitWidhUserPassword(documentID, inputPassword, oValue, pValue, strongEncryption);
275
if (!EqualsKey(uValue, keyLength))
279
this.document.SecuritySettings.hasOwnerPermissions = true;
285
[Conditional("DEBUG")]
286
void DumpBytes(string tag, byte[] bytes)
288
string dump = tag + ": ";
289
for (int idx = 0; idx < bytes.Length; idx++)
290
dump += String.Format("{0:X2}", bytes[idx]);
291
Debug.WriteLine(dump);
295
/// Pads a password to a 32 byte array.
297
byte[] PadPassword(string password)
299
byte[] padded = new byte[32];
300
if (password == null)
301
Array.Copy(passwordPadding, 0, padded, 0, 32);
304
int length = password.Length;
305
Array.Copy(PdfEncoders.RawEncoding.GetBytes(password), 0, padded, 0, Math.Min(length, 32));
307
Array.Copy(passwordPadding, 0, padded, length, 32 - length);
311
static byte[] passwordPadding = // 32 bytes password padding defined by Adobe
313
0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
314
0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A,
318
/// Generates the user key based on the padded user password.
320
void InitWidhUserPassword(byte[] documentID, string userPassword, byte[] ownerKey, int permissions, bool strongEncryption)
322
InitEncryptionKey(documentID, PadPassword(userPassword), ownerKey, permissions, strongEncryption);
323
SetupUserKey(documentID);
327
/// Generates the user key based on the padded owner password.
329
void InitWidhOwnerPassword(byte[] documentID, string ownerPassword, byte[] ownerKey, int permissions, bool strongEncryption)
331
byte[] userPad = ComputeOwnerKey(ownerKey, PadPassword(ownerPassword), strongEncryption);
332
InitEncryptionKey(documentID, userPad, ownerKey, permissions, strongEncryption);
333
SetupUserKey(documentID);
337
/// Computes the padded user password from the padded owner password.
339
byte[] ComputeOwnerKey(byte[] userPad, byte[] ownerPad, bool strongEncryption)
341
byte[] ownerKey = new byte[32];
343
byte[] digest = this.md5.ComputeHash(ownerPad);
344
if (strongEncryption)
346
byte[] mkey = new byte[16];
347
// Hash the pad 50 times
348
for (int idx = 0; idx < 50; idx++)
349
digest = this.md5.ComputeHash(digest);
350
Array.Copy(userPad, 0, ownerKey, 0, 32);
352
for (int i = 0; i < 20; i++)
354
for (int j = 0; j < mkey.Length; ++j)
355
mkey[j] = (byte)(digest[j] ^ i);
357
EncryptRC4(ownerKey);
362
PrepareRC4Key(digest, 0, 5);
363
EncryptRC4(userPad, ownerKey);
369
/// Computes the encryption key.
371
void InitEncryptionKey(byte[] documentID, byte[] userPad, byte[] ownerKey, int permissions, bool strongEncryption)
373
this.ownerKey = ownerKey;
374
this.encryptionKey = new byte[strongEncryption ? 16 : 5];
376
this.md5.Initialize();
377
this.md5.TransformBlock(userPad, 0, userPad.Length, userPad, 0);
378
this.md5.TransformBlock(ownerKey, 0, ownerKey.Length, ownerKey, 0);
380
// Split permission into 4 bytes
381
byte[] permission = new byte[4];
382
permission[0] = (byte)permissions;
383
permission[1] = (byte)(permissions >> 8);
384
permission[2] = (byte)(permissions >> 16);
385
permission[3] = (byte)(permissions >> 24);
386
this.md5.TransformBlock(permission, 0, 4, permission, 0);
387
this.md5.TransformBlock(documentID, 0, documentID.Length, documentID, 0);
388
this.md5.TransformFinalBlock(permission, 0, 0);
389
byte[] digest = this.md5.Hash;
390
this.md5.Initialize();
391
// Create the hash 50 times (only for 128 bit)
392
if (this.encryptionKey.Length == 16)
394
for (int idx = 0; idx < 50; idx++)
396
digest = this.md5.ComputeHash(digest);
397
this.md5.Initialize();
400
Array.Copy(digest, 0, this.encryptionKey, 0, this.encryptionKey.Length);
404
/// Computes the user key.
406
void SetupUserKey(byte[] documentID)
408
if (this.encryptionKey.Length == 16)
410
this.md5.TransformBlock(passwordPadding, 0, passwordPadding.Length, passwordPadding, 0);
411
this.md5.TransformFinalBlock(documentID, 0, documentID.Length);
412
byte[] digest = this.md5.Hash;
413
this.md5.Initialize();
414
Array.Copy(digest, 0, this.userKey, 0, 16);
415
for (int idx = 16; idx < 32; idx++)
416
this.userKey[idx] = 0;
418
for (int i = 0; i < 20; i++)
420
for (int j = 0; j < this.encryptionKey.Length; j++)
421
digest[j] = (byte)(this.encryptionKey[j] ^ i);
422
PrepareRC4Key(digest, 0, this.encryptionKey.Length);
423
EncryptRC4(this.userKey, 0, 16);
428
PrepareRC4Key(this.encryptionKey);
429
EncryptRC4(passwordPadding, this.userKey);
434
/// Prepare the encryption key.
438
PrepareRC4Key(this.key, 0, this.keySize);
442
/// Prepare the encryption key.
444
void PrepareRC4Key(byte[] key)
446
PrepareRC4Key(key, 0, key.Length);
450
/// Prepare the encryption key.
452
void PrepareRC4Key(byte[] key, int offset, int length)
456
for (int idx = 0; idx < 256; idx++)
457
this.state[idx] = (byte)idx;
459
for (int idx = 0; idx < 256; idx++)
461
idx2 = (key[idx1 + offset] + this.state[idx] + idx2) & 255;
462
tmp = this.state[idx];
463
this.state[idx] = this.state[idx2];
464
this.state[idx2] = tmp;
465
idx1 = (idx1 + 1) % length;
470
/// Encrypts the data.
472
void EncryptRC4(byte[] data)
474
EncryptRC4(data, 0, data.Length, data);
478
/// Encrypts the data.
480
void EncryptRC4(byte[] data, int offset, int length)
482
EncryptRC4(data, offset, length, data);
486
/// Encrypts the data.
488
void EncryptRC4(byte[] inputData, byte[] outputData)
490
EncryptRC4(inputData, 0, inputData.Length, outputData);
494
/// Encrypts the data.
496
void EncryptRC4(byte[] inputData, int offset, int length, byte[] outputData)
501
for (int idx = offset; idx < length; idx++)
504
y = (this.state[x] + y) & 255;
506
this.state[x] = this.state[y];
508
outputData[idx] = (byte)(inputData[idx] ^ state[(this.state[x] + this.state[y]) & 255]);
513
/// Checks whether the calculated key correct.
515
bool EqualsKey(byte[] value, int length)
517
for (int idx = 0; idx < length; idx++)
519
if (this.userKey[idx] != value[idx])
526
/// Set the hash key for the specified object.
528
internal void SetHashKey(PdfObjectID id)
530
byte[] objectId = new byte[5];
531
this.md5.Initialize();
532
// Split the object number and generation
533
objectId[0] = (byte)id.ObjectNumber;
534
objectId[1] = (byte)(id.ObjectNumber >> 8);
535
objectId[2] = (byte)(id.ObjectNumber >> 16);
536
objectId[3] = (byte)id.GenerationNumber;
537
objectId[4] = (byte)(id.GenerationNumber >> 8);
538
this.md5.TransformBlock(this.encryptionKey, 0, this.encryptionKey.Length, this.encryptionKey, 0);
539
this.md5.TransformFinalBlock(objectId, 0, objectId.Length);
540
this.key = this.md5.Hash;
541
this.md5.Initialize();
542
this.keySize = this.encryptionKey.Length + 5;
543
if (this.keySize > 16)
548
/// Prepares the security handler for encrypting the document.
550
public void PrepareEncryption()
552
Debug.Assert(this.document.securitySettings.DocumentSecurityLevel != PdfDocumentSecurityLevel.None);
553
int permissions = (int)this.Permission;
554
bool strongEncryption = this.document.securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128Bit;
560
if (strongEncryption)
562
vValue = new PdfInteger(2);
563
length = new PdfInteger(128);
564
rValue = new PdfInteger(3);
568
vValue = new PdfInteger(1);
569
length = new PdfInteger(40);
570
rValue = new PdfInteger(2);
573
if (this.userPassword == null || this.userPassword.Length == 0)
574
this.userPassword = "";
575
// Use user password twice if no owner password provided.
576
if (this.ownerPassword == null || this.ownerPassword.Length == 0)
577
this.ownerPassword = this.userPassword;
579
// Correct permission bits
580
permissions |= (int)(strongEncryption ? (uint)0xfffff0c0 : (uint)0xffffffc0);
581
permissions &= unchecked((int)0xfffffffc);
583
PdfInteger pValue = new PdfInteger(permissions);
585
Debug.Assert(this.ownerPassword.Length > 0, "Empty owner password.");
586
byte[] userPad = PadPassword(this.userPassword);
587
byte[] ownerPad = PadPassword(this.ownerPassword);
589
this.md5.Initialize();
590
this.ownerKey = ComputeOwnerKey(userPad, ownerPad, strongEncryption);
591
byte[] documentID = PdfEncoders.RawEncoding.GetBytes(this.document.Internals.FirstDocumentID);
592
InitWidhUserPassword(documentID, this.userPassword, this.ownerKey, permissions, strongEncryption);
594
PdfString oValue = new PdfString(PdfEncoders.RawEncoding.GetString(this.ownerKey));
595
PdfString uValue = new PdfString(PdfEncoders.RawEncoding.GetString(this.userKey));
597
Elements[Keys.Filter] = new PdfName("/Standard");
598
Elements[Keys.V] = vValue;
599
Elements[Keys.Length] = length;
600
Elements[Keys.R] = rValue;
601
Elements[Keys.O] = oValue;
602
Elements[Keys.U] = uValue;
603
Elements[Keys.P] = pValue;
607
/// The global encryption key.
609
byte[] encryptionKey;
612
/// The message digest algorithm MD5.
614
MD5 md5 = new MD5CryptoServiceProvider();
617
/// Bytes used for RC4 encryption.
619
byte[] state = new byte[256];
622
/// The encryption key for the owner.
624
byte[] ownerKey = new byte[32];
627
/// The encryption key for the user.
629
byte[] userKey = new byte[32];
632
/// The encryption key for a particular object/generation.
637
/// The encryption key length for a particular object/generation.
643
internal override void WriteObject(PdfWriter writer)
645
// Don't encypt myself
646
PdfStandardSecurityHandler securityHandler = writer.SecurityHandler;
647
writer.SecurityHandler = null;
648
base.WriteObject(writer);
649
writer.SecurityHandler = securityHandler;
654
/// Predefined keys of this dictionary.
656
internal sealed new class Keys : PdfSecurityHandler.Keys
659
/// (Required) A number specifying which revision of the standard security handler
660
/// should be used to interpret this dictionary:
661
/// � 2 if the document is encrypted with a V value less than 2 and does not have any of
662
/// the access permissions set (by means of the P entry, below) that are designated
663
/// "Revision 3 or greater".
664
/// � 3 if the document is encrypted with a V value of 2 or 3, or has any "Revision 3 or
665
/// greater" access permissions set.
666
/// � 4 if the document is encrypted with a V value of 4
668
[KeyInfo(KeyType.Integer | KeyType.Required)]
669
public const string R = "/R";
672
/// (Required) A 32-byte string, based on both the owner and user passwords, that is
673
/// used in computing the encryption key and in determining whether a valid owner
674
/// password was entered.
676
[KeyInfo(KeyType.String | KeyType.Required)]
677
public const string O = "/O";
680
/// (Required) A 32-byte string, based on the user password, that is used in determining
681
/// whether to prompt the user for a password and, if so, whether a valid user or owner
682
/// password was entered.
684
[KeyInfo(KeyType.String | KeyType.Required)]
685
public const string U = "/U";
688
/// (Required) A set of flags specifying which operations are permitted when the document
689
/// is opened with user access.
691
[KeyInfo(KeyType.Integer | KeyType.Required)]
692
public const string P = "/P";
695
/// (Optional; meaningful only when the value of V is 4; PDF 1.5) Indicates whether
696
/// the document-level metadata stream is to be encrypted. Applications should respect this value.
697
/// Default value: true.
699
[KeyInfo(KeyType.Boolean | KeyType.Optional)]
700
public const string EncryptMetadata = "/EncryptMetadata";
703
/// Gets the KeysMeta for these keys.
705
public static DictionaryMeta Meta
709
if (Keys.meta == null)
710
Keys.meta = CreateMeta(typeof(Keys));
714
static DictionaryMeta meta;
718
/// Gets the KeysMeta of this dictionary type.
720
internal override DictionaryMeta Meta
722
get { return Keys.Meta; }