1
- using System . IO ;
2
- using FluentAssertions ;
1
+ using FluentAssertions ;
3
2
using PdfSharpCore . Drawing ;
4
3
using PdfSharpCore . Pdf ;
5
4
using PdfSharpCore . Pdf . IO ;
6
5
using PdfSharpCore . Pdf . Security ;
6
+ using PdfSharpCore . Test . Helpers ;
7
+ using System . IO ;
7
8
using Xunit ;
9
+ using Xunit . Abstractions ;
8
10
9
11
namespace PdfSharpCore . Test . Security
10
12
{
11
13
public class PdfSecurity
12
14
{
15
+ private readonly ITestOutputHelper output ;
16
+
17
+ public PdfSecurity ( ITestOutputHelper testOutputHelper )
18
+ {
19
+ output = testOutputHelper ;
20
+ }
21
+
13
22
[ Theory ]
14
23
[ InlineData ( PdfDocumentSecurityLevel . Encrypted40Bit , "hunter1" ) ]
15
24
[ InlineData ( PdfDocumentSecurityLevel . Encrypted128Bit , "hunter1" ) ]
@@ -19,6 +28,8 @@ public void CreateAndReadPasswordProtectedPdf(PdfDocumentSecurityLevel securityL
19
28
var pageNewRenderer = document . AddPage ( ) ;
20
29
var renderer = XGraphics . FromPdfPage ( pageNewRenderer ) ;
21
30
renderer . DrawString ( "Test Test Test" , new XFont ( "Arial" , 12 ) , XBrushes . Black , new XPoint ( 12 , 12 ) ) ;
31
+ // validate correct handling of unicode strings (issue #264)
32
+ document . Outlines . Add ( "The only page" , pageNewRenderer ) ;
22
33
document . SecuritySettings . DocumentSecurityLevel = securityLevel ;
23
34
document . SecuritySettings . UserPassword = password ;
24
35
@@ -30,6 +41,86 @@ public void CreateAndReadPasswordProtectedPdf(PdfDocumentSecurityLevel securityL
30
41
delegate ( PdfPasswordProviderArgs args ) { args . Password = password ; } ) ;
31
42
32
43
loadDocument . PageCount . Should ( ) . Be ( 1 ) ;
44
+ loadDocument . Outlines [ 0 ] . Title . Should ( ) . Be ( "The only page" ) ;
45
+ loadDocument . Info . Producer . Should ( ) . Contain ( "PDFsharp" ) ;
46
+ }
47
+
48
+ [ Fact ]
49
+ public void ShouldBeAbleToOpenAesEncryptedDocuments ( )
50
+ {
51
+ // this document has a V value of 4 (see PdfReference 1.7, Chapter 7.6.1, Table 20)
52
+ // and an R value of 4 (see PdfReference 1.7, Chapter 7.6.3.2, Table 21)
53
+ // see also: Adobe Supplement to the ISO 32000, BaseVersion: 1.7, ExtensionLevel: 3
54
+ // Chapter 3.5.2, Table 3.19
55
+ var file = PathHelper . GetInstance ( ) . GetAssetPath ( "AesEncrypted.pdf" ) ;
56
+ var fi = new FileInfo ( file ) ;
57
+ var document = Pdf . IO . PdfReader . Open ( file , PdfDocumentOpenMode . Import ) ;
58
+
59
+ // verify document was actually AES-encrypted
60
+ var cf = document . SecurityHandler . Elements . GetDictionary ( "/CF" ) ;
61
+ var stdCf = cf . Elements . GetDictionary ( "/StdCF" ) ;
62
+ stdCf . Elements . GetString ( "/CFM" ) . Should ( ) . Be ( "/AESV2" ) ;
63
+
64
+ IO . PdfReader . AssertIsAValidPdfDocumentWithProperties ( document , ( int ) fi . Length ) ;
65
+ }
66
+
67
+ [ Fact ]
68
+ public void DocumentWithUserPasswordCannotBeOpenedWithoutPassword ( )
69
+ {
70
+ var file = PathHelper . GetInstance ( ) . GetAssetPath ( "AesEncrypted.pdf" ) ;
71
+ var document = Pdf . IO . PdfReader . Open ( file , PdfDocumentOpenMode . Import ) ;
72
+
73
+ // import pages into a new document
74
+ var encryptedDoc = new PdfDocument ( ) ;
75
+ foreach ( var page in document . Pages )
76
+ encryptedDoc . AddPage ( page ) ;
77
+
78
+ // save enrypted
79
+ encryptedDoc . SecuritySettings . UserPassword = "supersecret!11" ;
80
+ var saveFileName = PathHelper . GetInstance ( ) . GetAssetPath ( "SavedEncrypted.pdf" ) ;
81
+ encryptedDoc . Save ( saveFileName ) ;
82
+
83
+ // should throw because no password was provided
84
+ var ex = Assert . Throws < PdfReaderException > ( ( ) =>
85
+ {
86
+ var readBackDoc = Pdf . IO . PdfReader . Open ( saveFileName , PdfDocumentOpenMode . Import ) ;
87
+ } ) ;
88
+ ex . Message . Should ( ) . Contain ( "A password is required to open the PDF document" ) ;
89
+
90
+ // check with password
91
+ // TODO: should be checked in a separate test, but i was lazy...
92
+ var fi = new FileInfo ( saveFileName ) ;
93
+ var readBackDoc = Pdf . IO . PdfReader . Open ( saveFileName , "supersecret!11" , PdfDocumentOpenMode . Import ) ;
94
+ IO . PdfReader . AssertIsAValidPdfDocumentWithProperties ( readBackDoc , ( int ) fi . Length ) ;
95
+ readBackDoc . PageCount . Should ( ) . Be ( document . PageCount ) ;
96
+ }
97
+
98
+ // Same PDF protected by different tools or online-services
99
+ [ Theory ]
100
+ // https://www.ilovepdf.com/protect-pdf, 128 bit, /V 2 /R 3
101
+ [ InlineData ( @"protected-ilovepdf.pdf" , "test123" ) ]
102
+
103
+ // https://www.adobe.com/de/acrobat/online/password-protect-pdf.html, 128 bit, /V 4 /R 4
104
+ [ InlineData ( @"protected-adobe.pdf" , "test123" ) ]
105
+
106
+ // https://pdfencrypt.net, 256 bit, /V 5 /R 5
107
+ [ InlineData ( @"protected-pdfencrypt.pdf" , "test123" ) ]
108
+
109
+ // https://www.sodapdf.com/password-protect-pdf/
110
+ // this is the only tool tested, that encrypts with the latest known algorithm (256 bit, /V 5 /R 6)
111
+ // Note: SodaPdf also produced a pdf that would be considered "invalid" by PdfSharp, because of incorrect stream-lengths
112
+ // (in the Stream-Dictionary, the length was reported as 32, but in fact the length was 16)
113
+ // this needed to be handled as well
114
+ [ InlineData ( @"protected-sodapdf.pdf" , "test123" ) ]
115
+ public void CanReadPdfEncryptedWithSupportedAlgorithms ( string fileName , string password )
116
+ {
117
+ var path = PathHelper . GetInstance ( ) . GetAssetPath ( fileName ) ;
118
+
119
+ var doc = Pdf . IO . PdfReader . Open ( path , password , PdfDocumentOpenMode . Import ) ;
120
+ doc . Should ( ) . NotBeNull ( ) ;
121
+ doc . PageCount . Should ( ) . BeGreaterThan ( 0 ) ;
122
+ output . WriteLine ( "Creator : {0}" , doc . Info . Creator ) ;
123
+ output . WriteLine ( "Producer: {0}" , doc . Info . Producer ) ;
33
124
}
34
125
}
35
126
}
0 commit comments