├── Demo
├── MainFormUnit.dfm
├── MainFormUnit.pas
├── PKCS7ExtractorDemo.dpr
├── PKCS7ExtractorDemo.lpi
├── PKCS7ExtractorDemo.res
└── PKCS7ExtractorDemo_Icon.ico
├── Documentation
├── pkcs7extractory_demo.png
└── pkcs7extractory_logo.png
├── LEGGIMI.md
├── LICENSE
├── README.md
├── Source
└── PKCS7Extractor.pas
├── Tests
├── PKCS7ExtractorTest.dpr
├── PKCS7ExtractorTest.lpi
└── create_datacheck.bat
└── Utils
└── CCLib.TempFileStream.pas
/Demo/MainFormUnit.dfm:
--------------------------------------------------------------------------------
1 | object FormMain: TFormMain
2 | Left = 0
3 | Top = 0
4 | ActiveControl = bExtract
5 | BorderStyle = bsSizeToolWin
6 | Caption = 'PKCS#7 Extractor demo by Delphi Club Italia'
7 | ClientHeight = 478
8 | ClientWidth = 663
9 | Color = clWindow
10 | Font.Charset = DEFAULT_CHARSET
11 | Font.Color = clWindowText
12 | Font.Height = -11
13 | Font.Name = 'Tahoma'
14 | Font.Style = []
15 | KeyPreview = True
16 | OldCreateOrder = False
17 | Position = poScreenCenter
18 | OnCreate = FormCreate
19 | OnKeyDown = FormKeyDown
20 | DesignSize = (
21 | 663
22 | 478)
23 | PixelsPerInch = 96
24 | TextHeight = 13
25 | object lblVerification: TLabel
26 | Left = 8
27 | Top = 193
28 | Width = 57
29 | Height = 13
30 | Caption = '&Verification:'
31 | end
32 | object lblSignatureMode: TLabel
33 | Left = 8
34 | Top = 212
35 | Width = 79
36 | Height = 13
37 | Caption = 'Signature Mode:'
38 | end
39 | object lblSignatureModeValue: TLabel
40 | Left = 128
41 | Top = 212
42 | Width = 51
43 | Height = 13
44 | Caption = '(unknown)'
45 | end
46 | object lblVerificationValue: TLabel
47 | Left = 128
48 | Top = 193
49 | Width = 24
50 | Height = 13
51 | Caption = '(idle)'
52 | end
53 | object bExtract: TButton
54 | Left = 8
55 | Top = 146
56 | Width = 647
57 | Height = 41
58 | Anchors = [akLeft, akTop, akRight]
59 | Caption = 'Extract PKCS#7 message file...'
60 | Default = True
61 | TabOrder = 1
62 | OnClick = ExtractPKCS7
63 | end
64 | object memoSource: TMemo
65 | Left = 8
66 | Top = 231
67 | Width = 647
68 | Height = 239
69 | Anchors = [akLeft, akTop, akRight, akBottom]
70 | BevelInner = bvNone
71 | BevelKind = bkFlat
72 | BorderStyle = bsNone
73 | Lines.Strings = (
74 | 'memoSource')
75 | PopupMenu = popupEmpty
76 | ReadOnly = True
77 | ScrollBars = ssBoth
78 | TabOrder = 2
79 | WordWrap = False
80 | end
81 | object panelLibrary: TPanel
82 | Left = 8
83 | Top = 89
84 | Width = 554
85 | Height = 48
86 | Anchors = [akLeft, akTop, akRight]
87 | BevelOuter = bvLowered
88 | Color = clWindow
89 | ParentBackground = False
90 | TabOrder = 3
91 | object labelLocation: TLabel
92 | Left = 96
93 | Top = 8
94 | Width = 12
95 | Height = 13
96 | Caption = '...'
97 | end
98 | object labelVersion: TLabel
99 | Left = 96
100 | Top = 27
101 | Width = 12
102 | Height = 13
103 | Caption = '...'
104 | end
105 | object labelLocationL: TLabel
106 | Left = 8
107 | Top = 8
108 | Width = 77
109 | Height = 13
110 | Caption = 'Library location:'
111 | end
112 | object labelVersionL: TLabel
113 | Left = 8
114 | Top = 27
115 | Width = 75
116 | Height = 13
117 | Caption = 'Library version:'
118 | end
119 | end
120 | object pnlSubHeader: TPanel
121 | Left = 0
122 | Top = 78
123 | Width = 663
124 | Height = 5
125 | Cursor = crHandPoint
126 | Align = alTop
127 | BevelOuter = bvNone
128 | Color = 12608789
129 | ParentBackground = False
130 | TabOrder = 4
131 | OnClick = GoToWebsite
132 | end
133 | object pnlHeader: TPanel
134 | Left = 0
135 | Top = 0
136 | Width = 663
137 | Height = 78
138 | Cursor = crHandPoint
139 | Align = alTop
140 | BevelOuter = bvNone
141 | TabOrder = 5
142 | OnClick = GoToWebsite
143 | object imgDCILogo: TImage
144 | Left = 590
145 | Top = 0
146 | Width = 73
147 | Height = 78
148 | Cursor = crHandPoint
149 | Align = alRight
150 | Center = True
151 | Picture.Data = {
152 | 07544269746D617036300000424D363000000000000036000000280000004000
153 | 0000400000000100180000000000003000000000000000000000000000000000
154 | 0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
155 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
156 | FFFFFFFFFFFFFFFBFBFED7D7F3A8A7E57A79D85251CC3B39C52826BF1C1ABC14
157 | 12B91412B91D1BBC2927BF3C3AC55453CC7E7DD9ACABE6DADAF4FDFDFEFFFFFF
158 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
159 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
160 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
161 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F8
162 | FDBDBCEC706FD52F2EC20705B60200B40200B40200B40200B40200B40200B402
163 | 00B40200B40200B40200B40200B40200B40200B40200B40907B63331C27473D6
164 | C1C1EDFBFBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
165 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
166 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
167 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE4E4F78786DC2D2B
168 | C20301B50301B50301B50301B50201B50200B50200B40200B40200B40200B402
169 | 00B40200B40200B40200B40200B40200B40200B40200B40200B40200B40200B4
170 | 0200B43230C2908FDEEAEAF9FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
171 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
172 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
173 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5E5F87978D91816BD0402B60302
174 | B60301B60301B60301B60301B50301B50301B50301B50200B50200B50200B402
175 | 00B40200B40200B40200B40200B40200B40200B40200B40200B40200B40200B4
176 | 0200B40200B40200B41A18BB8180DAEBEBF9FFFFFFFFFFFFFFFFFFFFFFFFFFFF
177 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
178 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
179 | FFFFFFFFFFFFFFFFFFFFFFFFFBFBFE9190E01D1BBE0402B70402B70402B70402
180 | B70402B60402B60302B60301B60301B60301B60301B50301B50301B50301B502
181 | 01B50200B50200B40200B40200B40200B40200B40200B40200B40200B40200B4
182 | 0200B40200B40200B40200B40200B4211FBD9B9AE1FFFFFFFFFFFFFFFFFFFFFF
183 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
184 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
185 | FFFFFFFFFFFFFFFFFFD0D0F2413FC90503B80503B80503B80503B70403B70402
186 | B70402B70402B70402B60402B60402B60301B60301B60301B60301B50301B503
187 | 01B50301B50301B50200B50200B40200B40200B40200B40200B40200B40200B4
188 | 0200B40200B40200B40200B40200B40200B40200B44948C9D8D8F4FFFFFFFFFF
189 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
190 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
191 | FFFFFFFFFFFF9999E51412BD0604B90504B90503B80503B80503B80503B80503
192 | B80403B70402B70402B70402B70402B70402B60402B60302B60301B60301B603
193 | 01B50301B50301B50301B50201B50200B50200B50200B40200B40200B40200B4
194 | 0200B40200B40200B40200B40200B40200B40200B40200B41715BAA5A5E6FFFF
195 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
196 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
197 | FCFCFD7F7FCC0402BD0604B90604B90604B90604B90604B80604B80503B80503
198 | B80604B70604B70503B70502B70402B70402B70402B70503B60503B50402B504
199 | 02B50402B50402B50402B40402B40402B40402B40301B40301B40301B40301B3
200 | 0301B30301B30301B30200B40200B40200B40200B40200B40301B30100B68585
201 | D3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
202 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F8F9
203 | 6160C80705BB0C0AB50A08B70807B80706B90807B70A08B50B08B40A08B50907
204 | B60907B40A08B40907B40806B50604B70503B70604B50806B30A08B20A08B10A
205 | 08B00B09B00B0AAF0C0AAE0C0AAE0B09AE0A08AF0907B00806B00807B00806B0
206 | 0806AF0806AF0806AF0504B10302B30301B30301B30504B10705AF0705AF0201
207 | B57373C3FCFCFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
208 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F8FA5756C8
209 | 0C0AB90D0BAD0907AA0908AF0D0BB60B09B70F0DB30C0AAC0806A80A08AC0F0E
210 | B10F0DAE0806A80705A70A09AF0A07B50806B50C0BB10D0BAA0907A40A08A312
211 | 11A70D0BA10E0C9E0F0D9D0E0C9E0C0A9F0907A20806A5110FA90A08A40806A2
212 | 0F0DA50C0BA40604A30503A80806B00503B10705B00C0AAB0705A30503A30503
213 | AA0100B56261C9FBFBFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
214 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCFCFE5958D20B09B9
215 | 0E0DAB6766C4A5A4DC6D6CCB0A09B00E0DB40E0CAA6B6AC5A5A4DB6968C70A08
216 | A72F2DB2A2A1DAA09FD92F2EB00D0BAB100EAE100EA53D3BAEA3A2D8A2A2D743
217 | 42AC9291CEA5A4D5A5A5D5A5A5D6A4A4D7A3A3D98786CE1918A17E7DC9A4A4D8
218 | 5352B47272C2A4A3D96665C20908A40D0CAB100EA815139F8382CAA3A2D96766
219 | C80302AC0100B56363D4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
220 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7371DB0907BD100EB6
221 | 0E0DA4A6A6D8FFFFFFB2B2E10908AB1110B10E0DA3ACACDBFFFFFFADACDE0B0A
222 | A12221A7E4E4F3FFFFFF7D7CC407069512119C0C0B909291C9FFFFFFEBEBF540
223 | 3FA0F2F1F7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0E0F11F1E9ACFCFE8FFFFFF
224 | 7E7EC07D7DBFFFFFFFDDDDEF1818980E0C990D0C9337369EFCFCFCFFFFFF706F
225 | C90000A90301B30200B48180DAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
226 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA09FE60A08BF0B09BD110FB5
227 | 1211A3A1A0D4FFFFFFACABDD0C0BAA1311B11211A1A7A6D6FFFFFFA6A5DA0F0E
228 | A30E0D9F8D8CCCFFFFFFD6D6EA7271B87575BB7675B7DFDFEDFFFFFF9B9BCD2A
229 | 2993EBEBF4FFFFFF6E6EBB403EAF413FB73F3EB73938B11E1C9AC9C9E4FFFFFF
230 | 8180C0333298F4F4F9FFFFFF8E8EC4706FB8706EB4A6A6CEFFFFFFEDEDF72422
231 | AC0301AD0201B40200B40200B4AEADE7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
232 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5D5F41615C30B09BF0B09BE1210B6
233 | 1312A2A1A1D4FFFFFFACABDD0D0BAA1412B11412A1A7A7D6FFFFFFA6A5DA0E0C
234 | A61110A74645AEF9F9FCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFE5151AC2C
235 | 2A98EBEBF5FFFFFF4241AF0402A20503AD0403AD0C0AA61B1A9BC9C9E4FFFFFF
236 | 8483C5110F8CB9B9DCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAAA9DB0403
237 | A40504B20200B50200B40200B41715BADFDFF6FFFFFFFFFFFFFFFFFFFFFFFFFF
238 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFBFE4443CF0B09C00B09C00C0ABF1211B7
239 | 1413A3A1A1D4FFFFFFACACDE0E0CAA1413B21412A1A7A7D6FFFFFFA6A5DB0C0A
240 | A81311AF1918A1C8C8E5FFFFFFC4C3DC7979B0CCCCE1FFFFFFD6D6EC201F9C2F
241 | 2EA0EBEBF5FFFFFF4644B60604AD0605B70705B60F0DAD1A199EC9C9E4FFFFFF
242 | 8282C70907916B6AB8FFFFFFF3F3F88888B89A99C2FEFEFEFFFFFF5B5ABD0100
243 | A80403B40301B50301B50201B50200B54C4BCAFFFFFFFFFFFFFFFFFFFFFFFFFF
244 | FFFFFFFFFFFFFFFFFFFFFFFFFFFF9796E50C0AC10C0AC10C0AC10C0ABF1211B7
245 | 1413A4A2A1D4FFFFFFACACDD100EAA1715B01615A0A8A8D6FFFFFFA7A6DA0E0C
246 | A81311B20E0DA47B7AC4FFFFFFC6C6DD29287FD1D1E4FFFFFF8C8BCE0E0C9C30
247 | 2EA2EBEBF5FFFFFF4544B70503AE0604B90706B80F0DAE1A199EC9C9E4FFFFFF
248 | 8180C90C0A9A2C2BA1E8E8F4FDFDFE4F4E957474ACFFFFFFE4E4F31D1BAA0503
249 | B00301B50301B50301B50301B50301B50201B5A6A5E5FFFFFFFFFFFFFFFFFFFF
250 | FFFFFFFFFFFFFFFFFFFFFFE9E9FA2220C70D0BC20C0BC20C0AC10D0BC01312B8
251 | 1513A4A2A1D4FFFFFFAEADDC1413A51A18A9191899A9A8D3FFFFFFA8A8D7100F
252 | A11311AE1412A93837A9F3F3F9F8F8FB8584B5FBFBFDFBFBFD4241B4100FA32F
253 | 2DA4EBEBF5FFFFFF4544B70604AF0604B90806B80F0EAE1A199EC9C9E4FFFFFF
254 | 807FCA0C0AA00F0E9CA4A3D5FFFFFFA5A4C8BEBED8FFFFFF9C9CD60301A50705
255 | B30302B60301B60301B60301B50301B50301B52523BFF1F1FBFFFFFFFFFFFFFF
256 | FFFFFFFFFFFFFFFFFFFFFF7E7DDF0E0CC30D0BC30D0BC20D0BC20E0CC11311B9
257 | 1412A6A1A1D6FFFFFFAEAEDC1D1CA12D2CA72B2999AFAED4FFFFFFAEAED82322
258 | A02524AE1D1BAD1715A0B5B4DDFFFFFFEDEDF4FFFFFFC7C7E91311A81412AA2D
259 | 2BA7EBEBF5FFFFFF4443B90604AF0705BA0807B80F0DB01918A0C8C8E5FFFFFF
260 | 7F7ECB0806A40D0BA35655B4FEFEFEF2F2F7F7F7FAFEFEFE4B4AB90201AB0503
261 | B60402B60402B60302B60301B60301B60301B60301B58D8CDEFFFFFFFFFFFFFF
262 | FFFFFFFFFFFFFFFFE9E9FA1F1DC80E0CC40E0CC30E0CC30D0BC30E0CC21211BC
263 | 100EABA4A4DAFFFFFFAAA9DA5B5AB9E9E9F4E8E8F3FBFBFCFFFFFFFBFBFCE7E7
264 | F3E9E8F65D5CC50605A26665BEFFFFFFFFFFFFFFFFFF7878CD0705AC100FAF29
265 | 27ACF2F2F9FFFFFF4443BC0403B20705BB0806B90E0CB21514A5CECDE9FFFFFF
266 | 8080CF0301A90F0DAD1D1BA4DCDCEEFFFFFFFFFFFFD9D9EF1312AA0705B20403
267 | B70402B70402B70402B60402B60402B60302B60301B6201EBEF1F1FBFFFFFFFF
268 | FFFFFFFFFFFFFFFF908FE40F0DC50F0DC50E0CC40E0CC40E0CC40E0CC3110FBE
269 | 0D0BB38584D5D6D6F18988D35555BFDDDDF3DEDDF3DADAF0D7D7EFDAD9F0DDDD
270 | F3DCDCF35655C80907AE2524B0C3C3E8D6D6F0CECDED2E2CBA0A08B50D0BB420
271 | 1EB2C4C3EAD5D5F13635BE0503B50806BB0806BB0B0AB5100EADA5A5DED5D5F0
272 | 6665CB0201B00C0AB40806A87B7ACCD6D6EFD6D5EF7877CF0201AC0705B60503
273 | B80403B80402B70402B70402B70402B70402B70402B60302B69D9DE3FFFFFFFF
274 | FFFFFFFFFFF9F9FE3635CF100EC60F0DC60F0DC50F0DC50F0DC50F0DC4100EC2
275 | 1311BE1513B91514B71614B71716B61716B31917B01A18AF1A18AE1917AF1615
276 | B11311B5110FB9110FBA110FB61311B11513B0120FB20D0CB70D0BBB0D0BBA10
277 | 0EB7100EB30F0EB30E0CB60A09BA0806BC0806BC0A08B90E0CB50F0DB10F0DB0
278 | 0D0BB30B09B70A08B70C0AB40D0BAE110FAC0F0EAD0A08B10807B60604B80503
279 | B80503B80503B80503B80503B70402B70402B70402B70402B73D3CC7FEFEFFFF
280 | FFFFFFFFFFC4C4F1110FC7100EC7100EC6100EC6100EC60F0DC50F0DC50F0DC4
281 | 100EC3100EC10F0DC0100EC0110FBE1210BB1311B91513B61513B61412B61210
282 | B8100DBB0F0DBC110FBA1211B81311B51312B41311B51210B70F0EB90E0DBA0E
283 | 0DB90E0DB70F0DB5100EB5100EB60D0CB80B0AB90B09B90D0BB70E0CB40F0DB2
284 | 100EB20F0DB30E0DB30E0CB30D0BB30A09B40807B60705B70705B80604B90604
285 | B90504B90503B80503B80503B80503B80503B80403B70402B70705B8D2D1F2FF
286 | FFFFFFFFFF7977E0110FC8110FC7110FC7100EC7100EC6100EC6100EC60F0EC6
287 | 0F0DC50F0DC50F0DC50F0DC31210BF1614BB1614B31210AC1211AA110FAB100E
288 | B11412B81413B91312B30F0EAB1110A71210A61110A70F0DA90C0AAC0E0CB215
289 | 13B51412B10F0DA90F0DA60F0DA60C0AA90E0CB0110FB51211B40E0CAB0D0BA5
290 | 0F0DA30F0DA30E0DA30C0AA40908A90D0BB10B09B70705B90605BA0604BA0604
291 | B90604B90604B90604B90503B80503B80503B80503B80503B80403B78685DCFF
292 | FFFFFBFBFE3D3BD31210C91210C8110FC8110FC8110FC7110FC7100EC7100EC6
293 | 100EC60F0EC6100EC51311C11817B91615AB3735AF7877C69190D08786CD5453
294 | BC1A19AA1615AB4040B68D8CD08D8DCD8E8DCD8C8CCE8B8AD08B8AD35654C114
295 | 12A62524A66564BE8B8BCC8F8ECE7978C73635B11210A81614A86462C18D8CCD
296 | 8C8BCC8C8BCC8C8BCC8B8ACC6666C11D1BAB0C0AB10A08B80705BA0705BA0605
297 | BA0604BA0604B90604B90604B90604B90504B90503B80503B80503B84341CAFE
298 | FEFFDCDCF71816CB1210C91210C91210C91210C8110FC8110FC8110FC7110FC7
299 | 100EC7100EC61210C41816BC1816AC605EBCE8E8F4FFFFFFFFFFFFFFFFFFFFFF
300 | FFA7A6DA1A199D6A69BCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA09FD724
301 | 229FC7C6E5FFFFFFFFFFFFFFFFFFFFFFFFEDEDF75958B8111097B9B9DEFFFFFF
302 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8B8E21614AC0B09B60806BA0705BB0705
303 | BB0705BA0705BA0604BA0604BA0604B90604B90604B90504B90503B81412BCE7
304 | E7F8AFAEED1311CA1311CA1311CA1210C91210C91210C91210C81110C8110FC8
305 | 110FC8110FC71412C21816B43E3DB0F0F0F7FFFFFFC9C9E86766BE9E9DD5FFFF
306 | FFFFFFFF7A79C26160B3FFFFFFE2E2F16666BC5F5EC25D5DC65E5EC43A39AD82
307 | 81C5FFFFFFF4F4FA8887C86968B9D3D3EAFFFFFFC9C9E51C1A91B3B3D9FFFFFF
308 | B2B2D7605FAF6564B19998C8FCFCFDFFFFFF5251C00503B20907BB0806BB0705
309 | BB0705BB0705BB0705BA0705BA0605BA0604BA0604B90604B90604B90604B9BB
310 | BBEC8482E41412CB1412CB1311CB1311CA1311CA1311CA1210C91210C91210C9
311 | 1110C81110C71715C01514AC8B8ACDFFFFFFE9E8F53332AE100FA81210A8B6B6
312 | E3F0EFF98180C86362B5FFFFFFD4D4EC1917A50C0BAF0B09B50F0DAF12119FAE
313 | ADD9FFFFFFA4A4D70E0D9C0F0E99605FB5FFFFFFE2E2F12D2C94B2B2D6FFFFFF
314 | 8A8AC10E0D841716873F3F96F1F1F7FFFFFF6161C70301B20907BC0806BC0806
315 | BC0806BB0706BB0705BB0705BB0705BA0705BA0705BA0604BA0604B90604B991
316 | 90E0605FDD1412CC1412CC1412CC1412CB1311CB1311CB1311CA1311CA1210CA
317 | 1210C91211C81816C01817ABB0B0DCFFFFFFBABAE31614AD1918BB1513B9312F
318 | BC3735B81716A16A69BBFFFFFFD5D5EE1F1DB11110BD100EC11615B91615A4B4
319 | B4DDFFFFFF8988D0100FA81716A84F4EB2FFFFFFEAEAF4323196B2B1D6FFFFFF
320 | D8D8EAAFAED5B2B1D6DFDFEDFEFEFED8D8EE2927B30A08B70907BD0907BD0806
321 | BD0806BC0806BC0806BC0806BC0705BB0705BB0705BB0705BB0705BA0604BA6B
322 | 69D64949D81513CD1513CD1513CC1412CC1412CC1412CB1412CB1312CB1311CA
323 | 1311CA1412C91917C01A18ACB5B4DEFFFFFFB1B0DF1513AC1B19BB1B1ABC110F
324 | B2110FAB1817A36B6ABCFFFFFFD5D5EE1E1DB3110FBF0F0DC31614BA1615A6B3
325 | B3DDFFFFFF8B8AD30F0EAC1614AB4F4EB4FFFFFFEAEAF4333296B2B1D6FFFFFF
326 | FFFFFFFFFFFFFFFFFFFFFFFFF6F6FA5756B70D0AAA0D0CBA0907BE0907BE0907
327 | BD0907BD0907BD0806BC0806BC0806BC0806BC0806BB0705BB0705BB0705BB4F
328 | 4ECE3B39D61614CE1614CD1513CD1513CD1513CC1513CC1412CC1412CC1412CB
329 | 1412CB1311CA1816C31614AF9E9ED7FFFFFFCECDE9211FA5211FAF1918AB5352
330 | BF6968C63837AB6968BAFFFFFFD6D5EE1F1DB3110FBF100EC41715BB1715A6B3
331 | B3DDFFFFFF8B8AD3100EAC1615AC4F4EB4FFFFFFEAEAF4333396B2B2D6FFFFFF
332 | A4A3CF44439F4C4BA1AAAAD1FFFFFFB4B3E1110FAD0C0BBB0A08BE0A08BE0A08
333 | BE0907BE0907BD0907BD0907BD0907BD0806BC0806BC0806BC0806BB0706BB3D
334 | 3CCA302FD41614CF1614CE1614CE1614CE1513CD1513CD1513CD1513CC1412CC
335 | 1412CC1412CB1715C61514B7605FC2FFFFFFFCFCFD7D7CC425249F4544AEE2E2
336 | F2FFFFFF8A8ACC5F5EB6FFFFFFD5D5EF1E1DB51210C1110FC51715BC1615A8B3
337 | B2DEFFFFFF8B8AD4100EAE1614AE4F4EB6FFFFFFEAEAF5323198B1B1D8FFFFFF
338 | 9090C51C1B8C201F8C7E7DBBFFFFFFDEDEF31B1AB40B0ABB0B09BF0A08BF0A08
339 | BF0A08BE0A08BE0907BE0907BD0907BD0907BD0907BD0806BC0806BC0806BC32
340 | 30C72B29D41715CF1715CF1715CF1614CE1614CE1614CE1614CD1513CD1513CD
341 | 1513CC1513CC1614CA1A18C21E1CB3ADACDFFFFFFFFFFFFFDADAECF4F4F9FFFF
342 | FFE9E9F63534AF6463BFFFFFFFD9D9F11D1BB81210C2110FC51614BF1312ADB5
343 | B4E1FFFFFF8C8BD70E0CB21412B24D4CBBFFFFFFEEEEF72D2BA0B2B2DBFFFFFF
344 | EBEBF4D0D0E7D4D4E9FCFBFCFFFFFFAEADE20C0AB30D0BBE0B09C00B09C00B09
345 | BF0A08BF0A08BF0A08BE0A08BE0A08BE0907BE0907BD0907BD0907BD0807BC2C
346 | 2AC62C2AD41816D01715D01715D01715CF1715CF1614CE1614CE1614CE1614CE
347 | 1513CD1513CD1513CC1816C81917C02221B78786D4D4D3EFF3F3FBEBEBF8B5B4
348 | E64342C10F0DB05C5BC8EAEAF8C3C2EC1918BE110FC51210C71513C20F0DB5A1
349 | A0DFEAEAF87D7CD70B09B8100FB84342C1EAEAF8D6D5F02322AC9F9ED9EBEAF7
350 | EFEFF9F4F4FBF3F3FBE8E8F7AEADE22A29BA0C0ABA0D0BC00C0AC10B09C00B09
351 | C00B09C00B09BF0A09BF0A08BF0A08BF0A08BE0A08BE0907BE0907BD0907BD2E
352 | 2CC73130D61816D11816D11816D01715D01715D01715CF1715CF1715CE1614CE
353 | 1614CE1614CD1513CE1614CC1816C91917C31311BA2120BA2C2ABC2927BD1614
354 | BB1311BF1A18C11E1CC02624C12220C21614C41311C71210C71412C51614C020
355 | 1EBE2422BE1C1AC01311C11412C01917BD2322BD2221BC1917B9211FB82625B7
356 | 2827B52827B42726B52322B60F0DB40D0BBB0F0DC00D0BC10C0AC10C0AC10C0A
357 | C00C0AC00B09C00B09C00B09BF0B09BF0A08BF0A08BF0A08BE0A08BE0908BE33
358 | 32C83E3CD91917D21917D11816D11816D11917CE1B19CC1B19CA1C1AC81C1AC8
359 | 1B1AC81B19C91917CA1715CB1715CB1816CA1A19C61B19C11A18BF1A18BF1B19
360 | C11B19C21B19C21918C11614C01614C11816C21816C31816C21816C11917BF17
361 | 15BD1513BD1513BF1614C11614C01413C0110FBF100EC01211C1100FC0100FBE
362 | 1210BB1311BA1311B91210BA1110BE100EC00E0CC10F0DC0100EBE100EBD100E
363 | BD100EBD0F0EBD0E0DBD0D0BBE0C0ABF0B09C00B09BF0A08BF0A08BF0A08BE41
364 | 3FCD4F4DDD1917D31917D21917D21B19CF1E1CCA1B1AC11917B91B19B61B1AB5
365 | 1A19B61917B81917BD1C1AC41B19C81C1AC71B19C11715B71917B41917B31917
366 | B31817B31716B41514B61513B81D1BBC1615B61615B21716B01715B01614B115
367 | 13B11312B31614B61917B7110FB40E0DB81412C01210C4100EC6100EC6110FC4
368 | 1312C11614BB0F0DB30D0BB4100EBB1210C0110FBF1412BB0E0CB20D0BB01210
369 | B31615B40C0BAF0A09B10E0CB90F0DBE0B0AC00B09C00B09C00B09C00B09BF53
370 | 52D26867E31A18D31A18D31A18D21D1BCD1E1CC25251C67B7ACC7A79CA7A79CA
371 | 7A79CB7776CB5554C22422B71C1BBC1F1DBD3E3DBF7878CC7978C87978C97877
372 | CA7876CA7675CB7675CD6B6ACB2F2DB36B6AC57878C87877C77776C77675C975
373 | 74CA7574CC5958C24242B77574CB706FCE2725BF1110C2110FC7100EC71210C4
374 | 1513BD2A28B76F6ECA7372CC3B3AC1100FBB1413BB201EB46665C67473CA4C4B
375 | B93230AE7271C87170CB312FBD0D0ABB0D0BC00C0AC10C0AC10B0AC00B09C071
376 | 70DA8D8CEA1B19D41A19D41A18D31F1DCC1B19B8A09FDAFFFFFFFFFFFFFFFFFF
377 | FFFFFFFFFFFFFFFFFFC3C3E83D3CB81B19AC6E6DC5FFFFFFFFFFFFFFFFFFFFFF
378 | FFFFFFFFFFFFFFFFFFFFFDFDFD4241AFE6E7F3FFFFFFFFFFFFFFFFFFFFFFFFFF
379 | FFFFFFFFFFC7C7E67877C2FFFFFFFFFFFF4342C1100EBE1210C7110FC71312C4
380 | 1513B74342B7FFFFFEFFFFFF7D7CD20B09B41614B72C2AAFE9E9F4FFFFFFA0A0
381 | D25352ADFFFFFFFFFFFF6160C80906B60E0CC10C0BC20C0AC10C0AC10C0AC198
382 | 97E4B7B6F21B19D51B19D51B1AD3201ECB1E1DB49D9DD6FFFFFFF5F4FA8B8ACE
383 | 7E7ECCA5A4D9F8F7FBFFFFFFC5C4E62523A26F6EBDFFFFFFFFFFFF9F9FD47C7B
384 | CC7E7ED27E7CD37D7CD37473CC3D3BABE1E1F0FFFFFFCAC9E77A7AC97D7CD17C
385 | 7AD37D7CD15D5CBD7A7AC1FFFFFFFFFFFE4847BA1715B71816C11614C31715C1
386 | 1917B44645B2F7F7FBFFFFFF7C7BCE100EB11B19B3312FAAE2E2F1FFFFFF9E9E
387 | CD5655A7FFFFFFFFFFFF615FC40B09B50E0CC20D0BC20D0BC20D0BC20C0AC1C3
388 | C2F0E2E1FA2321D71C1AD51C1AD4211FCB201EB39E9DD5FFFFFFEBEBF62C2BB0
389 | 1A18B21B19A8908FCEFFFFFFFFFFFF5A5AB16A6AB6FFFFFFFFFFFF5453B51614
390 | A91918B31816B61715B61917AF3837AAE3E2F1FFFFFF9F9ED60E0DA91210B810
391 | 0EBB1311B71412A67D7CC3FFFFFFFFFFFE4645AC1615A51815AF1412B31918B7
392 | 1D1CB04948B0F7F7FAFFFFFF7E7EC81614A6201FAA3534A2E2E2EFFFFFFF9F9F
393 | CC5857A5FFFFFFFFFFFF6160C40C0AB60F0DC20E0CC30D0BC30D0BC31F1DC7EC
394 | ECFAFCFCFF4A49DE1C1AD61D1BD52220CB201FB49E9DD5FFFFFFECECF73432BE
395 | 211FC22120B85553B8FCFCFDFFFFFF8989C46968B2FFFFFFFFFFFF5857B41B19
396 | A61E1DB01C1AB51A19B6211FB33938ACE2E2F1FFFFFFA2A1DC1615B81917C817
397 | 15CA1B19C41A19AF7C7BC5FFFFFFFFFFFFABABD69796D09998D47D7CCC3D3BB4
398 | 1C1BA44C4BADF7F7FAFFFFFF8686C526259E2F2EA241409EE4E3EFFFFFFF9F9F
399 | CC5958A5FFFFFFFFFFFF6261C50D0BB60F0EC30E0CC40E0CC40E0CC34E4DD3FF
400 | FFFFFFFFFF8786EA1D1BD71D1CD62220CC201FB59E9DD6FFFFFFECECF73331C1
401 | 201EC72220BE4847B5F3F3F8FFFFFF9897CB6A6AB2FFFFFFFFFFFFB9B9DDA09F
402 | D6A2A1DBA09FDDA0A0DF5857C33332A9E2E2F1FFFFFFA2A2DD1514BB1816CB16
403 | 14CE1B19C71A18B17C7BC6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9E9F5
404 | 5554B14847A5F7F7FAFFFFFFEEEEF6E1E1F0E2E2F0E5E4F0FBFBFDFFFFFF9F9F
405 | CC5958A6FFFFFFFFFFFF6261C50D0BB7100EC40F0DC50E0DC40E0CC49190E4FF
406 | FFFFFFFFFFCFCFF71E1CD81E1CD62321CD2120B69E9ED6FFFFFFECECF73533BF
407 | 2220C42423BA4B4BB4F5F5F9FFFFFF9494CA6A69B3FFFFFFFFFFFFFFFFFFFFFF
408 | FFFFFFFFFFFFFFFFFFFF8685D22D2CA7E2E2F1FFFFFFA2A2DD1614BB1917CC16
409 | 14CE1B19C71B19B27C7BC6FFFFFFFEFEFE7D7CC25D5CB69392CBF6F6FAFFFFFF
410 | C5C4E14E4DA2F6F6FAFFFFFFF1F1F8E5E5F4E7E6F4E9E8F4FCFCFDFFFFFF9F9F
411 | CC5958A6FFFFFFFFFFFF6261C50E0CB8110FC50F0DC50F0DC51412C6DADAF6FF
412 | FFFFFFFFFFFCFCFF4A49E01E1DD72322CE2120B69E9ED6FFFFFFECECF73938B8
413 | 2927BA2422AD6564B9FEFEFEFFFFFF7B7AC06968B5FFFFFFFFFFFF8484C45958
414 | B75C5BBF5A58C15856C43B3AB83837ABE3E2F1FFFFFFA2A2DD1715BC1917CC17
415 | 15CF1C1AC81B1AB37C7BC6FFFFFFFEFEFE4948AD201E9E201F93B8B8D9FFFFFF
416 | E4E4F15C5BA7F4F4F9FFFFFF8382C91F1DA62927AA3C3BA3E3E3F0FFFFFF9F9F
417 | CD5958A7FFFFFFFFFFFF6362C60E0DB8110FC6100EC6100EC64D4CD5FFFFFFFF
418 | FFFFFFFFFFFFFFFFA1A0EF1F1DD82322CF2220B89E9ED7FFFFFFECECF63A39AB
419 | 2928A93C3BABC3C3E3FFFFFFFAFAFC4746B26C6BBCFFFFFFFFFFFF5958AF1E1C
420 | 9E2321A7201FAB1E1CAF2221AE3B3AADE3E2F1FFFFFFA2A2DE1715BD1A18CE18
421 | 16D01C1AC91B19B47C7BC8FFFFFFFEFEFE4E4DAD24229D3B3AA0D3D3E8FFFFFF
422 | DEDEEE5554A9F5F4FAFFFFFF7D7CCE1312B11E1CB43331AAE2E2F0FFFFFF9F9F
423 | CE5857A8FFFFFFFFFFFF6361C70E0CBA1210C6110FC7100FC7ADACEBFFFFFFFF
424 | FFFFFFFFFFFFFFFFEFEFFD3230DF2422D11F1EBD9E9EDBFFFFFFFDFDFDC9C8E6
425 | C5C4E4EAEAF5FFFFFFFFFFFF9796DC1B19AF6F6EC6FFFFFFFFFFFFD3D3EAC3C2
426 | E4C4C4E7C3C3E8C4C4E99B9ADA3735AFE4E3F3FFFFFFA3A2E11614C11A18CE18
427 | 16D11C1ACB1917B97B7ACCFFFFFFFFFFFFCECEE7C1C1E2E5E5F2FFFFFFFFFFFF
428 | 9B9BD54341ACFAFAFCFFFFFF7C7BD4100EBA1B19BD2F2EB4E4E4F3FFFFFF9E9E
429 | D25453AFFFFFFFFFFFFF6261CB0E0CBC1211C7110FC83433D0F8F7FDFFFFFFFF
430 | FFFFFFFFFFFFFFFFFFFFFF9998E4201ED91C1AC59594DEF6F6FBF7F7FBFFFFFF
431 | FFFFFFFBFBFEDAD9F38887DC2422C31C1AC16665CFF5F5FCF6F6FBFDFDFFFFFF
432 | FFFFFFFFFFFFFFFFFFFFC7C6ED2F2EB9D7D7F1F5F5FC9999E31412C61A18D019
433 | 17D21B1ACE1614C07271D1F5F5FBF6F5FBFEFEFFFFFFFFFBFBFEE6E6F6A4A3E1
434 | 2624B6403EBCEDEDF8F5F5FC7473D70F0DC01817C22928BDD7D7F1F5F5FB9393
435 | D64B49BAF5F5FBF5F5FB5B5ACE0C0AC01311C81210C9A09FE9FFFFFFFFFFFFFF
436 | FFFFFFFFFFFFFFFFFFFFFFF4F4F63C3ADC2220D43130CD3E3DCB403EC8403EC7
437 | 3F3EC73E3CC92B29C71A18C8211FCF2220CD2B2ACA3D3BC83F3DC53F3DC43F3D
438 | C43E3DC43D3CC53B3AC73432C82524C43735C73938C92D2BCB1D1BCD1B1AD11B
439 | 19D11C1ACF1D1BCA2928C53938C43B3AC03B3AC03B3AC03A39C12F2EC01817BE
440 | 1B1AC12321C23533C43533C52523C61816C71917C71D1BC23130C33533C32A28
441 | C02220BE3432C23230C51F1DC41412C71311C93B3AD3F8F8FDFFFFFFFFFFFFFF
442 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFB8B8DE1E1CDF211FD51F1DD21F1DD01F1DD0
443 | 1F1DD01E1DD0211FD12220D0211ECD2220CA2220C71F1DC3201EC1201EC1201E
444 | C0201EC01F1DC01E1CC11F1DC32120C41D1CC11C1AC21D1CC31F1EC61D1CC71D
445 | 1CC71E1CC61E1DC41E1CC11C1ABE1C1BBD1C1ABC1C1ABC1B1ABD1C1ABE1D1CC0
446 | 1D1BC11D1BBF1917BD1817BD1A18BF1B1AC11B19C11A18C11513C11614C41614
447 | C61715C71311C51210C61412C81413CA1311CBBBBAF0FFFFFFFFFFFFFFFFFFFF
448 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFE605FE8201EDC201EDB201EDB201EDA
449 | 1F1DDA201ED92221D22A27CC453ED14941CC4942C94942C94942C94A42C94942
450 | C84942C84842C84841C84841C84841C7403DBF3D3BBC3C3BBC3C3BBC3C3BBB3C
451 | 3ABB3C3ABB3B3ABA3B3ABA3B3ABA3B39BA3B39B94140BA4644BA4644BA4544B9
452 | 4544B94544B94544B84543B84543B74443B74341B9403FBE3231C31817C71615
453 | CC1513CD1513CC1513CC1513CC1412CC6968DEFFFFFFFFFFFFFFFFFFFFFFFFFF
454 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE3E3FB3331DF211FDC201EDC201EDB
455 | 201EDB2220D7221ED05B68BB51A457469B50469C51469C51469C51469C51469C
456 | 51469C51469C51469C51469C50459A4FC1D7CCECECF6ECECF6ECECF6ECEBF6EC
457 | EBF6ECECF6ECEBF6ECEBF6ECEBF6ECEBF6ECEBF68C8CF64343F64646F64646F6
458 | 4646F64646F64646F64646F64646F64646F54646F64645F75D5DF33635C81413
459 | CA1614CE1614CE1513CD1513CD3231D3ECEBFBFFFFFFFFFFFFFFFFFFFFFFFFFF
460 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8B8F4211FDD211FDC211FDC
461 | 211FDC2321D72620CE667CAC0890000081000082000082000082000082000082
462 | 00008200008200008200008200007E00BEE3BCFFFFFFFFFFFFFFFFFFFFFFFFFF
463 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6868FF0000FF0000FF0000FF
464 | 0000FF0000FF0000FF0000FF0000FF0000FF0000FF0000FF3434FE4847CA1513
465 | C91614CF1614CF1614CE1B19CFC4C4F2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
466 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9594EF2220DD211FDD
467 | 211FDC2422D72822CD657AAC1091080083000084000084000084000084000084
468 | 00008400008400008400008400008000BDE0BDFFFFFFFFFFFFFFFFFFFFFFFFFF
469 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6B6BFF0000FF0000FF0000FF
470 | 0000FF0000FF0000FF0000FF0000FF0000FF0000FF0000FF3838FC4746C91614
471 | C91715D01715CF1715CF9F9FEBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
472 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8382EC2220DD
473 | 2220DD2421D82720D0647AAF0B8F020081000082000082000082000082000082
474 | 00008200008200008200008200007E00BCE0BCFFFFFFFFFFFFFFFFFFFFFFFFFF
475 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6868FF0000FF0000FF0000FF
476 | 0000FF0000FF0000FF0000FF0000FF0000FF0000FF0000FF3434FC4745CC1514
477 | CA1816D01816D08C8BE8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
478 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFEFF7877EB
479 | 2220DE2321DB231ED66275B833A5272598202599202599202599202599202599
480 | 20259920259920259920259920249520CBE9C6FFFFFFFFFFFFFFFFFFFFFFFFFF
481 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8383FF2323FF2525FF2525FF
482 | 2525FF2525FF2525FF2525FF2525FF2525FF2525FF2525FF4F4FFD403FCF1614
483 | CD1816D1807FE6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
484 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
485 | 8483F1211FE22321D83434D3595DCE595FCB5A5FCA595FCA595FC9595FC9595F
486 | C9595EC9595EC9585EC9585EC8585EC86262D16563D46563D46463D46463D464
487 | 63D46463D36463D36463D36462D36362D36362D25C5BD25554D25655D25655D2
488 | 5655D25655D25655D15654D15654D15655D15554D25453D44947D51E1CCE1614
489 | D68B8AECFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
490 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
491 | FFFFFFA09FDF2422E02220DB1F1BD9201CD7201CD7201CD6201CD61F1BD61F1B
492 | D51F1BD51F1BD51F1BD51E1BD41E1BD41B19D11A19D01A18CF1A18CF1A18CF19
493 | 18CF1917CE1917CE1917CE1917CE1816CD1816CD1A18CD1C1ACD1B19CC1B19CC
494 | 1B19CC1A18CB1A18CB1A18CB1A18CA1918CA1918CA1816CB1615CE1F1DD4AAA9
495 | DDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
496 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
497 | FFFFFFFFFFFFBDBDF43A38E42321DE2321DE2220DE2220DE2220DD2220DD2220
498 | DD2220DC211FDC211FDC211FDC211FDB211EDB201EDB201EDB201EDA201EDA1F
499 | 1DDA1F1DDA1F1DD91F1DD91E1CD91E1CD81E1CD81E1CD81E1CD81D1CD71D1BD7
500 | 1D1BD71D1BD61D1BD61C1AD61C1AD51C1AD51B1AD51B19D53836DDC7C7EFFFFF
501 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
502 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
503 | FFFFFFFFFFFFFFFFFFE5E5FB6F6EEA2321DF2321DF2321DE2220DE2220DE2220
504 | DE2220DD2220DD211FDD211FDD211FDD211FDC211FDC201EDC201EDB201EDB20
505 | 1EDB201EDB1F1DDA1F1DDA1F1DDA1F1DDA1F1DD91E1CD91E1CD91E1CD81E1CD8
506 | 1D1BD81D1BD81D1BD71D1BD71C1AD71C1AD61C1AD67473E6ECECFCFFFFFFFFFF
507 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
508 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
509 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBCBBF54A48E52321DF2321DF2321DF2220
510 | DE2220DE2220DE2220DE2220DD2220DD211FDD211FDD211FDC211FDC211FDC20
511 | 1EDC201EDB201EDB201EDB201EDB1F1DDA1F1DDA1F1DDA1F1DD91E1CD91E1CD9
512 | 1E1CD91E1CD81D1BD81D1BD81D1BD74B49DFC3C2F4FFFFFFFFFFFFFFFFFFFFFF
513 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
514 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
515 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6F5FEA6A5F24846E42321DF2321
516 | DF2321DF2321DE2321DE2220DE2220DE2220DD2220DD2220DD211FDD211FDC21
517 | 1FDC211FDC211FDC201EDB201EDB201EDB201EDB201EDA1F1DDA1F1DDA1F1DD9
518 | 1F1DD91E1CD91E1CD94A49E0AAAAF0F9F9FEFFFFFFFFFFFFFFFFFFFFFFFFFFFF
519 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
520 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
521 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F7FEB1B0F46261
522 | E82B29E02321DF2321DF2321DF2321DE2220DE2220DE2220DE2220DD2220DD21
523 | 1FDD211FDD211FDC211FDC211FDC201EDC201EDB201EDB201EDB201EDA1F1DDA
524 | 2A28DC6463E5B6B5F3FBFBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
525 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
526 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
527 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
528 | FFDDDCFA9C9BF16361E83937E22321DF2321DF2321DE2220DE2220DE2220DE22
529 | 20DD2220DD2220DD211FDD211FDC211FDC211FDC211FDC3837DF6564E6A09FF0
530 | E0E0FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
531 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
532 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
533 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
534 | FFFFFFFFFFFFFFFFFFFFF0F0FDCAC9F7AAA9F38887EE6867E95856E64B49E447
535 | 46E44746E44B49E45856E56968E88988EDABABF2CDCCF7F3F3FDFFFFFFFFFFFF
536 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
537 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
538 | FFFF}
539 | Proportional = True
540 | OnClick = GoToWebsite
541 | end
542 | object lblTitle: TLabel
543 | Left = 8
544 | Top = 22
545 | Width = 260
546 | Height = 27
547 | Cursor = crHandPoint
548 | Caption = 'PKCS#7 Extractor demo'
549 | Font.Charset = ANSI_CHARSET
550 | Font.Color = clWindowText
551 | Font.Height = -24
552 | Font.Name = 'Arial'
553 | Font.Style = []
554 | ParentFont = False
555 | OnClick = GoToWebsite
556 | end
557 | end
558 | object bBrowse: TButton
559 | Left = 568
560 | Top = 89
561 | Width = 87
562 | Height = 48
563 | Hint = 'Load Libeay32.dll from another folder.'
564 | Anchors = [akTop, akRight]
565 | Caption = 'Browse...'
566 | ParentShowHint = False
567 | ShowHint = True
568 | TabOrder = 0
569 | OnClick = ChangeLibrary
570 | end
571 | object odInput: TOpenDialog
572 | DefaultExt = '*.p7m'
573 | Filter = 'PCKS#7 Message file (*.p7m)|*.p7m|Any file (*.*)|*.*'
574 | Title = 'Select PKCS#7 Message file...'
575 | Left = 336
576 | Top = 8
577 | end
578 | object sdOutput: TSaveDialog
579 | Title = 'Extract file to...'
580 | Left = 384
581 | Top = 8
582 | end
583 | object popupEmpty: TPopupMenu
584 | Left = 528
585 | Top = 8
586 | end
587 | end
588 |
--------------------------------------------------------------------------------
/Demo/MainFormUnit.pas:
--------------------------------------------------------------------------------
1 | unit MainFormUnit;
2 |
3 | {$IFDEF FPC}
4 | {$MODE Delphi}
5 | {$ENDIF}
6 |
7 | {***************************************************************************}
8 | { }
9 | { PKCS#7 Extractor for Delphi Demo Application }
10 | { Version 1.3.0.0 released June, 15th 2021 }
11 | { }
12 | { Copyright (C) 2018 Delphi Club Italia }
13 | { http://www.delphiclubitalia.it }
14 | { }
15 | { Original authors: }
16 | { Christian Cristofori github@christiancristofori.it }
17 | { Giancarlo Oneglio giancarlo.oneglio@gmail.com }
18 | { }
19 | {***************************************************************************}
20 | { }
21 | { Licensed under the GNU Lesser General Public License, Version 3; }
22 | { you may not use this file except in compliance with the License. }
23 | { }
24 | { This is free software: you can redistribute it and/or modify it under }
25 | { the terms of the GNU Lesser General Public License as published by the }
26 | { Free Software Foundation, either version 3 of the License, or (at your }
27 | { option) any later version. }
28 | { }
29 | { This is distributed in the hope that it will be useful, but WITHOUT }
30 | { ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or }
31 | { FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public }
32 | { License for more details. }
33 | { }
34 | { You should have received a copy of the GNU Lesser General Public }
35 | { License along with this software. }
36 | { If not, see . }
37 | { }
38 | {***************************************************************************}
39 |
40 | interface
41 |
42 | uses
43 | Forms, Classes, Dialogs, Controls, ExtCtrls, StdCtrls, Menus, Graphics,
44 | PKCS7Extractor;
45 |
46 | type
47 | TFormMain = class(TForm)
48 | bExtract: TButton;
49 | odInput: TOpenDialog;
50 | sdOutput: TSaveDialog;
51 | memoSource: TMemo;
52 | popupEmpty: TPopupMenu;
53 | panelLibrary: TPanel;
54 | pnlSubHeader: TPanel;
55 | pnlHeader: TPanel;
56 | imgDCILogo: TImage;
57 | lblTitle: TLabel;
58 | labelLocation: TLabel;
59 | labelVersion: TLabel;
60 | bBrowse: TButton;
61 | labelLocationL: TLabel;
62 | labelVersionL: TLabel;
63 | lblVerification: TLabel;
64 | lblSignatureMode: TLabel;
65 | lblSignatureModeValue: TLabel;
66 | lblVerificationValue: TLabel;
67 | procedure FormCreate(Sender: TObject);
68 | procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
69 | procedure ChangeLibrary(Sender: TObject);
70 | procedure ExtractPKCS7(Sender: TObject);
71 | procedure GoToWebsite(Sender: TObject);
72 | private
73 | procedure RefreshPanel;
74 | end;
75 |
76 | var
77 | FormMain: TFormMain;
78 |
79 | implementation
80 |
81 | {$R *.dfm}
82 |
83 | uses
84 | {$IFnDEF FPC}
85 | ShellAPI,
86 | {$ELSE}
87 | LCLIntf, LCLType,
88 | {$ENDIF}
89 | Windows, SysUtils, ShlObj, ActiveX;
90 |
91 | {$IFDEF FPC}
92 | type
93 | BFFCALLBACK = function(Wnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer stdcall;
94 | TFNBFFCallBack = type BFFCALLBACK;
95 | {$ENDIF}
96 |
97 | var
98 | mInitialFolder: String = ''; // NOT THREAD SAFE! (WHO CARES HERE?)
99 |
100 | function lpfnBrowseProc(Wnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer; stdcall;
101 | begin
102 | Result := 0;
103 | if (Wnd <> 0) and (uMsg = BFFM_INITIALIZED) then
104 | SendMessage(Wnd, BFFM_SETSELECTION, WPARAM(True), Windows.LPARAM(Pointer(mInitialFolder)))
105 | end;
106 |
107 | function BrowseForFolder(const AHandle: THandle; const AMessage: String; var AFolder: String): Boolean;
108 | var
109 | BrowseInfo: TBrowseInfo;
110 | pidlStart, pidlSelected: PItemIDList;
111 | Malloc: IMalloc;
112 | szDisplayNameBuff: Array[0..MAX_PATH] of Char;
113 | begin
114 | Result := False;
115 | mInitialFolder := AFolder;
116 | SHGetSpecialFolderLocation(AHandle, 0, pidlStart);
117 | try
118 | SHGetMalloc(Malloc);
119 |
120 | StrPCopy(szDisplayNameBuff, AMessage);
121 | FillChar(BrowseInfo, SizeOf(TBrowseInfo), #0);
122 | BrowseInfo.hwndOwner := AHandle;
123 | BrowseInfo.pidlRoot := pidlStart;
124 | BrowseInfo.pszDisplayName := szDisplayNameBuff;
125 | BrowseInfo.lpfn := TFNBFFCallBack(@lpfnBrowseProc);
126 | BrowseInfo.lParam := 0;
127 | BrowseInfo.lpszTitle := PChar(AMessage);
128 | BrowseInfo.ulFlags := $00000051;
129 |
130 | pidlSelected := SHBrowseForFolder(BrowseInfo);
131 | if pidlSelected <> nil then begin
132 | if SHGetPathFromIDList(pidlSelected, BrowseInfo.pszDisplayName) then
133 | AFolder := StrPas(BrowseInfo.pszDisplayName);
134 | Malloc.Free(pidlSelected);
135 | Result := True
136 | end
137 | finally
138 | Malloc.Free(pidlStart)
139 | end
140 | end;
141 |
142 | procedure TFormMain.ChangeLibrary(Sender: TObject);
143 | var
144 | F: String;
145 | begin
146 | // Loads last used path.
147 | F := labelLocation.Caption;
148 | // If path doesn't exist, uses application location.
149 | if not DirectoryExists(F) then
150 | F := ExtractFilePath(Application.ExeName);
151 | // Executes a "Browse for Folder" dialog.
152 | if BrowseForFolder(Handle, 'Select a folder to load libeay32.dll from.', F) and DirectoryExists(F) then begin
153 | // Is a library is already loaded, shows a message.
154 | if GetModuleHandle(LIBEAY32_LIBRARY) > 0 then
155 | Application.MessageBox('WARNING: unloading a DLL can be dangerous if you use multiple libraries to manage it''s functions.'#13#10 +
156 | 'This is a controlled example and we don''t have any other library refering to this module in memory or to it''s functions, but '+
157 | 'you should always pay attention because references are not checked again and this will lead to serious crashes.', 'WARNING', MB_ICONEXCLAMATION);
158 | // Uninitializes the wrapper.
159 | PKCS7Extractor.Unload;
160 | // Frees currently loaded library (if any).
161 | FreeLibrary(GetModuleHandle(LIBEAY32_LIBRARY));
162 | // Sets wrapper loading folder.
163 | PKCS7Extractor.SetFolder(F);
164 | // Initializes the wrapper again.
165 | PKCS7Extractor.Load
166 | end;
167 | RefreshPanel
168 | end;
169 |
170 | procedure TFormMain.ExtractPKCS7(Sender: TObject);
171 | const
172 | SIGNATURE_MODE: Array[TSignatureMode] of String = ('(unknown)', 'PKCS#7', 'CMS');
173 | VERIFY_STATUS: Array[TVerifyStatus] of String = ('(unknown/invalid)', 'full verification', 'partial verification');
174 | var
175 | sFolder, sFile, sExtension, S: String;
176 | begin
177 | // Pushes current directory.
178 | sFolder := GetCurrentDir;
179 | try
180 | // Clearing the UI.
181 | memoSource.Lines.Clear;
182 | lblSignatureModeValue.Caption := SIGNATURE_MODE[smUnknown];
183 | lblVerificationValue.Caption := '(idle)';
184 | // Check or tries to initialize the wrapper.
185 | if not PKCS7Extractor.Load then begin
186 | Application.MessageBox('Library Libeay32.dll has not been found or an error occurre while loading.', 'Extraction report', MB_ICONEXCLAMATION);
187 | Exit
188 | end;
189 | RefreshPanel;
190 | // Executed "Open File" dialog.
191 | if not odInput.Execute then
192 | Exit;
193 | // If verification fails, shows an error.
194 | if Verify(odInput.FileName) = vsUnknown then begin
195 | Application.MessageBox('Selected file is not a supported PKCS#7 message file.', 'Extraction report', MB_ICONEXCLAMATION);
196 | lblVerificationValue.Caption := VERIFY_STATUS[vsUnknown];
197 | Exit
198 | end;
199 | // Removes the last extension from filename. Since we expect this file to have the .p7m
200 | // extension appended to the original extension, this will mostly restore the original filename.
201 | sFile := ExpandFilename(ChangeFileExt(odInput.FileName, ''));
202 | // If destination file already exists, ask for overwrite permission.
203 | if FileExists(sFile) then
204 | case Application.MessageBox(PChar(Format('File "%s" alredy exists, do you want to overwrite?', [sFile])), 'File already exists.', MB_ICONQUESTION or MB_YESNOCANCEL) of
205 | ID_NO: begin
206 | // User asked to avoid overwrite, giving him the opportunity to save the
207 | // file to another location / change it's name.
208 | sExtension := ExtractFileExt(sFile);
209 | if Copy(sExtension, 1, 1) = '.' then
210 | Delete(sExtension, 1, 1);
211 | sExtension := UpperCase(sExtension);
212 | sdOutput.InitialDir := ExtractFilePath(sFile);
213 | sdOutput.FileName := ExtractFileName(sFile);
214 | sdOutput.Filter := 'Any file (*.*)|*.*';
215 | if Length(sExtension) > 0 then
216 | sdOutput.Filter := Format('%s file (*.%s)|*.%s|Any file (*.*)|*.*', [sExtension, sExtension, sExtension]);
217 | sdOutput.DefaultExt := sExtension;
218 | // Shows a "Save File" dialog, if user cancels this, any operation will be cancelled.
219 | if not sdOutput.Execute then
220 | Exit;
221 | sFile := sdOutput.FileName
222 | end;
223 | ID_CANCEL: Exit // User aborts operation.
224 | end;
225 | lblVerificationValue.Caption := VERIFY_STATUS[vsUnknown];
226 | // Extracts file.
227 | if Extract(odInput.FileName, sFile) then begin
228 | // We now know we can get a valid signature mode for this file.
229 | lblSignatureModeValue.Caption := SIGNATURE_MODE[SignatureMode(odInput.FileName)];
230 | S := '';
231 | // Extracts the whole file as a string.
232 | if ExtractToString(odInput.FileName, S) then
233 | memoSource.Lines.Text := S;
234 | // And get verification status for the file.
235 | lblVerificationValue.Caption := VERIFY_STATUS[Verify(odInput.FileName)];
236 | Application.MessageBox(PChar(Format('Content of the PKCS#7 message file has been extracted and saved successfully to file "%s".', [sFile])), 'Extraction report', MB_ICONINFORMATION);
237 | end else begin
238 | // Extraction failed.
239 | Application.MessageBox('Error while extracting data from PKCS#7 message file.', 'Extraction report', MB_ICONEXCLAMATION)
240 | end
241 | finally
242 | // Pops original directory.
243 | SetCurrentDir(sFolder)
244 | end
245 | end;
246 |
247 | procedure TFormMain.FormCreate(Sender: TObject);
248 | begin
249 | memoSource.Lines.Clear;
250 | memoSource.Font.Name := 'Courier New';
251 | PKCS7Extractor.Load;
252 | RefreshPanel
253 | end;
254 |
255 | procedure TFormMain.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
256 | begin
257 | if Key = VK_ESCAPE then
258 | Application.Terminate
259 | else
260 | if Key = VK_F1 then
261 | GoToWebsite(Sender)
262 | end;
263 |
264 | procedure TFormMain.GoToWebsite(Sender: TObject);
265 | begin
266 | {$IFDEF FPC}
267 | OpenURL('http://www.delphiclubitalia.it')
268 | {$ELSE}
269 | ShellExecute(0, 'open', 'http://www.delphiclubitalia.it', nil, nil, SW_SHOWMAXIMIZED)
270 | {$ENDIF}
271 | end;
272 |
273 | procedure TFormMain.RefreshPanel;
274 | var
275 | S: String;
276 | begin
277 | // Gets the folder and version of the currently loaded library from the wrapper.
278 | S := PKCS7Extractor.GetFolder;
279 | if Length(S) = 0 then
280 | S := '(unknown)';
281 | labelLocation.Caption := S;
282 | S := PKCS7Extractor.GetVersion;
283 | if Length(S) = 0 then
284 | S := '(unknown)';
285 | labelVersion.Caption := S
286 | end;
287 |
288 | end.
289 |
--------------------------------------------------------------------------------
/Demo/PKCS7ExtractorDemo.dpr:
--------------------------------------------------------------------------------
1 | program PKCS7ExtractorDemo;
2 |
3 | {$IFDEF FPC}
4 | {$MODE Delphi}
5 | {$ENDIF}
6 |
7 | uses
8 | {$IFDEF FPC}
9 | Interfaces,
10 | {$ENDIF }
11 | Forms,
12 | MainFormUnit in 'MainFormUnit.pas' {FormMain},
13 | PKCS7Extractor in '..\Source\PKCS7Extractor.pas';
14 |
15 | {$R *.res}
16 |
17 | begin
18 | Application.Initialize;
19 | Application.CreateForm(TFormMain, FormMain);
20 | Application.Run;
21 | end.
22 |
--------------------------------------------------------------------------------
/Demo/PKCS7ExtractorDemo.lpi:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/Demo/PKCS7ExtractorDemo.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DelphiClubItalia/PKCS7Extractor/6c206348b992cf622f4837ce84475a53e67361be/Demo/PKCS7ExtractorDemo.res
--------------------------------------------------------------------------------
/Demo/PKCS7ExtractorDemo_Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DelphiClubItalia/PKCS7Extractor/6c206348b992cf622f4837ce84475a53e67361be/Demo/PKCS7ExtractorDemo_Icon.ico
--------------------------------------------------------------------------------
/Documentation/pkcs7extractory_demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DelphiClubItalia/PKCS7Extractor/6c206348b992cf622f4837ce84475a53e67361be/Documentation/pkcs7extractory_demo.png
--------------------------------------------------------------------------------
/Documentation/pkcs7extractory_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DelphiClubItalia/PKCS7Extractor/6c206348b992cf622f4837ce84475a53e67361be/Documentation/pkcs7extractory_logo.png
--------------------------------------------------------------------------------
/LEGGIMI.md:
--------------------------------------------------------------------------------
1 | [english version - versione inglese](README.md)
2 |
3 | 
4 | # Libreria PKCS#7 Extractor per Delphi / Free Pascal / Lazarus
5 |
6 | Questo progetto è nato dalla necessità di estrarre semplicemente il contenuto dei file .P7M in alcune applicazioni Delphi.\
7 | Dal 1 gennaio 2019 gli sviluppatori italiani hanno dovuto affrontare il nuovo sistema di Fatturazione Elettronica, divenuto obbligatorio per Legge.\
8 | Le Fatture Elettroniche sono esenzialmente rappresentante in file XML che vengono firmate digitalmente utilizzando la modalità CAdES-BEM.\
9 | Gli sviluppatori che gestiscono fatture devono quindi generare questi file XML ed essere in grado di rileggerli, ma quando si tratta di una fattura firmata digitalmente inglobata in un file .P7M molti di loro hanno trovato la soluzione poco ortodossa di rimuovere l'envelope trovando il tag di apertura e di chiusura XML all'interno del file e copiando quella parte.\
10 | Abbiamo pensato che questo non potesse essere il modo professionale di gestire questa operazione, quindi abbiamo iniziato a studiare e siamo finiti a creare questo progetto.
11 |
12 | ## Indice
13 |
14 | - [Libreria PKCS#7 Extractor per Delphi / Free Pascal / Lazarus](#libreria-pkcs7-extractor-per-delphi-free-pascal-lazarus)
15 | - [Indice](#indice)
16 | - [File inclusi](#file-inclusi)
17 | - [PKCS7Extractor.pas](#pkcs7extractorpas)
18 | - [Che cosa è e come funziona](#che-cosa-e-e-come-funziona)
19 | - [Funzioni esportate](#funzioni-esportate)
20 | - [Classi esportate](#classi-esportate)
21 | - [Evento OnStreamCreate](#evento-onstreamcreate)
22 | - [Applicazione demo](#applicazione-demo)
23 | - [Unit tests](#unit-tests)
24 | - [Salvare un report dei test](#salvare-un-report-dei-test)
25 | - [Ottenere una lista delle librerie OpenSSL](#ottenere-una-lista-delle-librerie-openssl)
26 | - [Eseguire i test solo su alcune versioni di OpenSSL](#eseguire-i-test-solo-su-alcune-versioni-di-openssl)
27 | - [File necessari](#file-necessari)
28 | - [Nota sui file codificati Base64](#nota-sui-file-codificati-base64)
29 | - [Cronologia versioni](#cronologia-versioni)
30 | - [Compatibilità con i compilatori](#compatibilità-con-i-compilatori)
31 | - [Progetti per il futuro](#progetti-per-il-futuro)
32 | - [Ringraziamenti](#ringraziamenti)
33 | - [Progetti che utilizzano questa libreria](#progetti-che-utilizzano-questa-libreria)
34 | - [Commenti](#commenti)
35 |
36 | ## File inclusi
37 |
38 | ### [PKCS7Extractor.pas](Source/PKCS7Extractor.pas)
39 | ### Che cosa è e come funziona
40 | Questa è la unit principale con cui gli sviluppatori dovranno confrontarsi, esporta alcune utili funzioni per gestire i messaggi PKCS#7.
41 |
42 | ### Funzioni esportate
43 | Ogni funzione esportata ha una sua storia così lunga che a raccontarla nessuno ci crederebbe. Siamo chiari, non è stato per nulla facile, ma da ora dovrebbe esserlo. Iniziamo con il descrivere le funzioni esportate per il wrapper delle funzioni di [OpenSSL](https://www.openssl.org).
44 |
45 | ```delphi
46 | function Load: Boolean;
47 | ```
48 | Richiamare la funzione `Load` per inizializzare il wrapper, questo causerà se necessario il caricamento della libreria. Se è stata precedentemente specificata una cartella valida utilizzando la procedure `SetFolder`, verrà tentato di caricare la libreria dalla cartella specificata.\
49 | Il valore ritornato indica se il caricamento e/o collegamento della libreria è avvenuto con successo ed implica che tutte le funzioni necessarie siano state tornare.\
50 | Chiamare questa funzione più volte non ha alcun effetto pratico a meno che non venga scaricata prima la libreria.
51 |
52 | ```delphi
53 | function Loaded: Boolean;
54 | ```
55 | Ritorna se il wrapper è attualmente inizializzato correttamente.
56 |
57 | ```delphi
58 | procedure Unload;
59 | ```
60 | La procedure `Unload` causa la deinizializzazione del wrapper ma, come detto prima, la libreria rimarrà residente in memoria.
61 |
62 | ```delphi
63 | procedure SetFolder(const S: String);
64 | ```
65 | Richiamare `SetFolder` prima di `Load` per impostare una cartella specifica da cui caricare la libreria. Si notic che qualora la libreria fosse già stata caricata da questo wrapper o da qualsiasi altro componente o classe, le chiamate a questa procedura non avranno alcun effetto pratico.\
66 | Se viene specificata una cartella non valida o una cartella in cui non si trovi la libreria, verranno seguiti i normali percorsi utilizzati dal sistema operativo per il caricamento della libreria.\
67 | Non ha alcun effetto nel caso la libreria sia linkata staticamente nell'eseguibile.
68 |
69 | ```delphi
70 | function GetFolder: String;
71 | ```
72 | La funzione `GetFolder` ritorna il percorso assoluto da cui la libreria è attualmente caricata in memoria oppure una stringa vuota se la libreria non è presente in memoria.\
73 | Questo potrebbe differire dalla cartella specificata utilizzando `SetFolder` qualora la cartella specificata non sia valida o non contenga la libreria o ancora qualora la libreria fosse già precedentemente caricata.
74 |
75 | ```delphi
76 | function GetVersion: String;
77 | ```
78 | La funzione `GetVersion` ritorna semplicemente la stringa contenente la versione della libreria attualmente caricata in memoria o una stringa vuota qualora il wrapper non sia inizializzato.\
79 | La funzione corrispondente è stata introdotta in [OpenSSL](https://www.openssl.org) dalla versione 0.9.7, qyuindi questa funzione ritornerà una stringa vuota con versioni precedenti.
80 |
81 | ```delphi
82 | function GetErrorCode: Cardinal;
83 | ```
84 | Ritorna un codice numerico che rappresenta l'ultimo errore interno della libreria OpenSSL.
85 |
86 | ```delphi
87 | function GetError(const ErrorCode: Cardinal): String;
88 | ```
89 | Ritorna la descrizione testuale per un dato codice di errore.
90 |
91 | ```delphi
92 | function GetError: String;
93 | ```
94 | Ritorna la descrizione testuale per l'ultimo errore interno della libreria OpenSSL.\
95 | \
96 | Prima di continuare, è necessario prendere visione di due tipi indispensabili.
97 |
98 | ```delphi
99 | TVerifyStatus = (vsUnknown, vsFull, vsPartial);
100 | ```
101 |
102 | |Valore|Descrizione|
103 | |--|--|
104 | |`vsUnknown`|Nessun dato è stato caricato o i dati non erano validi.|
105 | |`vsPartial`|I dati e la struttura dell'envelope sono stati verificati.|
106 | |`vsFull`|E' stata eseguita una verifica completa dei dati.|
107 |
108 | ```delphi
109 | TSignatureMode = (smUnknown, smPKCS7, smCMS);
110 | ```
111 |
112 | |Valore|Descrizione|
113 | |--|--|
114 | |`smUnknown`|Nessun dato è stato caricato o i dati non erano validi.|
115 | |`smPKCS7`|I dati sono stati caricati ed erano imbustati con funzioni PKCS#7.|
116 | |`smCMS`|I dati sono stati caricati ed erano imbustati con funzioni CMS.|
117 |
118 | Quelle che seguono sono le funzioni di utilità.
119 |
120 | ```delphi
121 | function Extract(InStream, OutStream: TStream): Boolean;
122 | ```
123 | Dato un messaggio PKCS#7 presente in uno stream, ritorna il contenuto estratto ad un altro stream.\
124 | Ritorna se l'operazione è andata a buon fine o meno.\
125 | Si prega di notare che `InStream.Position` deve essere impostata al punto di inizio del messaggio PKCS#7 prima di richiamare questa funzione, perché la stessa abbia successo. La proprietà `Position` sarà variata al termine dell'esecuzione di questa funzione, anche se la stessa non ha prodotto un risultato positivo.\
126 | I dati risultati - se ve ne sono - sono aggiunti in coda al contenuto corrente dello stream di output, è quindi necessario fornire uno stream vuoto prima di richiamare questa funzione per ottenere solamente il contenuto del messaggio estratto.
127 |
128 | ```delphi
129 | function Extract(InFilename, OutFilename: String): Boolean;
130 | ```
131 | Funzione parallela che estrae i dati da un file PKCS#7 specificato con nome file ad un altro file. Ritorna se l'operazione è avvenuta correttamente o meno.
132 |
133 | ```delphi
134 | function Extract(Filename: String): Boolean;
135 | ```
136 | Estrae il contenuto di un file contenente un messaggio PKCS#7 usando come file di output un nome di file calcolato automaticamente. Ritorna se l'operazione è avvenuta correttamente o meno. Si tenga conto che se il nome di file di destinazione non può essere calcolato, l'operazione fallirà.\
137 | Per vedere come il file viene calcolato si legga della funzione `GuessFilename` qui sotto.
138 |
139 | ```delphi
140 | function GuessFilename(Filename: String): String;
141 | ```
142 | Prova a calcolare il nome di file di destinazione di un dato file originale, il che significa rimuovere l'estensione aggiunta (solitamente ".p7m") dal nome di file.\
143 | Per verificare che l'estensione sia stata aggiunta invece che sostituita, se nel file risultante non dovesse esserci un'altra estensione, la funzione fallità e ritornerà il nome del file originale.
144 |
145 | ```delphi
146 | function Verify(Stream: TStream): TVerifyStatus;
147 | ```
148 | Funzione necessaria a verificare se un dato stream contiene o meno un messaggio PKCS#7 valido. Come per `Extract(TStream, TStream)` ci si ricordi che la proprietà `Position` dello stream di input deve essere impostato all'inizio del messaggio PKCS#7 prima di essere passato a questa funzione.\
149 | A differenza di `Extract(TStream, TStream)` comunque, questa funzione manterrà il valore della proprietà `Position` al suo valore corrente.
150 |
151 | ```delphi
152 | function Verify(Filename: String): TVerifyStatus;
153 | ```
154 | Verifica semplicemente che un dato file contenga un messaggio PKCS#7 valido.
155 |
156 | ```delphi
157 | function Extract(Stream: TStream; var S: String): Boolean;
158 | ```
159 | Funzione per ottenere il contenuto di un messaggio PKCS#7 estratto da uno stream direttamente in una stringa. Se l'operazione fallisse il valore tornato sarebbe `False` e sarà tornata uns stringa vuota.
160 |
161 | ```delphi
162 | function ExtractToString(Filename: String; var S: String): Boolean;
163 | ```
164 | Funzione per ottenere il contenuto di un messaggio PKCS#7 estratto da un file direttamente in una stringa. Se l'operazione fallisse il valore tornato sarebbe `False` e sarà tornata uns stringa vuota.
165 |
166 | ```delphi
167 | function SignatureMode(Stream: TStream): TSignatureMode; overload;
168 | ```
169 |
170 | Ritorna il tipo di funzioni crittografiche utilizzate per imbustare dei dati. Preserva il valore della proprietà `Position` dello stream.
171 |
172 | ```delphi
173 | function SignatureMode(const Filename: String): TSignatureMode; overload;
174 | ```
175 |
176 | Ritorna il tipo di funzioni crittografiche utilizzate per imbustare un file.
177 |
178 | ### Classi esportate
179 |
180 | Quasta unit esporta una classe chiamata `TPKCS7Message` che è il cuore pulsante dove il lavoro viene svolto. Qualora si preferisse lavorare con questa, ecco come funziona. Si informa che a differenza delle suddette funzioni rapide, questa classe non tenta di caricare la libreria da sola, quindi bisogna sempre ricordare di richiamare `Load()` prima di utilizzarla.
181 |
182 | ```delphi
183 | constructor TPKCS7Message.Create;
184 | ```
185 |
186 | Crea un'istanza della classe.
187 |
188 | ```delphi
189 | procedure TPKCS7Message.Clear;
190 | ```
191 |
192 | Pulisce tutte le variabili locali cancellando eventuali dati caricati.
193 |
194 | ```delphi
195 | function TPKCS7Message.LoadFromStream(Stream: TStream): Boolean;
196 | ```
197 |
198 | Carica un messaggio PKCS#7 da uno stream, ritorna `True` al successo.
199 |
200 | ```delphi
201 | function TPKCS7Message.LoadFromFile(Filename: String): Boolean;
202 | ```
203 |
204 | Carica un messaggio PKCS#7 da un file, ritorna `True` al successo.
205 |
206 | ```delphi
207 | function TPKCS7Message.SaveToStream(Stream: TStream): Boolean;
208 | ```
209 |
210 | Salva il contenuto precedentemente caricato con `LoadFromStream` o `LoadFromFile` su uno stream, ritorna `True` al successo.
211 |
212 | ```delphi
213 | function TPKCS7Message.SaveToFile(Filename: String): Boolean;
214 | ```
215 |
216 | Salva il contenuto precedenemtente caricato con `LoadFromStream` o `LoadFromFile` su un file, ritorna `True` al successo.
217 |
218 | ```delphi
219 | property SignatureMode: TSignatureMode;
220 | ```
221 |
222 | Proprietà in sola lettura che rappresenta il tipo di funzioni crittografiche utilizzate per imbustare i dati.
223 |
224 | ```delphi
225 | property VerifyStatus: TVerifyStatus;
226 | ```
227 |
228 | Proprietà in sola lettura che rappresenta il tipo di verifica che è stata effettuata sui dati.
229 |
230 | #### Evento OnStreamCreate
231 |
232 | La classe estrae tutto il contenuto dai dati e li memorizza durante l'uso delle funzioni `LoadFromStream` o `LoadFromFile`.
233 | Si è scelto di memorizzare i dati in memoria RAM utilizzando un `TMemoryStream`, visto che i dati per cui era inizialmente destinata questa classe dovevano essere di dimensioni ridotte.
234 | Tuttavia è possibile che ci sia la necessità di reindirizzare i dati estratti altrove, per questo motivo è stato definito un evento, scatenato nel momento in cui la classe ha bisogno di allocare lo spazio dove memorizzare i dati.
235 | In questo modo è possibile redirigere i dati a qualsiasi discendente di `TStream` che sia scrivibile.
236 |
237 | ```delphi
238 | property OnStreamCreate: TStreamCreateEvent;
239 | ```
240 |
241 | Per esempio potremo definire nella nostra Form un evento con cui andare a creare un file temporaneo.
242 |
243 | ```delphi
244 | type
245 | TMyForm = class(TForm)
246 | ...
247 | procedure FormCreate(Sender: TObject);
248 | private
249 | FPKCS7Message: TPKCS7Message;
250 | protected
251 | procedure PKCS7Stream(Sender: TObject; var AStream: TStream);
252 | ...
253 |
254 | ...
255 | procedure TMyForm.PKCS7Stream(Sender: TObject; var AStream: TStream);
256 | begin
257 | AStream := TFileStream.Create('temp.tmp', fmCreate or fmOpenReadWrite or fmShareDenyWrite)
258 | end;
259 |
260 | ...
261 |
262 | procedure TMyForm.FormCreate(Sender: TObject);
263 | begin
264 | FPKCS7Message := TPKCS7Message.Create;
265 | FPKCS7Message.OnStreamCreate := PKCS7Stream;
266 | ...
267 | end;
268 | ...
269 | ```
270 |
271 | La classe si occuperà di richiamare il `Free` dello stream creato quando questo non sarà più necessario.
272 |
273 | ADDENDUM: nella cartella "Utils" potete trovare il file [CCLib.TempFileStream.pas](Utils/CCLib.TempFileStream.pas) che non fa parte di questa libreria ma è liberamente disponibile.
274 | Esporta una semplice classe discendente da `TStream` che crea un file temporaneo con nome casuale nella cartella temporanea di sistema, impedendone l'accesso ad altre applicazioni.
275 | Questo file sarà eliminato automaticamente quando verrà richiamato il metodo `Free` della classe.
276 |
277 | ## Applicazione demo
278 |
279 | L'applicazione demo contenuto nella cartella `\Demo` è un semplice estrattore che permette inoltre di cambiare la cartella da cui le librerie [OpenSSL](https://www.openssl.org) vengono caricate e selezionare un file, estrarlo verso un file di destinazione e visualizzare il suo contenuto in un controllo `TMemo`.
280 |
281 | 
282 |
283 | ## Unit tests
284 |
285 | La unit tests contenuta nella cartella `\Tests` è un progetto di un'applicazione console che esegue tutta una serie di test sulla libreria e sul wrapper.
286 |
287 | Il software richiede l'esistenza di alcune cartelle e file all'interno della stessa cartella in cui l'eseguibile viene lanciato, ecco una lista delle cartelle e che cosa devono contenere.
288 |
289 | | Nome cartella | Obbligatoria? | Descrizione |
290 | |--|--|--|
291 | | Data | SI | Questa cartella deve contenere una serie di file sui quali i test saranno eseguiti. |
292 | | DataCheck | SI | In questa cartella devono essere contenuti dei file con i risultati attesi dall'estrazione dei file nella cartella "Data". Ogni file deve avere esattamente lo stesso nome file ed estensione del file sorgente. **I test verranno eseguiti solamente sui file che hanno corrispondenza nelle due cartelle.**
293 | | OpenSSL\x32 | NO* | Questa cartella deve contenere delle sottocartelle ognuna delle quali deve contenere le librerie di [OpenSSL](https://www.openssl.org) in forma binaria. I test verranno eseguiti per ogni cartella trovata. **Inserire qui solamente librerie compilate a 32-bit** |
294 | | OpenSSL\x64 | NO* | Stessa cosa di cui sopra, ma per le librerie 64-bit. |
295 | | Temp | NO** | Questa cartella è utilizzata mentre i test vengono eseguiti per salvare alcuni file. **Non inserire alcun file in questa cartella**, questa cartella verrà eliminata ogni volta che vengono eseguiti i test. |
296 |
297 | ###
298 | ||Addendum|
299 | |---------------|--|
300 | |* | Questa cartella è richiesta solamente se l'applicazione demo è compilata per questa architettura. |
301 | |** | Questa cartella verrà creata automaticamente dal software ogni volta che viene eseguito. |
302 |
303 | ### Salvare un report dei test
304 | Per salvare un report dei test su un file di testo per una migliore analisi - o per smettere di perder tempo a scorrere la console - torna utile la redirezione dell'output.
305 |
306 | PKCS7ExtractorTest.exe >report.txt
307 | Genererà un file chiamato "report.txt" contenente tutte le informazioni necessarie.
308 | ### Ottenere una lista delle librerie OpenSSL
309 | Per ottenere una lista delle librerie [OpenSSL](https://www.openssl.org) presenti nelle cartelle `.\OpenSSL\x32` e/o `.\OpenSSL\x64` è possibile eseguire il programma di testo come:
310 |
311 | PKCS7ExtractorTest.exe /list
312 |
313 | Questo causerà l'output delle sole versioni delle librerie che il programma è in grado di caricare. Per salvare questa lista su un file di testo, è possibile sfruttare la ridirezione dell'output eseguendo il comando:
314 |
315 | PKCS7ExtractorTest.exe /list >output.txt
316 | Questo genererà un file chiamato "output.txt".
317 |
318 | ### Eseguire i test solo su alcune versioni di OpenSSL
319 | Avendo una lunga lista di versioni diverse di [OpenSSL](https://www.openssl.org) nella cartella `.\OpenSSL` perdavamo molto tempo eseguendo i test, per questa ragione abbiamo implementato la possibilità di specificare quali versioni eseguire.\
320 | E' sufficiente eseguire l'eseguibile specificando i nomi delle sottocartelle da cui caricare, ad esempio:
321 |
322 | PKCS7ExtractorTest.exe 0.9.6 openssl-0.9.8x-i386-win32 "openssl ultima"
323 | Eseguirà i test solamente con le librerie presenti nella cartelle chiamate "0.9.6", "openssl-0.9.8x-i386-win32" e "openssl ultima".
324 |
325 | ## File necessari
326 |
327 | Il progetto richiede le librerie [GNU Win32/64](http://gnuwin32.sourceforge.net/packages/openssl.htm) binarie per Windows.\
328 | La versione minima supportata dovrebbe essere la 0.9.6 rilasciata il 24 settembre 2000. Non siamo in grado di testare librerie precedenti a tale versione.\
329 | Con la versione 1.1.0 le librerie [OpenSSL](https://www.openssl.org) hanno avuto una riscrittura importante e stiamo attualmente lavorando per capire come rendere questa librerie compatibile con entrambe le versioni. Non garantiamo che questa libreria sarà in futuro compatibile con tali versioni.
330 |
331 | Questa unit è stata testata con le seguenti versioni binarie:
332 |
333 | | Versione | Data | x86 | x64 | Note |
334 | |---------|----------|-----|-----|-------|
335 | | 0.9.6 | 24 Sep 2000 | :white_check_mark: | *N/A* | |
336 | | 0.9.6b | 9 Jul 2001 | :white_check_mark: | *N/A* | |
337 | | 0.9.6i | ??? | ??? | ??? | siamo alla ricerca di questa versione |
338 | | 0.9.6j | ??? | ??? | ??? | siamo alla ricerca di questa versione |
339 | | ~~0.9.6k~~ | ~~30 Sep 2003~~ | ~~NO~~ | *N/A* | **STIAMO ATTUALMENTE INVESTIGANDO SU QUESTO PROBLEMA** |
340 | | 0.9.6l | ??? | ??? | ??? | siamo alla ricerca di questa versione |
341 | | ~~0.9.6m~~ | ~~17 Mar 2004~~ | ~~NO~~ | *N/A* | **STIAMO ATTUALMENTE INVESTIGANDO SU QUESTO PROBLEMA** |
342 | | 0.9.7 | ??? | ??? | ??? | siamo alla ricerca di questa versione |
343 | | 0.9.7a | ??? | ??? | ??? | siamo alla ricerca di questa versione |
344 | | 0.9.7b | ??? | ??? | ??? | siamo alla ricerca di questa versione |
345 | | 0.9.7c | ??? | ??? | ??? | siamo alla ricerca di questa versione |
346 | | 0.9.7d | ??? | ??? | ??? | siamo alla ricerca di questa versione |
347 | | 0.9.7e | ??? | ??? | ??? | siamo alla ricerca di questa versione |
348 | | 0.9.7f | ??? | ??? | ??? | siamo alla ricerca di questa versione |
349 | | 0.9.7g | ??? | ??? | ??? | siamo alla ricerca di questa versione |
350 | | 0.9.7h | ??? | ??? | ??? | siamo alla ricerca di questa versione |
351 | | 0.9.7i | ??? | ??? | ??? | siamo alla ricerca di questa versione |
352 | | ~~0.9.7j~~ | ~~04 May 2006~~ | ~~NO~~ | *N/A* | **STIAMO ATTUALMENTE INVESTIGANDO SU QUESTO PROBLEMA** |
353 | | 0.9.7k | ??? | ??? | ??? | siamo alla ricerca di questa versione |
354 | | 0.9.7l | ??? | ??? | ??? | siamo alla ricerca di questa versione |
355 | | ~~0.9.7m~~ | ~~23 Feb 2007~~ | ~~NO~~ | *N/A* | **STIAMO ATTUALMENTE INVESTIGANDO SU QUESTO PROBLEMA** |
356 | | 0.9.8 | ??? | ??? | ??? | siamo alla ricerca di questa versione |
357 | | 0.9.8a | ??? | ??? | ??? | siamo alla ricerca di questa versione |
358 | | 0.9.8b | ??? | ??? | ??? | siamo alla ricerca di questa versione |
359 | | 0.9.8c | ??? | ??? | ??? | siamo alla ricerca di questa versione |
360 | | 0.9.8d | ??? | ??? | ??? | siamo alla ricerca di questa versione |
361 | | 0.9.8e | 23 Feb 2007 | :white_check_mark: | *N/A* | |
362 | | 0.9.8f | ??? | ??? | ??? | siamo alla ricerca di questa versione |
363 | | 0.9.8g | ??? | ??? | ??? | siamo alla ricerca di questa versione |
364 | | 0.9.8h | 28 May 2008 | :white_check_mark: | :white_check_mark: | |
365 | | 0.9.8h | 28 May 2008 | :white_check_mark: | *N/A* | Indy / IntraWeb Edition |
366 | | 0.9.8i | 15 Sep 2008 | :white_check_mark: | :white_check_mark: | |
367 | | 0.9.8j | 07 Jan 2009 | :white_check_mark: | :white_check_mark: | |
368 | | 0.9.8k | 25 Mar 2009 | :white_check_mark: | :white_check_mark: | |
369 | | 0.9.8l | 5 Nov 2009 | :white_check_mark: | :white_check_mark: | |
370 | | 0.9.8l | 5 Nov 2009 | :white_check_mark: | *N/A* | Indy Backport |
371 | | 0.9.8m | 25 Feb 2010 | :white_check_mark: | :white_check_mark: | |
372 | | 0.9.8n | ??? | ??? | ??? | siamo alla ricerca di questa versione |
373 | | 0.9.8o | 01 Jun 2010 | :white_check_mark: | :white_check_mark: | |
374 | | 0.9.8p | ??? | ??? | ??? | siamo alla ricerca di questa versione |
375 | | 0.9.8q | 2 Dec 2010 | :white_check_mark: | :white_check_mark: | |
376 | | 0.9.8r | 8 Feb 2011 | :white_check_mark: | :white_check_mark: | |
377 | | 0.9.8r | 8 Feb 2011 | :white_check_mark: | :white_check_mark: | rev.2 |
378 | | 0.9.8s | 4 Jan 2012 | :white_check_mark: | :white_check_mark: | |
379 | | 0.9.8t | 18 Jan 2012 | :white_check_mark: | :white_check_mark: | |
380 | | 0.9.8u | 12 Mar 2012 | :white_check_mark: | :white_check_mark: | |
381 | | 0.9.8v | ??? | ??? | ??? | siamo alla ricerca di questa versione |
382 | | 0.9.8w | 23 Apr 2012 | :white_check_mark: | :white_check_mark: | |
383 | | 0.9.8x | 10 May 2012 | :white_check_mark: | :white_check_mark: | |
384 | | 0.9.8y | 5 Feb 2013 | :white_check_mark: | :white_check_mark: | |
385 | | 0.9.8za | ??? | ??? | ??? | siamo alla ricerca di questa versione |
386 | | 0.9.8zb | 6 Aug 2014 | :white_check_mark: | :white_check_mark: | |
387 | | 0.9.8zc | 15 Oct 2014 | :white_check_mark: | :white_check_mark: | |
388 | | 0.9.8zd | 8 Jan 2015 | :white_check_mark: | :white_check_mark: | |
389 | | 0.9.8ze | 15 Jan 2015 | :white_check_mark: | :white_check_mark: | |
390 | | 0.9.8zf | 19 Mar 2015 | :white_check_mark: | :white_check_mark: | |
391 | | 0.9.8zg | 11 Jun 2015 | :white_check_mark: | :white_check_mark: | |
392 | | 0.9.8zh | 3 Dec 2015 | :white_check_mark: | :white_check_mark: | |
393 | | 1.0.0 | 29 Mar 2010 | :white_check_mark: | :white_check_mark: | |
394 | | 1.0.0a | 1 Jun 2010 | :white_check_mark: | :white_check_mark: | |
395 | | 1.0.0b | ??? | ??? | ??? | searching fo this |
396 | | 1.0.0c | 2 Dec 2010 | :white_check_mark: | :white_check_mark: | |
397 | | 1.0.0d | 8 Feb 2011 | :white_check_mark: | :white_check_mark: | |
398 | | 1.0.0d | 8 Feb 2011 | :white_check_mark: | :white_check_mark: | rev.2 |
399 | | 1.0.0e | 6 Sep 2011 | :white_check_mark: | :white_check_mark: | |
400 | | 1.0.0f | 4 Jan 2012 | :white_check_mark: | :white_check_mark: | |
401 | | 1.0.0g | 18 Jan 2012 | :white_check_mark: | :white_check_mark: | |
402 | | 1.0.0h | 12 Mar 2012 | :white_check_mark: | :white_check_mark: | |
403 | | 1.0.0i | 19 Apr 2012 | :white_check_mark: | :white_check_mark: | |
404 | | 1.0.0j | 10 May 2012 | :white_check_mark: | :white_check_mark: | |
405 | | 1.0.0k | 5 Feb 2013 | :white_check_mark: | :white_check_mark: | |
406 | | 1.0.0l | 6 Jan 2014 | :white_check_mark: | :white_check_mark: | |
407 | | 1.0.0m | ??? | ??? | ??? | siamo alla ricerca di questa versione |
408 | | 1.0.0n | 6 Aug 2014 | :white_check_mark: | :white_check_mark: | |
409 | | 1.0.0o | 15 Oct 2014 | :white_check_mark: | :white_check_mark: | |
410 | | 1.0.0p | 8 Jan 2015 | :white_check_mark: | :white_check_mark: | |
411 | | 1.0.0q | 15 Jan 2015 | :white_check_mark: | :white_check_mark: | |
412 | | 1.0.0r | 19 Mar 2015 | :white_check_mark: | :white_check_mark: | |
413 | | 1.0.0s | 11 Jun 2015 | :white_check_mark: | :white_check_mark: | |
414 | | 1.0.0t | 3 Dec 2015 | :white_check_mark: | :white_check_mark: | |
415 | | 1.0.1 | 14 Mar 2012 | :white_check_mark: | :white_check_mark: | |
416 | | 1.0.1a | ??? | ??? | ??? | siamo alla ricerca di questa versione |
417 | | 1.0.1b | 26 Apr 2012 | :white_check_mark: | :white_check_mark: | |
418 | | 1.0.1c | 10 May 2012 | :white_check_mark: | :white_check_mark: | |
419 | | 1.0.1d | ??? | ??? | ??? | siamo alla ricerca di questa versione |
420 | | 1.0.1e | 11 Feb 2013 | :white_check_mark: | :white_check_mark: | |
421 | | 1.0.1f | 6 Jan 2014 | :white_check_mark: | :white_check_mark: | |
422 | | 1.0.1g | 7 Apr 2014 | :white_check_mark: | :white_check_mark: | |
423 | | 1.0.1h | 5 Jun 2014 | :white_check_mark: | :white_check_mark: | |
424 | | 1.0.1i | 6 Aug 2014 | :white_check_mark: | :white_check_mark: | |
425 | | 1.0.1j | 15 Oct 2014 | :white_check_mark: | :white_check_mark: | |
426 | | 1.0.1k | 8 Jan 2015 | :white_check_mark: | :white_check_mark: | |
427 | | 1.0.1l | 15 Jan 2015 | :white_check_mark: | :white_check_mark: | |
428 | | 1.0.1m | 19 Mar 2015 | :white_check_mark: | :white_check_mark: | |
429 | | 1.0.1o | 12 Jun 2015 | :white_check_mark: | :white_check_mark: | |
430 | | 1.0.1p | 9 Jul 2015 | :white_check_mark: | :white_check_mark: | |
431 | | 1.0.1q | 3 Dec 2015 | :white_check_mark: | :white_check_mark: | |
432 | | 1.0.1r | 28 Jan 2016 | :white_check_mark: | :white_check_mark: | |
433 | | 1.0.1s | 1 Mar 2016 | :white_check_mark: | :white_check_mark: | |
434 | | 1.0.1t | 3 May 2016 | :white_check_mark: | :white_check_mark: | |
435 | | 1.0.1u | 22 Sep 2016 | :white_check_mark: | :white_check_mark: | |
436 | | 1.0.2 | 22 Jan 2015 | :white_check_mark: | :white_check_mark: | |
437 | | 1.0.2a | 19 Mar 2015 | :white_check_mark: | :white_check_mark: | |
438 | | 1.0.2b | ??? | ??? | ??? | siamo alla ricerca di questa versione |
439 | | 1.0.2c | 12 Jun 2015 | :white_check_mark: | :white_check_mark: | |
440 | | 1.0.2d | 9 Jul 2015 | :white_check_mark: | :white_check_mark: | |
441 | | 1.0.2e | 3 Dec 2015 | :white_check_mark: | :white_check_mark: | |
442 | | 1.0.2f | 28 Jan 2016 | :white_check_mark: | :white_check_mark: | |
443 | | 1.0.2g | 1 Mar 2016 | :white_check_mark: | :white_check_mark: | |
444 | | 1.0.2h | 3 May 2016 | :white_check_mark: | :white_check_mark: | |
445 | | 1.0.2i | 22 Sep 2016 | :white_check_mark: | :white_check_mark: | |
446 | | 1.0.2j | 26 Sep 2016 | :white_check_mark: | :white_check_mark: | |
447 | | 1.0.2k | 26 Jan 2017 | :white_check_mark: | :white_check_mark: | |
448 | | 1.0.2l | 25 May 2017 | :white_check_mark: | :white_check_mark: | |
449 | | 1.0.2m | 2 Nov 2017 | :white_check_mark: | :white_check_mark: | |
450 | | 1.0.2n | 7 Dec 2017 | :white_check_mark: | :white_check_mark: | |
451 | | 1.0.2o | 27 Mar 2018 | :white_check_mark: | :white_check_mark: | |
452 | | 1.0.2p | 14 Aug 2018 | :white_check_mark: | :white_check_mark: | |
453 | | 1.0.2q | 20 Nov 2018 | :white_check_mark: | :white_check_mark: | |
454 | | 1.0.2r | 26 Feb 2019 | :white_check_mark: | :white_check_mark: | |
455 | | 1.0.2s | 28 May 2019 | :white_check_mark: | :white_check_mark: | |
456 | | 1.0.2t | 10 Sep 2019 | :white_check_mark: | :white_check_mark: | |
457 | | 1.0.2u | 20 Dec 2019 | :white_check_mark: | :white_check_mark: | |
458 |
459 | La maggior parte di queste versioni binarie è stata trovata su [indy.fulgan.com](https://indy.fulgan.com/SSL/Archive).\
460 | Siamo attualmente alla ricerca delle versioni binarie che ci mancano quindi fateci sapere qualora doveste trovarne qualcosa, saremo ben lieti di aggiungerle ai nostri test.\
461 | Ogni informazioni riguardante problemi di compatibilità sarà benvenuto.
462 | Si è deciso di ignorare le versioni 1.1.0+ da questa tabella, così come le versioni beta che non sono adatte alla distribuzione.
463 |
464 | ## Nota sui file codificati Base64
465 |
466 | **La libreria non gestisce i file codificati Base64, leggere il presente capitolo per scoprire come abilitarne la gestione.**
467 |
468 | Da quando questa libreria è stata rilasciata, siamo spesso stati contattati da persone che utilizzandola hanno trovato dei file che non venivano correttamente gestiti e spesso ci venivano indicati come corrotti o "strani".
469 | Ad una attenta analisi tutti questi file erano semplicemente codificati attraverso l'[algoritmo Base64](https://datatracker.ietf.org/doc/html/rfc4648) e vista la disponibilità di un gran numero di librerie, componenti o semplici unit che gestiscono tale formato, abbiamo sempre indirizzato altrove per porre rimedio.\
470 | La scelta di tale modus operandi è stata fatta anche sulla base dei risultati di un sondaggio sulle pagine del [Delphi Club Italia](http://www.delphiclubitalia.it) nel quale infatti è emerso che tale funzionalità avrebbe dovuto esulare da questa libreria in quanto non parte del processo. Molti hanno evidenziato come tanti sviluppatori avessero già risolto in proprio la problematica con una propria implementazione di un decoder Base64.\
471 | \
472 | Tuttavia recenti sviluppi ci hanno spinti nella direzione di integrare un controllo ed una gestione di tali file, anche in virtù del fatto che le librerie [OpenSSL](https://www.openssl.org) includono le funzionalità per la decodifica di tale formato, ma non applicano un riconoscimento automatico del formato in entrata delle funzioni.\
473 | \
474 | Le funzioni delle librerie si aspettano infatti che i dati siano racchiusi tra due separatori di blocco come qui mostrato per indicare come interpretare il dato.
475 |
476 | ```
477 | -----BEGIN PKCS7-----
478 | Q2kgZmEgcGlhY2VyZSBjaGUgc2lhdGUgY3VyaW9zaSBkaSBjb25vc2NlcmUgaWwgY29udGVudXRv
479 | IGRlbCBub3N0cm8gdGVzdG8gZGkgcHJvdmEsIG5vbiDDqCB1biBzZW1wbGljZSBsb3JlbSBpbXBz
480 | dW0sIG1hIHVuIHRlc3QgYWQgaG9jLiBWaSBhdWd1cmlhbW8gdW5hIGJ1b25hIGdpb3JuYXRhLg==
481 | -----END PKCS7-----
482 | ```
483 |
484 | Per questo motivo, si è introdotta una funzione in apertura dei file che verifica se è in formato Base64 ed in tal caso aggiunge i tag di apertura e chiusura di blocco.\
485 | \
486 | Nonostante le librerie [OpenSSL](https://www.openssl.org) non supportino la [variante URL safe](https://datatracker.ietf.org/doc/html/rfc4648#page-7) dell'alfabeto di Base64, tale procedura effettua il riconoscimento di entrambi gli alfabeti e eventualmente effettua conversione verso l'alfabeto standard.\
487 | Ciò introduce un possibile comportamento fallace in quanto un file nel quale vi sia la presenza mista di elementi distintivi dei due alfabeti non dovrebbe essere considerato un file codificato Base64 valido. Tuttavia si è valutato e si ritiene che l'evenienza che un file binario rientri in questa casistica sia talmente remota da non introdurre alcun tipo di problematica nell'utilizzo di questa libreria.\
488 | \
489 | Questa modifica non ha alcun effetto su tutti i file che già precedentemente si aprivano tenendo conto che:
490 | - qualsiasi file binario contenga valori al di fuori dei suddetti alfabeti di Base64 - ad eccezione dei caratteri CR e LF - non viene modificato;
491 | - qualsiasi file codificato Base64 contenga già tali tag ricade già nella suddetta regola.
492 |
493 | Chi volesse abilitare questa novità introdotta può semplicemente definire la direttiva di compilazione
494 |
495 | ```delphi
496 | {$DEFINE PKCS7MANAGEBASE64}
497 | ```
498 |
499 | Presente anche in testa al file `PKCS7Extractor.pas`.
500 |
501 | ## Cronologia versioni
502 |
503 | - Versione 1.3.0.0 rilasciata il 15 giugno 2021
504 | - aggiunta la gestione dei file codificati Base64 sfruttando un funzionamento interno delle librerie OpenSSL\
505 | Tale funzionalità non è attivata di default, ma può esserlo definendo la direttiva di compilazione PKCS7MANAGEBASE64
506 |
507 | - Versione 1.2.0.0 rilasciata il 2 marzo 2020
508 | - aggiunta la gestione dei file firmati utilizzando le funzioni di crittografia CMS (richiede librerie OpenSSL 0.9.8h o successive)
509 | - ora compatibile con Lazarus / Free Pascal
510 | - il sorgente non è compatibile con compilatori precedenti a Delphi 6
511 | - tutta l'elaborazione è stata sposta in LoadFromStream, utilizza meno variabili di classe
512 | - TPKCS7Message.Verify è stata sostituita dalla proprietà TPKCS7Message.VerifyStatus
513 | - nuova proprietà TPKCS7Message.SignatureMode
514 | - nuove funzioni veloci SignatureMode(Stream) and SignatureMode(String)
515 | - nuovo evento TPKCS7Message.OnStreamCreate che consente di modificare la classe TStream da utilizzare
516 | - Extract(Stream, S) e ExtractToString ora gestiscono i BOM e le codifiche (Delphi 2009+)
517 | - aggiunte le funzioni GetErrorCode, GetError(Cardinal) e GetError
518 | - non è più necessario specificare le opzioni di verifica
519 | - non lascia un file vuoto quando si crea un nuovo file con SaveToFile e fallisce
520 | - spostate tutte le noiose definizioni delle strutture nell'implementation
521 | - evitato hint del compilatore Delphi (ne persistono alcuni molto sciocchi su Free Pascal / Lazarus)
522 | - aggiunto il file [CCLib.TempFileStream.pas](Utils/CCLib.TempFileStream.pas)
523 | - testato con le librerie OpenSSL versioni 1.0.2q, 1.0.2r, 1.0.2s, 1.0.2t e 1.0.2u
524 | - risolti bug minori e riscritto parte di codice, rimosso lo zucchero sintattico
525 | - aggiunto file batch per la creazione automatica dei risultati attesi dei test
526 | - aggiornati README.md, LEGGIMI.md e lo screenshot della demo
527 | - ripristinato file dell'icona DCI nella cartella della demo
528 |
529 | - Versione 1.0.0.0 rilasciata il 27 novembre 2018
530 | - primo rilascio pubblico
531 | - correzione di errori minori
532 | - pulizia del codice ed aggiunta commenti
533 | - provato con alcune versioni in più di OpenSSL
534 |
535 | - Versione 0.9.5.0 - 26 novembre 2018
536 | - correzione errori
537 |
538 | - Versione 0.9.3.0 - 25 novembre 2018
539 | - la verifica supporta ora la verifica della firma
540 | - aggiunto il parametro per la verifica ad ogni funzione
541 |
542 | - Versione 0.9.0.0 - 25 novembre 2018
543 | - completamente modificata la struttura della libreria
544 | - ora è necessario un solo file
545 | - eliminate le due classi statiche `Libeay32Wrapper` e `PKCS7Message`
546 | - introdotta una classe `TPKCS7Message`, le funzioni rapide usano tale classe
547 |
548 | - Versione 0.8.2.0 - 24 novembre 2018
549 | - rimossa `PKCS7LibraryLocation` da `PKCS7Extractor.pas` utilizzare `Libeay32Wrapper.GetLibraryFolder`
550 | - rimossa `PKCS7LibraryVersion` da `PKCS7Extractor.pas` utilizzare `Libeay32Wrapper.SSLeay_version`
551 |
552 | - Versione 0.8.0.0 - 24 novembre 2018
553 | - aggiunta compatibilità x64
554 |
555 | - Versione 0.7.0.0 - 23 novembre 2018
556 | - rimossa la funzione `Loaded` da `Libeay32Wrapper`, utilizzare `Load`
557 | - riscritta completamente la funzione `Load` in `Libeay32Wrapper`
558 | - aggiunta funzione `GetLibraryFolder` per avere la posizione della libreria caricata
559 | - `SSLeay_version` non è più una semplice replica della funzione omonima esportata da [OpenSSL](https://www.openssl.org) ma una funzione più utile che ritorna una stringa contenente la versione
560 |
561 | - Versione 0.5.0.0 - 23 novembre 2018
562 | - abbandonata la dipendenza dal progetto [Indy](https://www.indyproject.org), utilizza ora un proprio wrapper minimale
563 | - rimossa la funzione `PKCS7Check` da `PKCS7Extractor.pas` utilizzare le funzioni `Libeay32Wrapper.Load` e `.Loaded`
564 | - aggiunto il file `Libeay32Wrapper.pas`
565 |
566 | - Versione 0.2.1.0 - 22 novembre 2018
567 | - aggiunta la funzione `PKCS7GuessFilename`
568 | - la funzione `PKCS7Extract(Filename)` ritorna ora il nome di destinazione o una stringa vuota
569 | - compatibile con Delphi 2007
570 |
571 | - Versione 0.2.0.0 - 22 novembre 2018
572 | - aggiunto `PKCS7ExtractorTest.dpr`
573 | - aggiunta la funzione `PKCS7Check`
574 | - aggiunta la funzione `PKCS7LibraryLocation`
575 | - aggiunta la funzione `PKCS7LibraryVersion`
576 | - aggiunta la funzione `IsPKCS7`
577 |
578 | - Versione 0.1.1.0 - 21 novembre 2018
579 | - risolta mancante inizializzazione della Libeay32.dll
580 |
581 | - Versione 0.1.0.0 - 20 novembre 2018
582 | - prima versione funzionante
583 |
584 | ## Compatibilità con i compilatori
585 |
586 | Vorremmo essere in grado di raggiungere la piena compatiblità con ogni compilatore Delphi (magari anche con [Free Pascal](https://www.freepascal.org)), ma abbiamo bisogno che altri ci aiutino a testare il codice con compilatori di cui non abbiamo la licenza. Per ogni compilatore non indicato come compatibile stiamo aspettando che qualcuno si metta in contatto con noi per inviarci i test compilati con quel compilatore.
587 |
588 | | | Compilatore | S.O. | Architettura | Versione | Tester/Note |
589 | |:---:|-------------|:----:|:------------:|---------:|-------------|
590 | | :x: | [Borland](https://www.embarcadero.com) Delphi 1 | :mount_fuji: | *N/A* | | No 32-bit support. |
591 | | :x: | [Borland](https://www.embarcadero.com) Delphi 2 | :mount_fuji: | x86 | 1.2.0.0 | Christian Cristofori |
592 | | :x: | [Borland](https://www.embarcadero.com) Delphi 3 | :mount_fuji: | x86 | 1.2.0.0 | Non *dovrebbe* funzionare. |
593 | | :x: | [Borland](https://www.embarcadero.com) Delphi 4 | :mount_fuji: | x86 | 1.2.0.0 | Non *dovrebbe* funzionare. |
594 | | :x: | [Borland](https://www.embarcadero.com) Delphi 5 | :mount_fuji: | x86 | 1.2.0.0 | Non *dovrebbe* funzionare. |
595 | | :white_check_mark: | [Borland](https://www.embarcadero.com) Delphi 6 | :mount_fuji: | x86 | | |
596 | | :white_check_mark: | [Borland](https://www.embarcadero.com) Delphi 7 | :mount_fuji: | x86 | 1.2.0.0 | Marcello Gribaudo |
597 | | :thought_balloon: | [Borland](https://www.embarcadero.com) Delphi 2005 | :mount_fuji: | x86 | | |
598 | | :thought_balloon: | [Borland](https://www.embarcadero.com) Delphi 2006 | :mount_fuji: | x86 | | |
599 | | :thought_balloon: | Turbo Delphi 2006 | :mount_fuji: | x86 | | |
600 | | :white_check_mark: | [CodeGear](https://www.embarcadero.com) Delphi 2007 | :mount_fuji: | x86 | 1.2.0.0 | Christian Cristofori |
601 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi 2009 | :mount_fuji: | x86 | | |
602 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi 2010 | :mount_fuji: | x86 | | |
603 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE | :mount_fuji: | x86 | | |
604 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE2 | :mount_fuji: | x86 | | |
605 | | :thought_balloon: | | :mount_fuji: | x64 | | |
606 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE3 | :mount_fuji: | x86 | | |
607 | | :thought_balloon: | | :mount_fuji: | x64 | | |
608 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE4 | :mount_fuji: | x86 | | |
609 | | :thought_balloon: | | :mount_fuji: | x64 | | |
610 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE5 | :mount_fuji: | x86 | | |
611 | | :thought_balloon: | | :mount_fuji: | x64 | | |
612 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE6 | :mount_fuji: | x86 | | |
613 | | :thought_balloon: | | :mount_fuji: | x64 | | |
614 | | :white_check_mark: | [Embarcadero](https://www.embarcadero.com) Delphi XE7 | :mount_fuji: | x86 | 1.2.0.0 | Diego Rigoni |
615 | | :white_check_mark: | | :mount_fuji: | x64 | 1.0.0.0 | Diego Rigoni |
616 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE8 | :mount_fuji: | x86 | | |
617 | | :thought_balloon: | | :mount_fuji: | x64 | | |
618 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi 10 Seattle | :mount_fuji: | x86 | | |
619 | | :thought_balloon: | | :mount_fuji: | x64 | | |
620 | | :white_check_mark: | [Embarcadero](https://www.embarcadero.com) Delphi 10.1 Berlin | :mount_fuji: | x86 | 1.2.0.0 | Gianni Giorgetti |
621 | | :white_check_mark: | | :mount_fuji: | x64 | 1.0.0.0 | Christian Cristofori |
622 | | :white_check_mark: | [Embarcadero](https://www.embarcadero.com) Delphi 10.2 Tokyo | :mount_fuji: | x86 | 1.0.0.0 | Christian Cristofori |
623 | | :white_check_mark: | | :mount_fuji: | x64 | 1.0.0.0 | Christian Cristofori |
624 | | :white_check_mark: | [Embarcadero](https://www.embarcadero.com) Delphi 10.3 Rio | :mount_fuji: | x86 | 1.2.0.0 | Christian Cristofori |
625 | | :white_check_mark: | | :mount_fuji: | x64 | 1.2.0.0 | Christian Cristofori |
626 | | :white_check_mark: | [Lazarus](https://www.lazarus-ide.org) 2.0.6 [Free Pascal](https://freepascal.org) 3.0.4 | :mount_fuji: | x86 | 1.2.0.0 | Christian Cristofori |
627 | | :thought_balloon: | | :mount_fuji: | x64 | | |
628 | | :x: | | :penguin: | x86 | \*1.1.0.0 | Christian Cristofori |
629 | | :thought_balloon: | | :penguin: | x64 | | |
630 |
631 | \*: la versione 1.1.0.0 non è mai stata più che una banale versione alpha.
632 |
633 | ## Progetti per il futuro
634 |
635 | La libreria è nata per un obiettivo che al momento pare essere stato ampiamente raggiunto.\
636 | Vista la longevità delle versioni crediamo che a questo punto la libreria si possa definire stabile e completa.\
637 | Non verranno pertanto aggiunte altre funzionalità, verranno pubblicate eventuali sotto-versioni per le seguenti motivazioni:
638 | - Correzioni e miglioramenti.
639 | - Rendere questa unit compatibile con ogni versione di Delphi o compilatore Object Pascal.
640 | - Compatibilità con ogni possibile versione di [OpenSSL](https://www.openssl.org).
641 |
642 | ## Ringraziamenti
643 |
644 | - [Delphi Club Italia](http://www.delphiclubitalia.it) - [Pagina Facebook](https://www.facebook.com/groups/delphiclubitalia)
645 | - [Christian Cristofori](https://github.com/zizzo81)
646 | - [Giancarlo Oneglio](http://www.onix.it)
647 | - [Diego Rigoni](mailto:diego@gdpitalia.com)
648 | - [Gianni Giorgetti](https://www.g3cube.net)
649 | - [Marcello Gribaudo](mailto:marcello.gribaudo@opigi.com)
650 |
651 | ## Progetti che utilizzano questa libreria.
652 |
653 | Questa libreria è utilizzabile liberamente dove e come si desidera, senza alcun obbligo. Ci farebbe però piacere avere un vostro feedback e aggiungere a questa lista un collegamento al vostro progetto.
654 |
655 | - [FExpolorer (Fattura Elettronica in Windows Explorer)](https://github.com/EtheaDev/FExplorer) di [Carlo Barazzetta](https://github.com/carloBarazzetta) e [Andrea Magni](https://github.com/andrea-magni)\
656 | Progetto Open-Source Delphi di un visualizzatore di Fatture Elettroniche con integrazione nella shell di Windows.
657 |
658 | ## Commenti
659 | Qualsiasi suggerimento, contributo o commento sarà veramente apprezzato.
660 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [versione italiana - italian version](LEGGIMI.md)
2 |
3 | 
4 | # PKCS#7 Extractor library for Delphi / Free Pascal / Lazarus
5 |
6 | This project is born from the need to simply extract .P7M files content in Delphi / Free Pascal / Lazarus applications.\
7 | Starting January 1st, 2019 italian developers have faced the new centralized digital invoicing system which has become mandatory by law.\
8 | Digital invoices are basically represented into XML files that are then digitally signed using CAdES-BES mode.\
9 | Developers managing invoices need to generate those XMLs and be able to read them back, but when a digitally signed invoice is enveloped into a .P7M file many of them ended up stripping it down by finding the position of the opening XML tag and the closing tag and then copying that part of the file.\
10 | We thought this couldn't be the professional way of managing this task, so we started investigating and we ended up creating this work.
11 |
12 | ## Summary
13 |
14 | - [PKCS#7 Extractor library for Delphi / Free Pascal / Lazarus](#pkcs7-extractor-library-for-delphi-free-pascal-lazarus)
15 | - [Summary](#summary)
16 | - [Included files](#included-files)
17 | - [PKCS7Extractor.pas](#pkcs7extractorpas)
18 | - [What it is and how does it work](#what-it-is-and-how-does-it-work)
19 | - [Exported functions](#exported-functions)
20 | - [Exported class](#exported-class)
21 | - [OnStreamCreate event](#onstreamcreate-event)
22 | - [Sample application](#sample-application)
23 | - [Unit tests](#unit-tests)
24 | - [Save a tests report](#save-a-tests-report)
25 | - [Get a list of the OpenSSL libraries](#get-a-list-of-the-openssl-libraries)
26 | - [Run test only on certain versions of OpenSSL](#run-test-only-on-certain-versions-of-openssl)
27 | - [Required files](#required-files)
28 | - [Notes about Base64-encoded files](#notes-about-base64-encoded-files)
29 | - [Version history](#version-history)
30 | - [Compiler compatibility](#compiler-compatibility)
31 | - [Projects for the future](#projects-for-the-future)
32 | - [Thanks](#thanks)
33 | - [Projects using this library](#projects-using-this-library)
34 | - [Comments](#comments)
35 |
36 | ## Included files
37 |
38 | ### [PKCS7Extractor.pas](Source/PKCS7Extractor.pas)
39 | ### What it is and how does it work
40 | This is the main unit developers will deal with. It exports some useful functions to deal with PKCS#7 messages.
41 | This is a wrapper for the [OpenSSL](https://www.openssl.org)'s Libeay32.dll file. Please keep in mind that this is a minimal wrapper written to include only the functions required for PKCS#7 extraction and it's not intended to become a full wrapper.\
42 | It also does not work like a regular wrapper: when loaded it it first searches for already present instances of the library and attaches to that, if library is not currently in memory it will try to load it like a normal wrapper would do.\
43 | But when unloaded this wrapper will only clear it's variables and will not free loaded library from memory. This was designed in this way because this shouldn't be used as a main source for [OpenSSL](https://www.openssl.org) interfacing and considering that other components/libraries could be present in the application unloading the library could drive to catastrophic results.\
44 | This behaviour will be safe for working along to others' works.
45 |
46 | ### Exported functions
47 | Every exported function has an history so long nobody would believe us. So let be clear, this was not so easy after all, but now it will be easy to go as follows. Let's begin decribe the functions for the wrapper of [OpenSSL](https://www.openssl.org) functions.
48 |
49 | ```delphi
50 | function Load: Boolean;
51 | ```
52 | Call the `Load` function to initialize the wrapper, this will eventually cause the load of the library. If a valid folder was specified using the `SetFolder` procedure, it will try to load the library from that folder.\
53 | The return value represents the successfull attach to the library or the load of the library and involves it's functions being found.
54 | Calling this more times will have no results as long as the library resides in memory.
55 |
56 | ```delphi
57 | function Loaded: Boolean;
58 | ```
59 | Returns if the wrapper is currently correctly initialized.
60 |
61 | ```delphi
62 | procedure Unload;
63 | ```
64 | The `Unload` procedure causes the wrapper to be uninitialized but, as said before, the library will be left loaded in memory.
65 |
66 | ```delphi
67 | procedure SetFolder(const S: String);
68 | ```
69 | Call `SetFolder` prior of the `Load` to set the folder where to load the library. Note that when the library has already been loaded from this wrapper or from another component/library calls to this function will have no effect at all.\
70 | If an invalid folder has been specified or the library is not found in the folder, normal system paths priority will be used to load the library.\
71 | Does not have effect when library is statically linked into executable.
72 |
73 | ```delphi
74 | function GetFolder: String;
75 | ```
76 | Function `GetFolder` will return the absolute path of the library being actually loaded into memory or an empty string if the library was not loaded.\
77 | This can differ from the folder specified by `SetFolder` when the specified folder is invalid or the library is not found in the folder.
78 |
79 | ```delphi
80 | function GetVersion: String;
81 | ```
82 | Utility function `GetVersion` does simply return the string containing the library version or an empty string if the wrapper has not been initialized.\
83 | The corresponding function has been introduced since [OpenSSL](https://www.openssl.org) version 0.9.7, hence this function will return empty with older versions.
84 |
85 | ```delphi
86 | function GetErrorCode: Cardinal;
87 | ```
88 | Returns a numeric value representing last OpenSSL library internal error.
89 |
90 | ```delphi
91 | function GetError(const ErrorCode: Cardinal): String;
92 | ```
93 | Returns the description of a given error code.
94 |
95 | ```delphi
96 | function GetError: String;
97 | ```
98 | Returns the description of last OpenSSL library internal error./
99 | /
100 | Before continuing, let's take a look at two fundamental types.
101 |
102 | ```delphi
103 | TVerifyStatus = (vsUnknown, vsFull, vsPartial);
104 | ```
105 |
106 | |Value|Description|
107 | |--|--|
108 | |`vsUnknown`|No data was loaded or data was invalid.|
109 | |`vsPartial`|Data and envelope has been verified.|
110 | |`vsFull`|A full verification has been made.|
111 |
112 | ```delphi
113 | TSignatureMode = (smUnknown, smPKCS7, smCMS);
114 | ```
115 |
116 | |Value|Description|
117 | |--|--|
118 | |`smUnknown`|No data was loaded or data was invalid.|
119 | |`smPKCS7`|Data has been loaded using PKCS#7 functions.|
120 | |`smCMS`|Data has been loaded using CMS functions.|
121 |
122 | What follows are the utility functions.
123 |
124 | ```delphi
125 | function Extract(InStream, OutStream: TStream): Boolean;
126 | ```
127 | Given a PKCS#7 message in a stream outputs the content extracted to the output stream.\
128 | Returns if the operation was successful.\
129 | Please keep in mind that `InStream.Position` has to be set to the beginning of the PKCS#7 data prior to calling this function for this to work. The `Position` property will be at the end of the stream once this returns, also when the function doesn't produce a positive result.\
130 | The resulting data - if any - is appended to the current content of the output stream, that needs to be cleared before being passed to this function to only receive the content extracted.
131 |
132 | ```delphi
133 | function Extract(InFilename, OutFilename: String): Boolean;
134 | ```
135 | Overloaded function that will extract from a source PKCS#7 message file to a output file and return if the operation was successful or not.
136 |
137 | ```delphi
138 | function Extract(Filename: String): Boolean;
139 | ```
140 | Extracts the content of the PKCS#7 message file using as output file the guessed destination filename. It will return if the operation was successful or not. Keep in mind that if the destination filename can't be guessed, the operation will fail.\
141 | To see how the filename is guessed look at the `GuessFilename` function below.
142 |
143 | ```delphi
144 | function GuessFilename(Filename: String): String;
145 | ```
146 | Tries to guess the destination filename from a given original filename, which means: remove the appended ".p7m" extensions from the file.\
147 | To check if this extension was appended instead of replaceing the original one, if the resulting filename does not contain another extension, the function will fail and return the original file name.
148 |
149 | ```delphi
150 | function Verify(Stream: TStream): TVerifyStatus;
151 | ```
152 | Utility function to check if a given stream does contain a valid PKCS#7 message. As for `Extract(TStream, TStream)` please keep in mind that input stream `Position` property must be set to the beginning of the PKCS#7 message prior to calling this.\
153 | Unlike `Extract(TStream, TStream)` however, this function will preserve stream position to it's current state.
154 |
155 | ```delphi
156 | function Verify(Filename: String): TVerifyStatus;
157 | ```
158 | Simply checks if given file contains a valid PKCS#7 message.
159 |
160 | ```delphi
161 | function Extract(Stream: TStream; var S: String): Boolean;
162 | ```
163 | Utility function to have the content of a PKCS#7 message extracted from a stream and put directly into a string. If the operation fails the return value will be `false` and an empty string will be set.
164 |
165 | ```delphi
166 | function ExtractToString(Filename: String; var S: String): Boolean;
167 | ```
168 | Utility function to have the content of a PKCS#7 message extracted from a file and put directly into a string. If the operation fails the return value will be `false` and an empty string will be set.
169 |
170 | ```delphi
171 | function SignatureMode(Stream: TStream): TSignatureMode; overload;
172 | ```
173 |
174 | Returns the crypto functions type used for enveloping data. Preserves value of the `Position` property of the stream.
175 |
176 | ```delphi
177 | function SignatureMode(const Filename: String): TSignatureMode; overload;
178 | ```
179 |
180 | Returns the crypto functions type used for enveloping a file.
181 |
182 | ### Exported class
183 |
184 | This unit exports a handle class `TPKCS7Message` that is the main core where the job is done. If you prefeer to deal with this, here's how it works. Please be advised that unlike utilities functions above, this class does not try to initialize the wrapper by itself, so always remember to call `Load()` before using this.
185 |
186 | ```delphi
187 | constructor TPKCS7Message.Create;
188 | ```
189 |
190 | Creates an instance of the class.
191 |
192 | ```delphi
193 | procedure TPKCS7Message.Clear;
194 | ```
195 |
196 | Clears all whe local variables discarding eventually loaded data.
197 |
198 | ```delphi
199 | function TPKCS7Message.LoadFromStream(Stream: TStream): Boolean;
200 | ```
201 |
202 | Loads a PKCS#7 message from a stream, returns `True` on success.
203 |
204 | ```delphi
205 | function TPKCS7Message.LoadFromFile(Filename: String): Boolean;
206 | ```
207 |
208 | Loads a PKCS#7 message file, returns `True` on success.
209 |
210 | ```delphi
211 | function TPKCS7Message.SaveToStream(Stream: TStream): Boolean;
212 | ```
213 |
214 | Saves content previously loaded with `LoadFromStream` or `LoadFromFile` methods to a stream, returns `True` on valid data.
215 |
216 | ```delphi
217 | function TPKCS7Message.SaveToFile(Filename: String): Boolean;
218 | ```
219 |
220 | Saves content previously loaded with `LoadFromStream` or `LoadFromFile` methods to a file, returns `True` on valid data.
221 |
222 | ```delphi
223 | property SignatureMode: TSignatureMode;
224 | ```
225 |
226 | Read-only property representing the crypto functions type used for enveloping data.
227 |
228 | ```delphi
229 | property VerifyStatus: TVerifyStatus;
230 | ```
231 |
232 | Read-only property representing the verification mode used for data.
233 |
234 | #### OnStreamCreate event
235 |
236 | The class extracts the whole content from the data and stores it to temporary memory inside the `LoadFromStream` or `LoadFromFile` functions.
237 | We chose to store that data in the RAm memory by using a `TMemoryStream` because the initial target of this class were small files.
238 | It's possible to redirect that data to other destinations, for this reason an event has been introduces, which gets called when the class needs to allocate space.
239 | In this way a redirection to every writable `TStream` descendant is possible.
240 |
241 | ```delphi
242 | property OnStreamCreate: TStreamCreateEvent;
243 | ```
244 |
245 | As an example, we could define an event on out Form to save it to a temporary file.
246 |
247 | ```delphi
248 | type
249 | TMyForm = class(TForm)
250 | ...
251 | procedure FormCreate(Sender: TObject);
252 | private
253 | FPKCS7Message: TPKCS7Message;
254 | protected
255 | procedure PKCS7Stream(Sender: TObject; var AStream: TStream);
256 | ...
257 |
258 | ...
259 | procedure TMyForm.PKCS7Stream(Sender: TObject; var AStream: TStream);
260 | begin
261 | AStream := TFileStream.Create('temp.tmp', fmCreate or fmOpenReadWrite or fmShareDenyWrite)
262 | end;
263 |
264 | ...
265 |
266 | procedure TMyForm.FormCreate(Sender: TObject);
267 | begin
268 | FPKCS7Message := TPKCS7Message.Create;
269 | FPKCS7Message.OnStreamCreate := PKCS7Stream;
270 | ...
271 | end;
272 | ...
273 | ```
274 |
275 | The class will call the `Free` method of the stream when this is not longer necessary.
276 |
277 | ADDENDUM: in the "Utils" folder you can find the file [CCLib.TempFileStream.pas](Utils/CCLib.TempFileStream.pas) which is not part ot this library but freely available.
278 | It simply exports a `TStream` descendant class that creates a temporary file in system's temporary folder with a randomic filename, denying access to the file to other applications.
279 | This file will be automatically deleted once the `Free` method is called.
280 |
281 | ## Sample application
282 |
283 | The sample application contained into `Demo` folder is a simple extractor which allows to change the folder where the [OpenSSL](https://www.openssl.org) libraries are loaded from and pick up a file, extract it to destination file and view it's contents into a `TMemo`.
284 |
285 | 
286 |
287 | ## Unit tests
288 |
289 | The unit tests contained in `Tests` folder is a console application project that will run a whole serie of test on the library and the wrapper.
290 |
291 | The software requires some folders and files to be in the same folder from where the compiled executable is run from, here's the list of the folders and what they need to contain.
292 |
293 | | Folder name | Required? | Description |
294 | |--|--|--|
295 | | Data | YES | This need to contain a bunch of files on whose the tests will be executed.
296 | | DataCheck | YES | In this folder there must be expected results from the extraction of the files in the "Data" folder. Each file must have the exact filename and extension of the source file. **Tests will be executed only on files that have a correspondence in the two folders above.**
297 | | OpenSSL\x32 | NO* | This folder should contain subfolders each of whose contains the [OpenSSL](https://www.openssl.org) libraries in binary form. Tests will be executed for each folder found. **Only put 32-bit libraries inside this folder**
298 | | OpenSSL\x64 | NO* | Same as the above, but for the 64-bit libraries.
299 | | Temp | NO** | This folder is used while executing tests for saving some files. **Do not put files inside this folder**, this folder will be deleted each time the tests are run.
300 |
301 | ###
302 | ||Addendum|
303 | |---------------|--|
304 | |* | This folder is required only when application is compiled for the corrisponding architecture. |
305 | |** | This folder will be created automatically by the software each time it is run.
306 |
307 | ### Save a tests report
308 | To save a tests report to a text file for further investigation - or just to stop wasting time scrolling the console - the use of output redirection comes handy.
309 |
310 | PKCS7ExtractorTest.exe >report.txt
311 | Will generate a text file called "report.txt" containing all the information needed.
312 | ### Get a list of the OpenSSL libraries
313 | To obtain a list of the [OpenSSL](https://www.openssl.org) libraries in the `.\OpenSSL\x32` and `.\OpenSSL\x64` folders the tests program can be run as:
314 |
315 | PKCS7ExtractorTest.exe /list
316 |
317 | This will cause the program to only output a list of the libraries versions it is able to load. To save this list to a text file simply use output redirection by running the command:
318 |
319 | PKCS7ExtractorTest.exe /list >output.txt
320 | This will generate a file named "output.txt".
321 |
322 | ### Run test only on certain versions of OpenSSL
323 | Having a lot of different version of [OpenSSL](https://www.openssl.org) libraries in the `.\OpenSSL` folder in sub-folders we ended up losing a lot of time running tests for certain versions. For this reason we implemented a way to specify which versions to run.\
324 | Just run the command specifying the folders names where to load from, for example:
325 |
326 | PKCS7ExtractorTest.exe 0.9.6 openssl-0.9.8x-i386-win32 "openssl latest"
327 | Will execute tests only on the folders named "0.9.6", "openssl-0.9.8x-i386-win32" and "openssl latest".
328 |
329 | ## Required files
330 |
331 | This project only requires [GNU Win32/64](http://gnuwin32.sourceforge.net/packages/openssl.htm) binary libraries for Windows.
332 | Minimum supported version should be 0.9.6 released on September, 24th 2000. We're unable to test binaries before this version.\
333 | Starting from version 1.1.0 [OpenSSL](https://www.openssl.org) libraries had a mayor rewrite and we're currently investigatin on how to maker this libary compatible with both versions. We don't guarantee this will be possible in the future.
334 |
335 | This unit has been tested with these binary versions:
336 |
337 | | Version | Released | x86 | x64 | Notes |
338 | |---------|----------|-----|-----|-------|
339 | | 0.9.6 | 24 Sep 2000 | :white_check_mark: | *N/A* | |
340 | | 0.9.6b | 9 Jul 2001 | :white_check_mark: | *N/A* | |
341 | | 0.9.6i | ??? | ??? | ??? | searching for this |
342 | | 0.9.6j | ??? | ??? | ??? | searching for this |
343 | | ~~0.9.6k~~ | ~~30 Sep 2003~~ | ~~NO~~ | *N/A* | **CURRENTLY INVESTIGATING THIS ISSUE** |
344 | | 0.9.6l | ??? | ??? | ??? | searching for this |
345 | | ~~0.9.6m~~ | ~~17 Mar 2004~~ | ~~NO~~ | *N/A* | **CURRENTLY INVESTIGATING THIS ISSUE** |
346 | | 0.9.7 | ??? | ??? | ??? | searching for this |
347 | | 0.9.7a | ??? | ??? | ??? | searching for this |
348 | | 0.9.7b | ??? | ??? | ??? | searching for this |
349 | | 0.9.7c | ??? | ??? | ??? | searching for this |
350 | | 0.9.7d | ??? | ??? | ??? | searching for this |
351 | | 0.9.7e | ??? | ??? | ??? | searching for this |
352 | | 0.9.7f | ??? | ??? | ??? | searching for this |
353 | | 0.9.7g | ??? | ??? | ??? | searching for this |
354 | | 0.9.7h | ??? | ??? | ??? | searching for this |
355 | | 0.9.7i | ??? | ??? | ??? | searching for this |
356 | | ~~0.9.7j~~ | ~~04 May 2006~~ | ~~NO~~ | *N/A* | **CURRENTLY INVESTIGATING THIS ISSUE** |
357 | | 0.9.7k | ??? | ??? | ??? | searching for this |
358 | | 0.9.7l | ??? | ??? | ??? | searching for this |
359 | | ~~0.9.7m~~ | ~~23 Feb 2007~~ | ~~NO~~ | *N/A* | **CURRENTLY INVESTIGATING THIS ISSUE** |
360 | | 0.9.8 | ??? | ??? | ??? | searching for this |
361 | | 0.9.8a | ??? | ??? | ??? | searching for this |
362 | | 0.9.8b | ??? | ??? | ??? | searching for this |
363 | | 0.9.8c | ??? | ??? | ??? | searching for this |
364 | | 0.9.8d | ??? | ??? | ??? | searching for this |
365 | | 0.9.8e | 23 Feb 2007 | :white_check_mark: | *N/A* | |
366 | | 0.9.8f | ??? | ??? | ??? | searching for this |
367 | | 0.9.8g | ??? | ??? | ??? | searching for this |
368 | | 0.9.8h | 28 May 2008 | :white_check_mark: | :white_check_mark: | |
369 | | 0.9.8h | 28 May 2008 | :white_check_mark: | *N/A* | Indy / IntraWeb Edition |
370 | | 0.9.8i | 15 Sep 2008 | :white_check_mark: | :white_check_mark: | |
371 | | 0.9.8j | 07 Jan 2009 | :white_check_mark: | :white_check_mark: | |
372 | | 0.9.8k | 25 Mar 2009 | :white_check_mark: | :white_check_mark: | |
373 | | 0.9.8l | 5 Nov 2009 | :white_check_mark: | :white_check_mark: | |
374 | | 0.9.8l | 5 Nov 2009 | :white_check_mark: | *N/A* | Indy Backport |
375 | | 0.9.8m | 25 Feb 2010 | :white_check_mark: | :white_check_mark: | |
376 | | 0.9.8n | ??? | ??? | ??? | searching for this |
377 | | 0.9.8o | 01 Jun 2010 | :white_check_mark: | :white_check_mark: | |
378 | | 0.9.8p | ??? | ??? | ??? | searching for this |
379 | | 0.9.8q | 2 Dec 2010 | :white_check_mark: | :white_check_mark: | |
380 | | 0.9.8r | 8 Feb 2011 | :white_check_mark: | :white_check_mark: | |
381 | | 0.9.8r | 8 Feb 2011 | :white_check_mark: | :white_check_mark: | rev.2 |
382 | | 0.9.8s | 4 Jan 2012 | :white_check_mark: | :white_check_mark: | |
383 | | 0.9.8t | 18 Jan 2012 | :white_check_mark: | :white_check_mark: | |
384 | | 0.9.8u | 12 Mar 2012 | :white_check_mark: | :white_check_mark: | |
385 | | 0.9.8v | ??? | ??? | ??? | searching for this |
386 | | 0.9.8w | 23 Apr 2012 | :white_check_mark: | :white_check_mark: | |
387 | | 0.9.8x | 10 May 2012 | :white_check_mark: | :white_check_mark: | |
388 | | 0.9.8y | 5 Feb 2013 | :white_check_mark: | :white_check_mark: | |
389 | | 0.9.8za | ??? | ??? | ??? | searching for this |
390 | | 0.9.8zb | 6 Aug 2014 | :white_check_mark: | :white_check_mark: | |
391 | | 0.9.8zc | 15 Oct 2014 | :white_check_mark: | :white_check_mark: | |
392 | | 0.9.8zd | 8 Jan 2015 | :white_check_mark: | :white_check_mark: | |
393 | | 0.9.8ze | 15 Jan 2015 | :white_check_mark: | :white_check_mark: | |
394 | | 0.9.8zf | 19 Mar 2015 | :white_check_mark: | :white_check_mark: | |
395 | | 0.9.8zg | 11 Jun 2015 | :white_check_mark: | :white_check_mark: | |
396 | | 0.9.8zh | 3 Dec 2015 | :white_check_mark: | :white_check_mark: | |
397 | | 1.0.0 | 29 Mar 2010 | :white_check_mark: | :white_check_mark: | |
398 | | 1.0.0a | 1 Jun 2010 | :white_check_mark: | :white_check_mark: | |
399 | | 1.0.0b | ??? | ??? | ??? | searching fo this |
400 | | 1.0.0c | 2 Dec 2010 | :white_check_mark: | :white_check_mark: | |
401 | | 1.0.0d | 8 Feb 2011 | :white_check_mark: | :white_check_mark: | |
402 | | 1.0.0d | 8 Feb 2011 | :white_check_mark: | :white_check_mark: | rev.2 |
403 | | 1.0.0e | 6 Sep 2011 | :white_check_mark: | :white_check_mark: | |
404 | | 1.0.0f | 4 Jan 2012 | :white_check_mark: | :white_check_mark: | |
405 | | 1.0.0g | 18 Jan 2012 | :white_check_mark: | :white_check_mark: | |
406 | | 1.0.0h | 12 Mar 2012 | :white_check_mark: | :white_check_mark: | |
407 | | 1.0.0i | 19 Apr 2012 | :white_check_mark: | :white_check_mark: | |
408 | | 1.0.0j | 10 May 2012 | :white_check_mark: | :white_check_mark: | |
409 | | 1.0.0k | 5 Feb 2013 | :white_check_mark: | :white_check_mark: | |
410 | | 1.0.0l | 6 Jan 2014 | :white_check_mark: | :white_check_mark: | |
411 | | 1.0.0m | ??? | ??? | ??? | searching for this |
412 | | 1.0.0n | 6 Aug 2014 | :white_check_mark: | :white_check_mark: | |
413 | | 1.0.0o | 15 Oct 2014 | :white_check_mark: | :white_check_mark: | |
414 | | 1.0.0p | 8 Jan 2015 | :white_check_mark: | :white_check_mark: | |
415 | | 1.0.0q | 15 Jan 2015 | :white_check_mark: | :white_check_mark: | |
416 | | 1.0.0r | 19 Mar 2015 | :white_check_mark: | :white_check_mark: | |
417 | | 1.0.0s | 11 Jun 2015 | :white_check_mark: | :white_check_mark: | |
418 | | 1.0.0t | 3 Dec 2015 | :white_check_mark: | :white_check_mark: | |
419 | | 1.0.1 | 14 Mar 2012 | :white_check_mark: | :white_check_mark: | |
420 | | 1.0.1a | ??? | ??? | ??? | searching for this |
421 | | 1.0.1b | 26 Apr 2012 | :white_check_mark: | :white_check_mark: | |
422 | | 1.0.1c | 10 May 2012 | :white_check_mark: | :white_check_mark: | |
423 | | 1.0.1d | ??? | ??? | ??? | searching for this |
424 | | 1.0.1e | 11 Feb 2013 | :white_check_mark: | :white_check_mark: | |
425 | | 1.0.1f | 6 Jan 2014 | :white_check_mark: | :white_check_mark: | |
426 | | 1.0.1g | 7 Apr 2014 | :white_check_mark: | :white_check_mark: | |
427 | | 1.0.1h | 5 Jun 2014 | :white_check_mark: | :white_check_mark: | |
428 | | 1.0.1i | 6 Aug 2014 | :white_check_mark: | :white_check_mark: | |
429 | | 1.0.1j | 15 Oct 2014 | :white_check_mark: | :white_check_mark: | |
430 | | 1.0.1k | 8 Jan 2015 | :white_check_mark: | :white_check_mark: | |
431 | | 1.0.1l | 15 Jan 2015 | :white_check_mark: | :white_check_mark: | |
432 | | 1.0.1m | 19 Mar 2015 | :white_check_mark: | :white_check_mark: | |
433 | | 1.0.1o | 12 Jun 2015 | :white_check_mark: | :white_check_mark: | |
434 | | 1.0.1p | 9 Jul 2015 | :white_check_mark: | :white_check_mark: | |
435 | | 1.0.1q | 3 Dec 2015 | :white_check_mark: | :white_check_mark: | |
436 | | 1.0.1r | 28 Jan 2016 | :white_check_mark: | :white_check_mark: | |
437 | | 1.0.1s | 1 Mar 2016 | :white_check_mark: | :white_check_mark: | |
438 | | 1.0.1t | 3 May 2016 | :white_check_mark: | :white_check_mark: | |
439 | | 1.0.1u | 22 Sep 2016 | :white_check_mark: | :white_check_mark: | |
440 | | 1.0.2 | 22 Jan 2015 | :white_check_mark: | :white_check_mark: | |
441 | | 1.0.2a | 19 Mar 2015 | :white_check_mark: | :white_check_mark: | |
442 | | 1.0.2b | 11 Jun 2015 | ??? | ??? | searching for this |
443 | | 1.0.2c | 12 Jun 2015 | :white_check_mark: | :white_check_mark: | |
444 | | 1.0.2d | 9 Jul 2015 | :white_check_mark: | :white_check_mark: | |
445 | | 1.0.2e | 3 Dec 2015 | :white_check_mark: | :white_check_mark: | |
446 | | 1.0.2f | 28 Jan 2016 | :white_check_mark: | :white_check_mark: | |
447 | | 1.0.2g | 1 Mar 2016 | :white_check_mark: | :white_check_mark: | |
448 | | 1.0.2h | 3 May 2016 | :white_check_mark: | :white_check_mark: | |
449 | | 1.0.2i | 22 Sep 2016 | :white_check_mark: | :white_check_mark: | |
450 | | 1.0.2j | 26 Sep 2016 | :white_check_mark: | :white_check_mark: | |
451 | | 1.0.2k | 26 Jan 2017 | :white_check_mark: | :white_check_mark: | |
452 | | 1.0.2l | 25 May 2017 | :white_check_mark: | :white_check_mark: | |
453 | | 1.0.2m | 2 Nov 2017 | :white_check_mark: | :white_check_mark: | |
454 | | 1.0.2n | 7 Dec 2017 | :white_check_mark: | :white_check_mark: | |
455 | | 1.0.2o | 27 Mar 2018 | :white_check_mark: | :white_check_mark: | |
456 | | 1.0.2p | 14 Aug 2018 | :white_check_mark: | :white_check_mark: | |
457 | | 1.0.2q | 20 Nov 2018 | :white_check_mark: | :white_check_mark: | |
458 | | 1.0.2r | 26 Feb 2019 | :white_check_mark: | :white_check_mark: | |
459 | | 1.0.2s | 28 May 2019 | :white_check_mark: | :white_check_mark: | |
460 | | 1.0.2t | 10 Sep 2019 | :white_check_mark: | :white_check_mark: | |
461 | | 1.0.2u | 20 Dec 2019 | :white_check_mark: | :white_check_mark: | |
462 |
463 | Most of these binaries can be found at [indy.fulgan.com](https://indy.fulgan.com/SSL/Archive).\
464 | We're currently looking for binaries not listed here so please let us know if there're some around, we'll be happy to add them to our tests.\
465 | Every compatibility issues reported will be appreciated.
466 | We decided to remove versions 1.1.0+ in this table, as well as beta versions which are not suitable for distribution.
467 |
468 | ## Notes about Base64-encoded files
469 |
470 | **This library does not manage Base64-encoded files, read this chapter to discover how to enable management.**
471 |
472 | Since this library has been made available, we got in touch with people using it found files that they stated where not correctly managed, telling us that files appeared corrupted or "strange".
473 | After analysing all that files we discovered those where simply encoded using the [Base64 algorithm](https://datatracker.ietf.org/doc/html/rfc4648). Since there's a great availability of libraries, components and simple units that manage that encoding, we just addressed people that way.\
474 | This way of managing the problem has been decided based on the results of a public poll on [Delphi Club Italia](http://www.delphiclubitalia.it) from which emerged that this functionality should not be part of this library because it doesn't belong to the process. Many pointed that most developers had already solved this problem with one of the implementations of the Base64 decoder available.\
475 | \
476 | However, recent developments have pushed us in the direction of integratic a control and management of these files, mostly because the [OpenSSL](https://www.openssl.org) libraries already include all the functionalities for decoding this format, but they do not apply automatic recognition of the incoming format on functions.\
477 | \
478 | In fact, the library functions expect the data to be enclosed between two block separators as shown here, to indicate how to interpret the data.
479 |
480 | ```
481 | -----BEGIN PKCS7-----
482 | V2UgYXJlIHBsZWFzZWQgYnkgeW91IGN1cmlvc2l0eSB0byB1bmNvdmVyIHRoZSBjb250ZW50IG9m
483 | IG91ciBzYW1wbGUgdGV4dCwgaXQgaXMgbm90IGEgc2ltcGxlIGxvcmVtIGltcHN1bSwgYnV0IGEg
484 | dGFpbG9yZWQgdGV4dCB3ZSB3cm90ZSBmb3IgeW91LiBXZSB3aXNoIHlvdSBhIGdvb2QgZGF5Lg==
485 | -----END PKCS7-----
486 | ```
487 |
488 | For this reason, a function has been introduced when opening the files, that checks if they are in Base64 format and in this case adds the opening and closing tags.\
489 | \
490 | Although the [OpenSSL](https://www.openssl.org) libraries do not support the [URL safe variant alfabet](https://datatracker.ietf.org/doc/html/rfc4648#page-7) of the Base64, this procedure recognizes both alphabets and when needed converts to the standard alphabet.\
491 | This introduces a possible erroneous behavior in which a file where there is the mixed presence of distinctive elements of the two alphabets should not be considered a valid Base64 encoded file. However, it has been considered and weighted that the occurrence of a binary file falling into this case is so remote that it does not introduce any kind of risk in the use of this library.\
492 | \
493 | This feature does not have any effect on all the files that where managed before, taking into account that:
494 | - every binary file containing values outside the aforementioned alfabets of the Base64 algorithm - excluding CR and LF codes - gets not altered;
495 | - every Base64-encoded file already containing the tags falls under the aforementioned rule.
496 |
497 | Anyone wishing to enable this new feature can simply define the compilation directive.
498 |
499 | ```delphi
500 | {$DEFINE PKCS7MANAGEBASE64}
501 | ```
502 |
503 | Also present at the top of the `PKCS7Extractor.pas` file.
504 |
505 | ## Version history
506 |
507 | - Version 1.3.0.0 released June, 15th 2021
508 | - added management of Base64-encoded files using OpenSSL libraries' internal method\
509 | this functionality is not provided by default but can be enabled by defining the PKCS7MANAGEBASE64 compiler directive.
510 |
511 | - Version 1.2.0.0 released March, 2nd 2020
512 | - added support for files signed using CMS cryptography (requires OpenSSL libraries 0.9.8h+)
513 | - now compatible with Lazarus / Free Pascal
514 | - lost compatibility with Delphi compiler prior to Delphi 6
515 | - all the magic has been moved to the LoadFromStream function removing a logical bug, less class variables are used now
516 | - TPKCS7Message.Verify has been replaced by property TPKCS7Message.VerifyStatus
517 | - new property TPKCS7Message.SignatureMode
518 | - new SignatureMode(Stream) and SignatureMode(String) utility functions
519 | - new event TPKCS7Message.OnStreamCreate allows to override default TStream descendant
520 | - Extract(Stream, S) and ExtractToString now manage BOMs and encodings (Delphi 2009+)
521 | - added functions GetErrorCode, GetError(Cardinal) e GetError
522 | - no need to specify verifications options anymore
523 | - doesn't leave an empty file while creating a new file with failing SaveToFile function
524 | - moved all the annoying structures definition in implementation
525 | - avoided a compiler hint on Delphi (still there are some in Lazarus / Free Pascal, but very odd)
526 | - added file [CCLib.TempFileStream.pas](Utils/CCLib.TempFileStream.pas)
527 | - tested on OpenSSL libraries versions 1.0.2q, 1.0.2r, 1.0.2s, 1.0.2t and 1.0.2u
528 | - minor bugfixing and code refactoring, removed syntactic sugar
529 | - added batch file for automatic creation of test expected results
530 | - updated README.md, LEGGIMI.md and demo screenshot
531 | - restored DCI icon's file in the demo folder
532 |
533 | - Version 1.0.0.0 released November, 27th 2018
534 | - first public release
535 | - minor bugfixes
536 | - code cleanup and commented
537 | - tested with more versions of OpenSSL binaries
538 |
539 | - Version 0.9.5.0 - November, 26th 2018
540 | - bugfixes
541 |
542 | - Version 0.9.3.0 - November, 25th 2018
543 | - verification now supports full signature method
544 | - added verification parameter to every function
545 |
546 | - Version 0.9.0.0 - November, 25th 2018
547 | - changed structure of the library
548 | - only one file required
549 | - the two static classes (`Libeay32Wrapper` and `PKCS7Message`) are gone
550 | - a new class `TPKCS7Message` has been introduced, utility functions use that class
551 |
552 | - Version 0.8.2.0 - November, 24rd 2018
553 | - removed `PKCS7LibraryLocation` from `PKCS7Extractor.pas` use `Libeay32Wrapper.GetLibraryFolder`
554 | - removed `PKCS7LibraryVersion` from `PKCS7Extractor.pas` use `Libeay32Wrapper.SSLeay_version`
555 |
556 | - Version 0.8.0.0 - November, 24rd 2018
557 | - added x64 compatibility
558 |
559 | - Version 0.7.0.0 - November, 23rd 2018
560 | - removed the `Loaded` function from `Libeay32Wrapper`, just use `Load`
561 | - completly rewritten the `Load` function in `Libeay32Wrapper`
562 | - added function `GetLibraryFolder` to get location of the library loaded
563 | - `SSLeay_version` is no more a replication of the [OpenSSL](https://www.openssl.org) specification but a more convenient function returning a string containing version
564 |
565 | - Version 0.5.0.0 - November, 23rd 2018
566 | - abandoned dependency on [Indy](https://www.indyproject.org) project, now uses a minimal wrapper
567 | - removed `PKCS7Check` from `PKCS7Extractor.pas` use `Libeay32Wrapper.Load` and `.Loaded` functions
568 | - added `Libeay32Wrapper.pas`
569 | - removed `PKCS7Check` function
570 |
571 | - Version 0.2.1.0 - November, 22nd 2018
572 | - added `PKCS7GuessFilename` function
573 | - `PKCS7Extract(Filename)` function now returns destination filename or an empty string on failure
574 | - compatible with Delphi 2007
575 |
576 | - Version 0.2.0.0 - November, 22nd 2018
577 | - added `PKCS7ExtractorTest.dpr`
578 | - added `PKCS7Check` function
579 | - added `PKCS7LibraryLocation` function
580 | - added `PKCS7LibraryVersion` function
581 | - added `IsPKCS7` functions
582 |
583 | - Version 0.1.1.0 - November, 21st 2018
584 | - fixed missing Libeay32.dll library initialization
585 |
586 | - Version 0.1.0.0 - November, 20st 2018
587 | - first working version
588 |
589 | ## Compiler compatibility
590 |
591 | We would like to be able to achieve the full compatibility to every Delphi / Free Pascal / Lazarus, but we need others to help us testing with compilers we don't own. For each unchecked compiler we're waiting for someone to get in touch with us sending the compiled binaries.
592 |
593 | | | Compiler | OS | Architecture | Version | Tester/Notes |
594 | |:---:|----------|:---:|:------------:|--------:|--------------|
595 | | :x: | [Borland](https://www.embarcadero.com) Delphi 1 | :mount_fuji: | *N/A* | | No 32-bit support. |
596 | | :x: | [Borland](https://www.embarcadero.com) Delphi 2 | :mount_fuji: | x86 | 1.2.0.0 | Christian Cristofori |
597 | | :x: | [Borland](https://www.embarcadero.com) Delphi 3 | :mount_fuji: | x86 | 1.2.0.0 | Non *dovrebbe* funzionare. |
598 | | :x: | [Borland](https://www.embarcadero.com) Delphi 4 | :mount_fuji: | x86 | 1.2.0.0 | Non *dovrebbe* funzionare. |
599 | | :x: | [Borland](https://www.embarcadero.com) Delphi 5 | :mount_fuji: | x86 | 1.2.0.0 | Non *dovrebbe* funzionare. |
600 | | :white_check_mark: | [Borland](https://www.embarcadero.com) Delphi 6 | :mount_fuji: | x86 | | |
601 | | :white_check_mark: | [Borland](https://www.embarcadero.com) Delphi 7 | :mount_fuji: | x86 | 1.2.0.0 | Marcello Gribaudo |
602 | | :thought_balloon: | [Borland](https://www.embarcadero.com) Delphi 2005 | :mount_fuji: | x86 | | |
603 | | :thought_balloon: | [Borland](https://www.embarcadero.com) Delphi 2006 | :mount_fuji: | x86 | | |
604 | | :thought_balloon: | Turbo Delphi 2006 | :mount_fuji: | x86 | | |
605 | | :white_check_mark: | [CodeGear](https://www.embarcadero.com) Delphi 2007 | :mount_fuji: | x86 | 1.2.0.0 | Christian Cristofori |
606 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi 2009 | :mount_fuji: | x86 | | |
607 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi 2010 | :mount_fuji: | x86 | | |
608 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE | :mount_fuji: | x86 | | |
609 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE2 | :mount_fuji: | x86 | | |
610 | | :thought_balloon: | | :mount_fuji: | x64 | | |
611 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE3 | :mount_fuji: | x86 | | |
612 | | :thought_balloon: | | :mount_fuji: | x64 | | |
613 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE4 | :mount_fuji: | x86 | | |
614 | | :thought_balloon: | | :mount_fuji: | x64 | | |
615 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE5 | :mount_fuji: | x86 | | |
616 | | :thought_balloon: | | :mount_fuji: | x64 | | |
617 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE6 | :mount_fuji: | x86 | | |
618 | | :thought_balloon: | | :mount_fuji: | x64 | | |
619 | | :white_check_mark: | [Embarcadero](https://www.embarcadero.com) Delphi XE7 | :mount_fuji: | x86 | 1.2.0.0 | Diego Rigoni |
620 | | :white_check_mark: | | :mount_fuji: | x64 | 1.0.0.0 | Diego Rigoni |
621 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi XE8 | :mount_fuji: | x86 | | |
622 | | :thought_balloon: | | :mount_fuji: | x64 | | |
623 | | :thought_balloon: | [Embarcadero](https://www.embarcadero.com) Delphi 10 Seattle | :mount_fuji: | x86 | | |
624 | | :thought_balloon: | | :mount_fuji: | x64 | | |
625 | | :white_check_mark: | [Embarcadero](https://www.embarcadero.com) Delphi 10.1 Berlin | :mount_fuji: | x86 | 1.2.0.0 | Gianni Giorgetti |
626 | | :white_check_mark: | | :mount_fuji: | x64 | 1.0.0.0 | Christian Cristofori |
627 | | :white_check_mark: | [Embarcadero](https://www.embarcadero.com) Delphi 10.2 Tokyo | :mount_fuji: | x86 | 1.0.0.0 | Christian Cristofori |
628 | | :white_check_mark: | | :mount_fuji: | x64 | 1.0.0.0 | Christian Cristofori |
629 | | :white_check_mark: | [Embarcadero](https://www.embarcadero.com) Delphi 10.3 Rio | :mount_fuji: | x86 | 1.2.0.0 | Christian Cristofori |
630 | | :white_check_mark: | | :mount_fuji: | x64 | 1.2.0.0 | Christian Cristofori |
631 | | :white_check_mark: | [Lazarus](https://www.lazarus-ide.org) 2.0.6 [Free Pascal](https://freepascal.org) 3.0.4 | :mount_fuji: | x86 | 1.2.0.0 | Christian Cristofori |
632 | | :thought_balloon: | | :mount_fuji: | x64 | | |
633 | | :x: | | :penguin: | x86 | \*1.1.0.0 | Christian Cristofori |
634 | | :thought_balloon: | | :penguin: | x64 | | |
635 |
636 | \*: version 1.1.0.0 never made out of alpha version.
637 |
638 | ## Projects for the future
639 |
640 | This library was born for a goal which at the moment appears to be fully achieved.\
641 | Given the longevity of the version we believe that a this time the library can be considered stable and complete.\
642 | Therefore no other features will be added, some sub-version could be published for the following reasons:
643 | - Fixes and improvements.
644 | - Making this unit compatible with all versions of Delphi or other Object Pascal compilers.
645 | - Compatibility with all possible versions of [OpenSSL](https://www.openssl.org).
646 |
647 | ## Thanks
648 |
649 | - [Delphi Club Italia](http://www.delphiclubitalia.it) [Facebook page](https://www.facebook.com/groups/delphiclubitalia)
650 | - [Christian Cristofori](https://github.com/zizzo81)
651 | - [Giancarlo Oneglio](http://www.onix.it)
652 | - [Diego Rigoni](mailto:diego@gdpitalia.com)
653 | - [Gianni Giorgetti](https://www.g3cube.net)
654 | - [Marcello Gribaudo](mailto:marcello.gribaudo@opigi.com)
655 |
656 | ## Projects using this library
657 |
658 | Feel free to use this library where and how you wish, without any obligations, but we really would like to hear from you and put a link to your project in this list.
659 |
660 | - [FExpolorer (Fattura Elettronica in Windows Explorer)](https://github.com/EtheaDev/FExplorer) by [Carlo Barazzetta](https://github.com/carloBarazzetta) and [Andrea Magni](https://github.com/andrea-magni)\
661 | Open-Source Delphi project of a fully functional Legal Invoices viewer integrated with Windows' shell.
662 |
663 | ## Comments
664 | Any suggestion, contribution and comment will be appreciated.
665 |
--------------------------------------------------------------------------------
/Source/PKCS7Extractor.pas:
--------------------------------------------------------------------------------
1 | unit PKCS7Extractor;
2 |
3 | {$IFDEF FPC}
4 | {$MODE Delphi}
5 | {$ENDIF}
6 |
7 | {//$DEFINE PKCS7MANAGEBASE64}
8 |
9 | {***************************************************************************}
10 | { }
11 | { PKCS#7 Extractor library for Delphi }
12 | { Version 1.3.0.0 released June, 15th 2021 }
13 | { }
14 | { Copyright (C) 2018 Delphi Club Italia }
15 | { http://www.delphiclubitalia.it }
16 | { }
17 | { Original authors: }
18 | { Christian Cristofori github@christiancristofori.it }
19 | { Giancarlo Oneglio giancarlo.oneglio@gmail.com }
20 | { }
21 | {***************************************************************************}
22 | { }
23 | { Licensed under the GNU Lesser General Public License, Version 3; }
24 | { you may not use this file except in compliance with the License. }
25 | { }
26 | { This is free software: you can redistribute it and/or modify it under }
27 | { the terms of the GNU Lesser General Public License as published by the }
28 | { Free Software Foundation, either version 3 of the License, or (at your }
29 | { option) any later version. }
30 | { }
31 | { This is distributed in the hope that it will be useful, but WITHOUT }
32 | { ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or }
33 | { FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public }
34 | { License for more details. }
35 | { }
36 | { You should have received a copy of the GNU Lesser General Public }
37 | { License along with this software. }
38 | { If not, see . }
39 | { }
40 | {***************************************************************************}
41 |
42 | interface
43 |
44 | uses
45 | Classes;
46 |
47 | const
48 | LIBEAY32_LIBRARY = 'libeay32.dll';
49 |
50 | function GetFolder: String;
51 | procedure SetFolder(const Value: String);
52 | function Load: Boolean;
53 | function Loaded: Boolean;
54 | procedure Unload;
55 | function GetVersion: String;
56 | function GetErrorCode: Cardinal;
57 | function GetError: String; overload;
58 | function GetError(const ErrorCode: Cardinal): String; overload;
59 |
60 | type
61 | TVerifyStatus = (vsUnknown, vsFull, vsPartial);
62 |
63 | TSignatureMode = (smUnknown, smPKCS7, smCMS);
64 |
65 | TStreamCreateEvent = procedure(Sender: TObject; var AStream: TStream) of Object;
66 |
67 | TPKCS7Message = class
68 | private
69 | fContent: TStream;
70 | fOnStreamCreate: TStreamCreateEvent;
71 | fSignatureMode: TSignatureMode;
72 | fVerifyStatus: TVerifyStatus;
73 | public
74 | constructor Create;
75 | destructor Destroy; override;
76 |
77 | procedure Clear;
78 |
79 | function LoadFromStream(Stream: TStream): Boolean;
80 | function LoadFromFile(const Filename: String): Boolean;
81 |
82 | function SaveToStream(Stream: TStream): Boolean;
83 | function SaveToFile(const Filename: String): Boolean;
84 |
85 | property OnStreamCreate: TStreamCreateEvent read fOnStreamCreate write fOnStreamCreate;
86 | property SignatureMode: TSignatureMode read fSignatureMode;
87 | property VerifyStatus: TVerifyStatus read fVerifyStatus;
88 | end;
89 |
90 | // Tries to guess destination filename of a .*.p7m file
91 | function GuessFilename(const Filename: String): String;
92 |
93 | // Extracts data from PKCS#7 message stream to a stream.
94 | function Extract(InStream, OutStream: TStream): Boolean; overload;
95 |
96 | // Utility function: does the above operation from a file to another by
97 | // specifing the filenames.
98 | function Extract(const InFilename, OutFilename: String): Boolean; overload;
99 |
100 | // Utility function: doest the above operation from a file to another by specifing
101 | // the filename, this function tries to guess the destination filename by removing
102 | // the final extension, if another extension is present it will do the job.
103 | function Extract(const Filename: String): String; overload;
104 |
105 | // Utility function to extract the content from a stream directly to a String.
106 | function Extract(Stream: TStream; var S: String): Boolean; overload;
107 |
108 | // Utility function to extract the content from a file directly to a String.
109 | function ExtractToString(const Filename: String; var S: String): Boolean; overload;
110 |
111 | // Gets the verification status for a stream.
112 | function Verify(Stream: TStream): TVerifyStatus; overload;
113 |
114 | // Gets the verification status for a file.
115 | function Verify(const Filename: String): TVerifyStatus; overload;
116 |
117 | // Gets signature mode for a stream.
118 | function SignatureMode(Stream: TStream): TSignatureMode; overload;
119 |
120 | // Gets signature mode for a file.
121 | function SignatureMode(const Filename: String): TSignatureMode; overload;
122 |
123 | implementation
124 |
125 | uses
126 | Windows, SysUtils;
127 |
128 | // ================================================== LIBEAY32.DLL MICRO WRAPPER
129 |
130 | const
131 | READ_BUFFER_SIZE = $1000;
132 |
133 | BIO_CTRL_RESET = $01;
134 | BIO_CTRL_INFO = $03;
135 | BIO_CTRL_FLUSH = $0B;
136 |
137 | PKCS7_NOVERIFY = $20;
138 |
139 | PKCS7_OP_GET_DETACHED_SIGNATURE = 2;
140 |
141 | CMS_NOVERIFY = $20;
142 |
143 | NID_PKCS7_SIGNED = 22;
144 |
145 | LIBEAY_OK = 1;
146 |
147 | type
148 | PBIO_METHOD = Pointer;
149 |
150 | PSTACK = Pointer;
151 |
152 | CRYPTO_EX_DATA = record
153 | sk: PSTACK;
154 | dummy: Integer
155 | end;
156 |
157 | PPBIO = ^PBIO;
158 | PBIO = ^BIO;
159 | BIO = record
160 | method: PBIO_METHOD;
161 | callback: Pointer;
162 | cb_arg: PAnsiChar;
163 | init,
164 | shutdown,
165 | flags,
166 | retry_reasonm,
167 | num: Integer;
168 | ptr: Pointer;
169 | next_bio,
170 | prev_bio: PBIO;
171 | references: Integer;
172 | num_read,
173 | num_write: LongWord;
174 | ex_data: CRYPTO_EX_DATA
175 | end;
176 |
177 | PASN1_OBJECT = ^ASN1_OBJECT;
178 | ASN1_OBJECT = record
179 | sn,
180 | ln: PAnsiChar;
181 | nid,
182 | length: Integer;
183 | data: PAnsiChar;
184 | flags: Integer
185 | end;
186 |
187 | PKCS7_union = record
188 | ptr: PAnsiChar
189 | end;
190 |
191 | PCMS_ContentInfo = ^CMS_ContentInfo;
192 | CMS_ContentInfo = record
193 | contentType: PAnsiChar;
194 | d: Pointer
195 | end;
196 |
197 | PPKCS7 = ^PKCS7;
198 | PKCS7 = record
199 | asn1: PAnsiChar;
200 | length: LongInt;
201 | state,
202 | detached: Integer;
203 | _type: PASN1_OBJECT;
204 | d: PKCS7_union
205 | end;
206 |
207 | PX509_STORE = Pointer;
208 |
209 | PPPKCS7 = ^PPKCS7;
210 |
211 | PPEM_PASSWORD_CB = Pointer;
212 |
213 | PSTACK_OF_X509 = Pointer;
214 |
215 | const
216 | // Total number of functions in the TLibeay32Functions structure counting optionals too.
217 | LIBEAY32_FUNCTIONS_COUNT = 23;
218 | // Number of optionals functions.
219 | LIBEAY32_OPTIONAL_COUNT = 4;
220 |
221 | type
222 | TLibeay32Functions = packed record
223 | case Integer of
224 | 0: (
225 | {00} sk_num : function(const x: PSTACK): Integer cdecl; // OpenSSL 0.9.6
226 |
227 | {01} BIO_s_mem : function: PBIO_METHOD cdecl; // OpenSSL pre-0.9.6
228 | {02} BIO_new : function(t: PBIO_METHOD): PBIO cdecl; // OpenSSL pre-0.9.6
229 | {03} BIO_new_mem_buf : function(buf: Pointer; len: Integer): PBIO cdecl; // introduced OpenSSL 0.9.6
230 | {04} BIO_ctrl : function(b: PBIO; c: Integer; l: LongInt; a: Pointer): LongInt cdecl; // OpenSSL pre-0.9.6
231 | {05} BIO_read : function(b: PBIO; d: Pointer; l: Integer): Integer cdecl; // OpenSSL pre-0.9.6
232 | {06} BIO_write : function(b: pBIO; const p: Pointer; l: Integer): Integer cdecl; // OpenSSL pre-0.9.6
233 | {07} BIO_free : function(b: PBIO): Integer cdecl; // OpenSSL pre-0.9.6
234 |
235 | {08} OBJ_obj2nid : function(const o: PASN1_OBJECT): Integer cdecl; // OpenSSL pre-0.9.6
236 |
237 | {09} d2i_PKCS7_bio : function(b: PBIO; p: PPKCS7): PPKCS7 cdecl; // OpenSSL pre-0.9.6
238 |
239 | {10} PEM_read_bio_PKCS7 : function(b: PBIO; x: PPPKCS7; c: PPEM_PASSWORD_CB; u: Pointer): PPKCS7 cdecl; // OpenSSL pre-0.9.6
240 | {11} SMIME_read_PKCS7 : function(b: PBIO; {var }c: PBIO): PPKCS7 cdecl; // OpenSSL pre-0.9.6
241 |
242 | {12} PKCS7_ctrl : function(p: PPKCS7; c, l: Integer; a: PAnsiChar): Integer cdecl; // OpenSSL pre-0.9.6
243 | {13} PKCS7_get_signer_info : function(p: PPKCS7): PSTACK cdecl; // OpenSSL pre-0.9.6
244 | {14} PKCS7_dataInit : function(p: PPKCS7; b: PBIO): PBIO cdecl; // OpenSSL pre-0.9.6
245 | {15} PKCS7_free : procedure(p: PPKCS7) cdecl; // OpenSSL pre-0.9.6
246 |
247 | {16} PKCS7_verify : function(p: PPKCS7; c: PSTACK_OF_X509; s: PX509_STORE; i, o: PBIO; f: Integer): Integer cdecl; // OpenSSL pre-0.9.6
248 | // TODO: see PKCS7_dataVerify
249 |
250 | {17} X509_STORE_new : function: PX509_STORE cdecl; // OpenSSL pre-0.9.6
251 | {18} X509_STORE_free : procedure(s: pX509_STORE) cdecl; // OpenSSL pre-0.9.6
252 |
253 | // FROM HERE FUNCTIONS ARE OPTIONALS, DON'T PLACE REQUIRED FUNCTIONS HERE!
254 |
255 | {19} d2i_CMS_bio : function(b: PBIO; p: PCMS_ContentInfo): PCMS_ContentInfo cdecl; // introduced OpenSSL 0.9.8h
256 | {20} CMS_dataInit : function(p: PCMS_ContentInfo; b: PBIO): PBIO cdecl; // introduced OpenSSL 0.9.8h
257 | {21} CMS_verify : function(p: PCMS_ContentInfo; c: PSTACK_OF_X509; s: PX509_STORE; i, o: PBIO; f: Integer): Integer cdecl; // introduced OpenSSL 0.9.8h
258 | {22} CMS_ContentInfo_free : procedure(P: PCMS_ContentInfo) cdecl; // introduced OpenSSL 0.9.8h
259 |
260 | );
261 | 1: (Functions : Array[0..LIBEAY32_FUNCTIONS_COUNT - 1] of Pointer)
262 | end;
263 |
264 | var
265 | mFolder: String = '';
266 | mFunctions: TLibeay32Functions;
267 | mErrorLoaded: Boolean = False;
268 |
269 | function Load: Boolean;
270 | var
271 | hLibeay32: HMODULE;
272 | OpenSSL_add_all_digests: procedure cdecl;
273 | ERR_load_crypto_strings: procedure cdecl;
274 | begin
275 | // Loads library
276 | hLibeay32 := GetModuleHandle(LIBEAY32_LIBRARY);
277 | if hLibeay32 = 0 then
278 | hLibeay32 := LoadLibrary(PChar(mFolder + LIBEAY32_LIBRARY));
279 | if hLibeay32 = 0 then
280 | hLibeay32 := LoadLibrary(LIBEAY32_LIBRARY);
281 |
282 | // Gets functions pointers
283 | @mFunctions.sk_num := GetProcAddress(hLibeay32, 'sk_num');
284 |
285 | @mFunctions.BIO_s_mem := GetProcAddress(hLibeay32, 'BIO_s_mem');
286 | @mFunctions.BIO_new := GetProcAddress(hLibeay32, 'BIO_new');
287 | @mFunctions.BIO_new_mem_buf := GetProcAddress(hLibeay32, 'BIO_new_mem_buf');
288 | @mFunctions.BIO_ctrl := GetProcAddress(hLibeay32, 'BIO_ctrl');
289 | @mFunctions.BIO_read := GetProcAddress(hLibeay32, 'BIO_read');
290 | @mFunctions.BIO_write := GetProcAddress(hLibeay32, 'BIO_write');
291 |
292 | @mFunctions.BIO_free := GetProcAddress(hLibeay32, 'BIO_free');
293 |
294 | @mFunctions.OBJ_obj2nid := GetProcAddress(hLibeay32, 'OBJ_obj2nid');
295 |
296 | @mFunctions.X509_STORE_new := GetProcAddress(hLibeay32, 'X509_STORE_new');
297 | @mFunctions.X509_STORE_free := GetProcAddress(hLibeay32, 'X509_STORE_free');
298 |
299 | @mFunctions.d2i_PKCS7_bio := GetProcAddress(hLibeay32, 'd2i_PKCS7_bio');
300 |
301 | @mFunctions.PEM_read_bio_PKCS7 := GetProcAddress(hLibeay32, 'PEM_read_bio_PKCS7');
302 | @mFunctions.SMIME_read_PKCS7 := GetProcAddress(hLibeay32, 'SMIME_read_PKCS7');
303 |
304 | @mFunctions.PKCS7_ctrl := GetProcAddress(hLibeay32, 'PKCS7_ctrl');
305 | @mFunctions.PKCS7_get_signer_info := GetProcAddress(hLibeay32, 'PKCS7_get_signer_info');
306 | @mFunctions.PKCS7_dataInit := GetProcAddress(hLibeay32, 'PKCS7_dataInit');
307 | @mFunctions.PKCS7_free := GetProcAddress(hLibeay32, 'PKCS7_free');
308 | @mFunctions.PKCS7_verify := GetProcAddress(hLibeay32, 'PKCS7_verify');
309 |
310 | // Optionals functions
311 | @mFunctions.d2i_CMS_bio := GetProcAddress(hLibeay32, 'd2i_CMS_bio');
312 | @mFunctions.CMS_dataInit := GetProcAddress(hLibeay32, 'CMS_dataInit');
313 | @mFunctions.CMS_verify := GetProcAddress(hLibeay32, 'CMS_verify');
314 | @mFunctions.CMS_ContentInfo_free := GetProcAddress(hLibeay32, 'CMS_ContentInfo_free');
315 |
316 | Result := Loaded;
317 |
318 | // Need to initialize the library.
319 | if Result then begin
320 | @OpenSSL_add_all_digests := GetProcAddress(hLibeay32, 'OPENSSL_add_all_algorithms_noconf'); // introduced OpenSSL 0.9.7
321 | if @OpenSSL_add_all_digests = nil then
322 | @OpenSSL_add_all_digests := GetProcAddress(hLibeay32, 'OpenSSL_add_all_digests'); // OpenSSL pre-0.9.6
323 | if @OpenSSL_add_all_digests <> nil then
324 | OpenSSL_add_all_digests
325 | else
326 | Result := False
327 | end;
328 |
329 | if Result then begin
330 | @ERR_load_crypto_strings := GetProcAddress(hLibeay32, 'ERR_load_crypto_strings');
331 | if @ERR_load_crypto_strings <> nil then begin
332 | ERR_load_crypto_strings;
333 | mErrorLoaded := True
334 | end
335 | end;
336 |
337 | if not Result then
338 | Unload
339 | end;
340 |
341 | procedure SetFolder(const Value: String);
342 | begin
343 | mFolder := IncludeTrailingPathDelimiter(Value)
344 | end;
345 |
346 | procedure Unload;
347 | begin
348 | mErrorLoaded := False;
349 | FillChar(mFunctions, SizeOf(TLibeay32Functions), 0)
350 | end;
351 |
352 | function GetVersion: String;
353 | var
354 | SSLeay_version: function(t: Integer): PAnsiChar cdecl;
355 | begin
356 | Result := '';
357 | @SSLeay_version := GetProcAddress(GetModuleHandle(LIBEAY32_LIBRARY), 'SSLeay_version'); // OpenSSL pre-0.9.6
358 | if @SSLeay_version <> nil then
359 | Result := String(SSLeay_version(0))
360 | end;
361 |
362 | function GetErrorCode: Cardinal;
363 | var
364 | ERR_get_error: function: Cardinal cdecl;
365 | begin
366 | Result := 0;
367 | @ERR_get_error := GetProcAddress(GetModuleHandle(LIBEAY32_LIBRARY), 'ERR_get_error'); // OpenSSL pre-0.9.6
368 | if @ERR_get_error <> nil then
369 | Result := ERR_get_error
370 | end;
371 |
372 | function GetError: String;
373 | begin
374 | Result := GetError(GetErrorCode)
375 | end;
376 |
377 | function GetError(const ErrorCode: Cardinal): String; overload;
378 | var
379 | P: Array[0..1023] of AnsiChar;
380 | ERR_error_string_n: procedure(e: Cardinal; b: PAnsiChar; l: {$IFDEF WIN64}UInt64{$ELSE}Cardinal{$ENDIF}) cdecl;
381 | ERR_error_string: function(e: Cardinal; b: PAnsiChar): PAnsiChar cdecl;
382 | begin
383 | Result := '';
384 | if mErrorLoaded and (ErrorCode > 0) then begin
385 | @ERR_error_string_n := GetProcAddress(GetModuleHandle(LIBEAY32_LIBRARY), 'ERR_error_string_n'); // introduced OpenSSL 0.9.6
386 | if @ERR_error_string_n <> nil then begin
387 | FillChar(P, SizeOf(P), 0);
388 | ERR_error_string_n(ErrorCode, P, SizeOf(P));
389 | Result := String(P)
390 | end else begin
391 | @ERR_error_string := GetProcAddress(GetModuleHandle(LIBEAY32_LIBRARY), 'ERR_error_string'); // OpenSSL pre-0.9.6
392 | if @ERR_error_string <> nil then
393 | Result := String(ERR_error_string(ErrorCode, nil))
394 | end
395 | end
396 | end;
397 |
398 | function GetFolder: String;
399 | var
400 | hLibeay32: THandle;
401 | szFilename: Array[0..MAX_PATH] of Char;
402 | begin
403 | Result := mFolder;
404 | hLibeay32 := GetModuleHandle(LIBEAY32_LIBRARY);
405 | if hLibeay32 > 0 then begin
406 | FillChar(szFilename, SizeOf(szFilename), 0);
407 | if GetModuleFileName(hLibeay32, szFilename, MAX_PATH) > 0 then
408 | Result := IncludeTrailingPathDelimiter(ExtractFilePath(szFilename))
409 | end
410 | end;
411 |
412 | function Loaded: Boolean;
413 | var
414 | I: Integer;
415 | begin
416 | Result := GetModuleHandle(LIBEAY32_LIBRARY) > 0;
417 | for I := 0 to LIBEAY32_FUNCTIONS_COUNT - LIBEAY32_OPTIONAL_COUNT - 1 do
418 | Result := Result and (mFunctions.Functions[I] <> nil)
419 | end;
420 |
421 | // ==================================================== BASE64 CHECK AND PREPARE
422 |
423 | {$IFDEF PKCS7MANAGEBASE64}
424 | const
425 | BASE64_BEGIN: AnsiString = '-----BEGIN PKCS7-----'#10; // DO NOT EDIT THIS! DO NOT CHANGE TYPE!
426 | BASE64_END: AnsiString = #10'-----END PKCS7-----'; // DO NOT EDIT THIS! DO NOT CHANGE TYPE!
427 |
428 | function Base64Prepare(Data: Pointer; iSize, iMemory: Integer): Integer;
429 | const
430 | BASE64_LOOKUP_TABLE: Array[Byte] of Byte = (
431 | {00} {01} {02} {03} {04} {05} {06} {07} {08} {09} {0A} {0B} {0C} {0D} {0E} {0F}
432 | {00} $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $7F, $FF, $FF, $7F, $FF, $FF,
433 | {10} $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
434 | {20} $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $3E, $FF, $3E, $FF, $3F,
435 | {30} $34, $35, $36, $37, $38, $39, $3A, $3B, $3C, $3D, $FF, $FF, $FF, $FF, $2B, $2F,
436 | {40} $FF, $00, $01, $02, $03, $04, $05, $06, $07, $08, $09, $0A, $0B, $0C, $0D, $0E,
437 | {50} $0F, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $FF, $FF, $FF, $FF, $3F,
438 | {60} $FF, $1A, $1B, $1C, $1D, $1E, $1F, $20, $21, $22, $23, $24, $25, $26, $27, $28,
439 | {70} $29, $2A, $2B, $2C, $2D, $2E, $2F, $30, $31, $32, $33, $FF, $FF, $FF, $FF, $FF,
440 | {80} $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
441 | {90} $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
442 | {A0} $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
443 | {B0} $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
444 | {C0} $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
445 | {D0} $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
446 | {E0} $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
447 | {F0} $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF);
448 | var
449 | I, X, iData, iPadding, iSkip: Int64;
450 | OldResult: Integer;
451 | P: Pointer;
452 | begin
453 | Result := iSize;
454 | // Counts how many Base64 characters are found, both data or padding (only at
455 | // the end of the block). CRs and LFs are considered valid but counted for
456 | // being skipped in the resulting block.
457 | iData := 0;
458 | iPadding := 0;
459 | iSkip := 0;
460 | for I := 0 to iSize - 1 do
461 | // This is a padding character
462 | if PByteArray(Data)^[I] = $3D then begin
463 | // No more than 2 padding characters are allowed at the end of the block.
464 | if iPadding < 2 then
465 | Inc(iPadding)
466 | else
467 | Exit
468 | end else
469 | // Checks for the corresponding Base64 value.
470 | case BASE64_LOOKUP_TABLE[PByteArray(Data)^[I]] of
471 | // Invalid character outside the Base64 alphabet.
472 | $FF: Exit;
473 | // CR or LF, will skip this.
474 | $7F: Inc(iSkip);
475 | else
476 | // If I didn't already start the final padding, this is to be considered good data.
477 | if iPadding = 0 then
478 | Inc(iData)
479 | else
480 | Exit
481 | end;
482 | // If data length (including padding) is greater that zero and 32-bit aligned, it's valid Base64 data!
483 | if ((iData + iPadding) mod 4 = 0) and ((iData + iPadding) > 0) then begin
484 | // If I've found some CRs and LFs, now let's remove them
485 | if iSkip > 0 then begin
486 | // Pre-calculate resulting buffer size.
487 | Result := iSize - iSkip;
488 | X := 0;
489 | // Allocate a temporary buffer.
490 | GetMem(P, Result);
491 | try
492 | for I := 0 to iSize - 1 do
493 | // Converting URL-safe
494 | if BASE64_LOOKUP_TABLE[PByteArray(Data)^[I]] = $3E then begin
495 | PByteArray(P)^[X] := $2B;
496 | Inc(X)
497 | end else
498 | if BASE64_LOOKUP_TABLE[PByteArray(Data)^[I]] = $3F then begin
499 | PByteArray(P)^[X] := $2F;
500 | Inc(X)
501 | end else
502 | // Copy every character to the temporary buffer skipping CRs and LFs.
503 | if BASE64_LOOKUP_TABLE[PByteArray(Data)^[I]] <> $7F then begin
504 | PByteArray(P)^[X] := PByteArray(Data)^[I];
505 | Inc(X)
506 | end;
507 | // Clear the destination buffer.
508 | FillChar(Data^, iMemory, 0);
509 | // Copy the temporary buffer to destination.
510 | Move(P^, Data^, Result)
511 | finally
512 | // Release memory used by the temporary buffer.
513 | FreeMem(P, Result)
514 | end
515 | end;
516 | // Make a copy of the buffer size.
517 | OldResult := Result;
518 | // Calculate the length of output buffer include heading and trailing tags.
519 | Result := Result + Length(BASE64_BEGIN) + Length(BASE64_END);
520 | // Allocate a temporary buffer.
521 | GetMem(P, Result);
522 | try
523 | // Compose final result:
524 | // - adds the heading tag;
525 | Move(BASE64_BEGIN[1], P^, Length(BASE64_BEGIN));
526 | // - adds the Base64 data;
527 | Move(Data^, PByteArray(P)^[Length(BASE64_BEGIN)], OldResult);
528 | // - adds the trailing tag.
529 | Move(BASE64_END[1], PByteArray(P)^[Length(BASE64_BEGIN) + OldResult], Length(BASE64_END));
530 | // Clearing the output buffer.
531 | FillChar(Data^, iMemory, 0);
532 | // Copying data from temporary buffer to output.
533 | Move(P^, Data^, Result)
534 | finally
535 | // Release
536 | FreeMem(P, Result)
537 | end
538 | end
539 | end;
540 | {$ENDIF}
541 |
542 | // =========================================================== LIBRARY FUNCTIONS
543 |
544 | function Extract(InStream, OutStream: TStream): Boolean;
545 | begin
546 | Result := False;
547 | if Loaded or Load then
548 | with TPKCS7Message.Create do try
549 | Result := LoadFromStream(InStream) and SaveToStream(OutStream)
550 | finally
551 | Free
552 | end
553 | end;
554 |
555 | function Extract(const InFilename, OutFilename: String): Boolean;
556 | begin
557 | Result := False;
558 | if Loaded or Load then
559 | with TPKCS7Message.Create do try
560 | Result := LoadFromFile(InFilename) and SaveToFile(OutFilename)
561 | finally
562 | Free
563 | end
564 | end;
565 |
566 | function Extract(const Filename: String): String;
567 | begin
568 | Result := GuessFilename(Filename);
569 | if (Result = Filename) or (not Extract(Filename, Result)) then
570 | Result := ''
571 | end;
572 |
573 | function Extract(Stream: TStream; var S: String): Boolean;
574 | var
575 | MStream: TMemoryStream;
576 | SStream: TStringStream;
577 | {$IF CompilerVersion >= 20}
578 | Encoding: TEncoding;
579 | Buffer: TBytes;
580 | iSize: Integer;
581 | {$IFEND}
582 | begin
583 | S := '';
584 | Result := False;
585 | if Stream = nil then
586 | Exit;
587 | MStream := TMemoryStream.Create;
588 | try
589 | if Extract(Stream, MStream) then begin
590 | {$IF CompilerVersion >= 20}
591 | iSize := MStream.Size;
592 | // That's long enough for preamble detection
593 | if iSize > 256 then
594 | iSize := 256;
595 | // Tries to detect correct encoding.
596 | SetLength(Buffer, iSize);
597 | try
598 | MStream.Position := 0;
599 | if MStream.Read(Buffer[0], iSize) = iSize then begin
600 | Encoding := nil;
601 | TEncoding.GetBufferEncoding(Buffer, Encoding)
602 | end else
603 | Encoding := TEncoding.Default
604 | finally
605 | SetLength(Buffer, 0)
606 | end;
607 | {$IFEND}
608 | // Read this with detected encoding (Delphi 2009+).
609 | SStream := TStringStream.Create(''{$IF CompilerVersion >= 20}, Encoding{$IFEND});
610 | try
611 | Result := SStream.CopyFrom(MStream, 0) = MStream.Size;
612 | if Result then
613 | S := SStream.DataString
614 | finally
615 | SStream.Free
616 | end
617 | end
618 | finally
619 | MStream.Free
620 | end
621 | end;
622 |
623 | function ExtractToString(const Filename: String; var S: String): Boolean;
624 | var
625 | InStream: TFileStream;
626 | begin
627 | Result := False;
628 | if FileExists(Filename) then try
629 | InStream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
630 | try
631 | InStream.Position := 0;
632 | Result := Extract(InStream, S)
633 | finally
634 | InStream.Free
635 | end
636 | except
637 | Result := False
638 | end
639 | end;
640 |
641 | function GuessFilename(const Filename: String): String;
642 | var
643 | S: String;
644 | begin
645 | Result := Filename;
646 | S := ChangeFileExt(Filename, '');
647 | if Length(ExtractFileExt(S)) > 0 then
648 | Result := S
649 | end;
650 |
651 | function Verify(Stream: TStream): TVerifyStatus;
652 | var
653 | P: Int64;
654 | begin
655 | Result := vsUnknown;
656 | if (Stream <> nil) and (Loaded or Load) then begin
657 | P := Stream.Position;
658 | try
659 | with TPKCS7Message.Create do try
660 | if LoadFromStream(Stream) then
661 | Result := VerifyStatus
662 | finally
663 | Free
664 | end
665 | finally
666 | Stream.Position := P
667 | end
668 | end
669 | end;
670 |
671 | function Verify(const Filename: String): TVerifyStatus;
672 | begin
673 | Result := vsUnknown;
674 | if Loaded or Load then
675 | with TPKCS7Message.Create do try
676 | if LoadFromFile(Filename) then
677 | Result := VerifyStatus
678 | finally
679 | Free
680 | end
681 | end;
682 |
683 | function SignatureMode(Stream: TStream): TSignatureMode;
684 | var
685 | P: Int64;
686 | begin
687 | Result := smUnknown;
688 | if (Stream <> nil) and (Loaded or Load) then begin
689 | P := Stream.Position;
690 | try
691 | with TPKCS7Message.Create do try
692 | if LoadFromStream(Stream) then
693 | Result := SignatureMode
694 | finally
695 | Free
696 | end
697 | finally
698 | Stream.Position := P
699 | end
700 | end
701 | end;
702 |
703 | function SignatureMode(const Filename: String): TSignatureMode;
704 | begin
705 | Result := smUnknown;
706 | if Loaded or Load then
707 | with TPKCS7Message.Create do try
708 | if LoadFromFile(Filename) then
709 | Result := SignatureMode
710 | finally
711 | Free
712 | end
713 | end;
714 |
715 | // ================================================================== MAIN CLASS
716 |
717 | { TPKCS7Message }
718 |
719 | procedure TPKCS7Message.Clear;
720 | begin
721 | if fContent <> nil then
722 | fContent.Free;
723 | fContent := nil;
724 | fSignatureMode := smUnknown;
725 | fVerifyStatus := vsUnknown
726 | end;
727 |
728 | constructor TPKCS7Message.Create;
729 | begin
730 | inherited Create;
731 | fContent := nil;
732 | fOnStreamCreate := nil;
733 | Clear
734 | end;
735 |
736 | destructor TPKCS7Message.Destroy;
737 | begin
738 | Clear;
739 | inherited Destroy
740 | end;
741 |
742 | function TPKCS7Message.LoadFromFile(const Filename: String): Boolean;
743 | var
744 | FStream: TFileStream;
745 | begin
746 | Result := False;
747 | if FileExists(Filename) then try
748 | FStream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
749 | try
750 | FStream.Position := 0;
751 | Result := LoadFromStream(FStream)
752 | finally
753 | FStream.Free
754 | end
755 | except
756 | Result := False
757 | end
758 | end;
759 |
760 | function TPKCS7Message.LoadFromStream(Stream: TStream): Boolean;
761 | var
762 | iRead: Integer;
763 | iTotalRead: Integer;
764 | iTotalMemory: Integer;
765 | iTotalWritten: Integer;
766 | iWritten: Integer;
767 | pCMS: PCMS_ContentInfo;
768 | pMemory: Pointer;
769 | pOutputBIO: PBIO;
770 | pPKCS: PPKCS7;
771 | pTempBIO: PBIO;
772 | pTempStack: PSTACK;
773 | pTempStore: PX509_STORE;
774 | begin
775 | // Default return value
776 | Result := False;
777 | // Clears class instance variables
778 | Clear;
779 | // Parameters check
780 | if (Stream = nil) or (not Loaded) then
781 | Exit;
782 |
783 | // Verify data length
784 | iTotalRead := Stream.Size - Stream.Position;
785 | if iTotalRead <= 0 then
786 | Exit;
787 |
788 | pCMS := nil;
789 | pPKCS := nil;
790 | try
791 | {$IFDEF PKCS7MANAGEBASE64}
792 | // Allocate enough memory for Base64 manipulation
793 | iTotalMemory := iTotalRead + Length(BASE64_BEGIN) + Length(BASE64_END);
794 | {$ELSE}
795 | iTotalMemory := iTotalRead;
796 | {$ENDIF}
797 | GetMem(pMemory, iTotalMemory);
798 | try
799 | // Read from stream
800 | if Stream.Read(pMemory^, iTotalRead) = iTotalRead then begin
801 |
802 | {$IFDEF PKCS7MANAGEBASE64}
803 | // Check if it's Base64 data and manage it.
804 | iTotalRead := Base64Prepare(pMemory, iTotalRead, iTotalMemory);
805 | {$ENDIF}
806 |
807 | // Create BIO from this memory
808 | pTempBIO := mFunctions.BIO_new_mem_buf(pMemory, iTotalRead);
809 |
810 | if pTempBIO <> nil then try
811 | // Tries to read this as a .P7M file.
812 | pPKCS := mFunctions.d2i_PKCS7_bio(pTempBIO, nil);
813 |
814 | // If it failed, tries again as a .PEM file. (rewinds BIO to BOF)
815 | if (pPKCS = nil) and (mFunctions.BIO_ctrl(pTempBIO, BIO_CTRL_RESET, 0, nil) = 1) then
816 | pPKCS := mFunctions.PEM_read_bio_PKCS7(pTempBIO, nil, nil, nil);
817 |
818 | // If failed again, tries as a S/MIME message. (rewinds BIO to BOF)
819 | if (pPKCS = nil) and (mFunctions.BIO_ctrl(pTempBIO, BIO_CTRL_RESET, 0, nil) = 1) then
820 | pPKCS := mFunctions.SMIME_read_PKCS7(pTempBIO, nil);
821 |
822 | // If CMS cryptographic functions are available (hence using OpenSSL libraries 0.9.8h+)
823 | if (@mFunctions.d2i_CMS_bio <> nil) and (@mFunctions.CMS_verify <> nil) and (@mFunctions.CMS_ContentInfo_free <> nil) then
824 | // If failed, let's switch to CMS encoding (rewinds BIO to BOF)
825 | if (pPKCS = nil) and (mFunctions.BIO_ctrl(pTempBIO, BIO_CTRL_RESET, 0, nil) = 1) then begin
826 | pCMS := mFunctions.d2i_CMS_bio(pTempBIO, nil);
827 | if pCMS <> nil then begin
828 | // We need a X509_STORE here.
829 | pTempStore := mFunctions.X509_STORE_new;
830 | if pTempStore <> nil then try
831 | if mFunctions.CMS_verify(pCMS, nil, pTempStore, nil, nil, CMS_NOVERIFY) <> LIBEAY_OK then begin
832 | mFunctions.CMS_ContentInfo_free(pCMS);
833 | pCMS := nil
834 | end
835 | finally
836 | mFunctions.X509_STORE_free(pTempStore)
837 | end
838 | end
839 | end
840 | finally
841 | mFunctions.BIO_free(pTempBIO)
842 | end
843 | end
844 | finally
845 | FreeMem(pMemory, iTotalMemory)
846 | end;
847 |
848 | // Can only chech these when using PKCS#7
849 |
850 | // Check that the given data was signed using PKCS#7.
851 | // This is a small verification also on Verification = False
852 | if (pPKCS <> nil) and (mFunctions.OBJ_obj2nid(pPKCS^._type) <> NID_PKCS7_SIGNED) then begin
853 | mFunctions.PKCS7_free(pPKCS);
854 | pPKCS := nil
855 | end;
856 | if (pPKCS <> nil) and (Pointer(mFunctions.PKCS7_ctrl(pPKCS, PKCS7_OP_GET_DETACHED_SIGNATURE, 0, nil)) <> nil) then begin
857 | mFunctions.PKCS7_free(pPKCS);
858 | pPKCS := nil
859 | end;
860 | // Reads data about signer and checks that at least one is present.
861 | // This is a small verification also on Verification = False
862 | if pPKCS <> nil then begin
863 | pTempStack := mFunctions.PKCS7_get_signer_info(pPKCS);
864 | if (pTempStack = nil) or (mFunctions.sk_num(pTempStack) = 0) then begin
865 | mFunctions.PKCS7_free(pPKCS);
866 | pPKCS := nil
867 | end
868 | end;
869 |
870 | // Going to verify input for both PKCS#7 and CMS
871 | if (pPKCS <> nil) or (pCMS <> nil) then begin
872 | // Creates memory structure for output data
873 | pOutputBIO := mFunctions.BIO_new(mFunctions.BIO_s_mem);
874 | if pOutputBIO <> nil then try
875 | // Tries *full* verificiation
876 |
877 | // Verifies PKCS7 while extracting it.
878 | pTempStore := mFunctions.X509_STORE_new;
879 | if pTempStore <> nil then try
880 | if pPKCS <> nil then
881 | Result := mFunctions.PKCS7_verify(pPKCS, nil, pTempStore, nil, pOutputBIO, PKCS7_NOVERIFY) = LIBEAY_OK
882 | else
883 | // No need to test @mFunctions.CMS_verify <> nil since pCMS will be null
884 | Result := mFunctions.CMS_verify(pCMS, nil, pTempStore, nil, pOutputBIO, CMS_NOVERIFY) = LIBEAY_OK
885 | finally
886 | mFunctions.X509_STORE_free(pTempStore)
887 | end;
888 | // If we managed to get the data, we have a *full* verification.
889 | if Result then
890 | fVerifyStatus := vsFull
891 | else begin
892 | // Does no verification, just tries to read.
893 |
894 | // Writes out extracted data.
895 | if pPKCS <> nil then
896 | pTempBIO := mFunctions.PKCS7_dataInit(pPKCS, nil)
897 | else
898 | // No need to test @mFunctions.CMS_dataInit <> nil since pCMS will be null
899 | pTempBIO := mFunctions.CMS_dataInit(pCMS, nil);
900 | if pTempBIO <> nil then try
901 | GetMem(pMemory, READ_BUFFER_SIZE);
902 | try
903 | // Copies data from buffer to output BIO
904 | iTotalRead := 0;
905 | iTotalWritten := 0;
906 | repeat
907 | iRead := mFunctions.BIO_read(pTempBIO, pMemory, READ_BUFFER_SIZE);
908 | iTotalRead := iTotalRead + iRead;
909 | if iRead > 0 then begin
910 | iWritten := mFunctions.BIO_write(pOutputBIO, pMemory, iRead);
911 | iTotalWritten := iTotalWritten + iWritten;
912 | end;
913 | until iRead <= 0;
914 | Result := iTotalRead = iTotalWritten
915 | finally
916 | FreeMem(pMemory, READ_BUFFER_SIZE)
917 | end
918 | finally
919 | mFunctions.BIO_free(pTempBIO)
920 | end;
921 | // If we were able to get that data, we have a partial verify.
922 | if Result then
923 | fVerifyStatus := vsPartial
924 | end;
925 |
926 | // Writes to output stream
927 | if Result and (pOutputBIO^.num_write > 0) then begin
928 | // Allows developer to use a better TStream descendant for storage (TFileStream, ...)
929 | if @fOnStreamCreate <> nil then
930 | fOnStreamCreate(Self, fContent);
931 | // By default we use TMemorySteam, this could use a bit of RAM.
932 | if fContent = nil then
933 | fContent := TMemoryStream.Create;
934 | GetMem(pMemory, pOutputBIO^.num_write);
935 | try
936 | // Copies data from BIO to memory buffer.
937 | Result := (Cardinal(mFunctions.BIO_read(pOutputBIO, pMemory, pOutputBIO^.num_write)) = pOutputBIO^.num_write) and
938 | // Copies data from memory to stream.
939 | (fContent.Write(pMemory^, pOutputBIO^.num_write) = Int64(pOutputBIO^.num_write))
940 | finally
941 | FreeMem(pMemory)
942 | end
943 | end
944 | finally
945 | mFunctions.BIO_free(pOutputBIO)
946 | end
947 | end;
948 | finally
949 | if Result then begin
950 | // Saves signature mode.
951 | if pCMS <> nil then
952 | fSignatureMode := smCMS
953 | else if pPKCS <> nil then
954 | fSignatureMode := smPKCS7;
955 | end else
956 | // Since it was a failure, make sure everything is cleared out.
957 | Clear;
958 | // Frees everything left in memory. (no need to test @mFunctions.CMS_ContentInfo_free <> nil)
959 | if pCMS <> nil then
960 | mFunctions.CMS_ContentInfo_free(pCMS);
961 | if pPKCS <> nil then
962 | mFunctions.PKCS7_free(pPKCS)
963 | end
964 | end;
965 |
966 | function TPKCS7Message.SaveToFile(const Filename: String): Boolean;
967 | var
968 | FStream: TFileStream;
969 | wMode: Word;
970 | begin
971 | // File open mode
972 | wMode := fmOpenReadWrite or fmShareDenyWrite;
973 | if not FileExists(Filename) then
974 | wMode := wMode or fmCreate;
975 | // Opens file stream
976 | try
977 | FStream := TFileStream.Create(Filename, wMode);
978 | try
979 | // Saves to output stream
980 | FStream.Size := 0;
981 | Result := SaveToStream(FStream)
982 | finally
983 | FStream.Free
984 | end
985 | except
986 | Result := False;
987 | // When something goes wrong when creating a file, make sure
988 | // we don't leave empty files around.
989 | if wMode and fmCreate = fmCreate then
990 | Windows.DeleteFile(PChar(Filename))
991 | end
992 | end;
993 |
994 | function TPKCS7Message.SaveToStream(Stream: TStream): Boolean;
995 | begin
996 | Result := False;
997 | if (fContent <> nil) and (Stream <> nil) then
998 | Result := Stream.CopyFrom(fContent, 0) = fContent.Size
999 | end;
1000 |
1001 | initialization
1002 | FillChar(mFunctions, SizeOf(TLibeay32Functions), 0)
1003 |
1004 | finalization
1005 |
1006 | end.
1007 |
--------------------------------------------------------------------------------
/Tests/PKCS7ExtractorTest.dpr:
--------------------------------------------------------------------------------
1 | program PKCS7ExtractorTest;
2 |
3 | {$APPTYPE CONSOLE}
4 |
5 | {***************************************************************************}
6 | { }
7 | { PKCS#7 Extractor for Delphi Unit Tests }
8 | { Version 1.3.0.0 released June, 15th 2021 }
9 | { }
10 | { Copyright (C) 2018 Delphi Club Italia }
11 | { http://www.delphiclubitalia.it }
12 | { }
13 | { Original authors: }
14 | { Christian Cristofori github@christiancristofori.it }
15 | { Giancarlo Oneglio giancarlo.oneglio@gmail.com }
16 | { }
17 | {***************************************************************************}
18 | { }
19 | { Licensed under the GNU Lesser General Public License, Version 3; }
20 | { you may not use this file except in compliance with the License. }
21 | { }
22 | { This is free software: you can redistribute it and/or modify it under }
23 | { the terms of the GNU Lesser General Public License as published by the }
24 | { Free Software Foundation, either version 3 of the License, or (at your }
25 | { option) any later version. }
26 | { }
27 | { This is distributed in the hope that it will be useful, but WITHOUT }
28 | { ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or }
29 | { FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public }
30 | { License for more details. }
31 | { }
32 | { You should have received a copy of the GNU Lesser General Public }
33 | { License along with this software. }
34 | { If not, see . }
35 | { }
36 | {***************************************************************************}
37 |
38 | uses
39 | Windows,
40 | SysUtils,
41 | Classes,
42 | PKCS7Extractor in '..\Source\PKCS7Extractor.pas';
43 |
44 | const
45 | ARCHITECTURE = {$IFDEF WIN64}'x64'{$ELSE}'x86'{$ENDIF};
46 |
47 | var
48 | paramList: Boolean;
49 | paramVersions: TStringList;
50 | currentFolder: String;
51 |
52 | function GetTempFolder: String;
53 | var
54 | dwLength: Cardinal;
55 | szPath: PChar;
56 | begin
57 | Result := '';
58 | dwLength := GetTempPath(0, nil);
59 | if dwLength = 0 then Exit;
60 | Inc(dwLength);
61 | GetMem(szPath, dwLength * SizeOf(Char));
62 | try
63 | FillChar(szPath^, dwLength * SizeOf(Char), 0);
64 | if GetTempPath(dwLength, szPath) > 0 then
65 | Result := IncludeTrailingPathDelimiter(szPath)
66 | finally
67 | FreeMem(szPath, dwLength * SizeOf(Char))
68 | end
69 | end;
70 |
71 | function GetTempFile(const AFolder, APrefix: String): String;
72 | const
73 | ALPHA = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
74 | var
75 | S, sFolder: String;
76 | szFile: PChar;
77 | szPrefix: Array[0..3] of Char;
78 | I: Integer;
79 | begin
80 | Result := '';
81 | // Checks given folder
82 | sFolder := IncludeTrailingPathDelimiter(AFolder);
83 | if (not DirectoryExists(sFolder)) and (not ForceDirectories(sFolder)) then begin
84 | if Length(AFolder) > 0 then Exit;
85 | // If no folder was given, checks for the TEMP folder
86 | sFolder := GetTempFolder;
87 | if (not DirectoryExists(sFolder)) and (not ForceDirectories(sFolder)) then Exit
88 | end;
89 | // Generates a random prefix
90 | FillChar(szPrefix, Length(szPrefix) * SizeOf(Char), 0);
91 | Randomize;
92 | for I := 0 to 2 do
93 | szPrefix[I] := ALPHA[Random(Length(ALPHA)) + 1];
94 | S := APrefix;
95 | for I := Length(S) downto 1 do
96 | if Pos(S[I], ALPHA) = 0 then
97 | Delete(S, I, 1);
98 | if Length(S) > 3 then
99 | Delete(S, 4, MaxInt);
100 | for I := 1 to Length(S) do
101 | szPrefix[I - 1] := S[I];
102 | // Generates the filename
103 | GetMem(szFile, (MAX_PATH + 1) * SizeOf(Char));
104 | try
105 | FillChar(szFile^, (MAX_PATH + 1) * SizeOf(Char), 0);
106 | if GetTempFileName(PChar(sFolder), szPrefix, 0, szFile) > 0 then
107 | Result := szFile
108 | finally
109 | FreeMem(szFile, (MAX_PATH + 1) * SizeOf(Char))
110 | end
111 | end;
112 |
113 | var
114 | slLibs: TStringList;
115 |
116 | procedure SearchLibrary(const F: String);
117 | var
118 | SearchRec: TSearchRec;
119 | R: Integer;
120 | N: String;
121 | begin
122 | R := FindFirst(IncludeTrailingPathDelimiter(F) + '*', faAnyFile, SearchRec);
123 | while R = 0 do begin
124 | if (SearchRec.Attr and faDirectory = faDirectory) and (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then begin
125 | N := IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(F) + SearchRec.Name);
126 | if FileExists(N + LIBEAY32_LIBRARY) and ((paramVersions.Count = 0) or (paramVersions.IndexOf(SearchRec.Name) > -1)) then
127 | slLibs.Add(N);
128 | SearchLibrary(N)
129 | end;
130 | R := FindNext(SearchRec)
131 | end;
132 | FindClose(SearchRec);
133 | end;
134 |
135 | var
136 | slTests: TStringList;
137 |
138 | procedure SearchTests;
139 | var
140 | SearchRec: TSearchRec;
141 | R: Integer;
142 | begin
143 | R := FindFirst(currentFolder + 'Data\*', faAnyFile - faDirectory, SearchRec);
144 | while R = 0 do begin
145 | if FileExists(currentFolder + 'DataCheck\' + SearchRec.Name) then
146 | slTests.Add(SearchRec.Name);
147 | R := FindNext(SearchRec)
148 | end;
149 | FindClose(SearchRec);
150 | end;
151 |
152 | procedure EmptyFolder(const S: String);
153 | var
154 | SearchRec: TSearchRec;
155 | R: Integer;
156 | begin
157 | if Length(S) = 0 then Exit;
158 | R := FindFirst(IncludeTrailingPathDelimiter(S) + '*', faAnyFile, SearchRec);
159 | while R = 0 do begin
160 | if SearchRec.Attr and faDirectory = faDirectory then begin
161 | if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then begin
162 | EmptyFolder(IncludeTrailingPathDelimiter(S) + SearchRec.Name);
163 | RemoveDir(IncludeTrailingPathDelimiter(S) + SearchRec.Name)
164 | end
165 | end else
166 | DeleteFile(IncludeTrailingPathDelimiter(S) + SearchRec.Name);
167 | R := FindNext(SearchRec)
168 | end;
169 | FindClose(SearchRec);
170 | end;
171 |
172 | const
173 | VERIFY_STATUS_TO_STRING: Array[TVerifyStatus] of String = ('vsUnknown', 'vsFull', 'vsPartial');
174 | SIGNATURE_MODE_TO_STRING: Array[TSignatureMode] of String = ('smUnknown', 'smPKCS7', 'smCMS');
175 | var
176 | tempFile, tempFolder: String;
177 | msInput, msOutput: TMemoryStream;
178 | S: String;
179 | I: Integer;
180 | V: TVerifyStatus;
181 | M: TSignatureMode;
182 | begin
183 | try
184 | paramList := FindCmdLineSwitch('list', ['/', '-'], True);
185 | if not paramList then begin
186 | WriteLn('PKCS#7 Extractor library for Delphi. Copyright (C) Delphi Club Italia, 2018.');
187 | WriteLn;
188 | WriteLn('Usage:');
189 | WriteLn(' PKCS7ExtractorTest.exe [version-folder, ...] [/list]');
190 | WriteLn;
191 | WriteLn('Specify the names of one or more version folders inside the OpenSSL to run only those test.');
192 | WriteLn('Use /list to generate the list of what OpenSSL version where loaded.');
193 | WriteLn;
194 | WriteLn('This software requires four sub-folders in the folder it is run:');
195 | WriteLn(' Data - put here some PKCS#7 messages files on whose you want to run tests');
196 | WriteLn(' DataCheck - put here the expected result files from extraction, note that the files');
197 | WriteLn(' needs to have the same filename and extension (thus including added extension');
198 | WriteLn(' that the source files in the Data folder.');
199 | WriteLn;
200 | WriteLn(' [Tests will be run only on files that have a correspondence in the two above folders!]');
201 | WriteLn;
202 | WriteLn(' OpenSSL - put in any subfolder the OpenSSL libraries. Tests will be executed for every');
203 | WriteLn(' different copy of the libraries found.');
204 | WriteLn;
205 | WriteLn(' [This folder is separated in two folders: "x64" and "x32", program will work on folder');
206 | WriteLn(' compatible with compilatio architecture]');
207 | WriteLn;
208 | WriteLn(' Temp - a temporary folder the program uses for saving files. This folder gets created');
209 | WriteLn(' automatically every time you run this program.');
210 | WriteLn;
211 | WriteLn(' [DO NOT PUT FILES INTO THIS FOLDER, THEY WILL BE DELETED]');
212 | WriteLn;
213 | end;
214 | tempFile := GetTempFile('', 'p7m');
215 | currentFolder := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)));
216 | tempFolder := currentFolder + 'Temp\';
217 | if (not FileExists(tempFolder)) and (not ForceDirectories(tempFolder)) then begin
218 | WriteLn(Format('WARNING: inaccessible folder "%s", PKCS7Extract(Filename) will not be tested.', [tempFolder]));
219 | tempFolder := '';
220 | end;
221 | EmptyFolder(tempFolder);
222 | slLibs := TStringList.Create;
223 | slTests := TStringList.Create;
224 | try
225 | Write('Searching for libraries... ');
226 | paramVersions := TStringList.Create;
227 | try
228 | if (not paramList) and (ParamCount > 0) then
229 | for I := 1 to ParamCount do
230 | paramVersions.Add(ParamStr(I));
231 | SearchLibrary(currentFolder + 'OpenSSL\' + ARCHITECTURE);
232 | finally
233 | paramVersions.Free
234 | end;
235 | if slLibs.Count = 0 then begin
236 | WriteLn('no libraries found!');
237 | WriteLn(Format('Unable to continue: please be sure to put at least one libeay32.dll into folder "%sOpenSSL\" or in a subfolder.', [currentFolder]));
238 | Exit
239 | end else
240 | WriteLn(Format('%d libraries found.', [slLibs.Count]));
241 |
242 | if paramList then begin
243 | I := 0;
244 | while slLibs.Count > 0 do begin
245 | PKCS7Extractor.Unload;
246 | FreeLibrary(GetModuleHandle(LIBEAY32_LIBRARY));
247 | PKCS7Extractor.SetFolder(slLibs[0]);
248 | if PKCS7Extractor.Loaded then begin
249 | Inc(I);
250 | slTests.Add(PKCS7Extractor.GetVersion)
251 | end;
252 | slLibs.Delete(0)
253 | end;
254 | slTests.Sorted := False;
255 | slTests.Sorted := True;
256 | while slTests.Count > 0 do begin
257 | WriteLn(slTests[0]);
258 | slTests.Delete(0)
259 | end;
260 | WriteLn(Format('%d libraries successfully loaded.', [I]));
261 | Exit
262 | end;
263 |
264 | Write('Searching for test data... ');
265 | SearchTests;
266 | if slTests.Count = 0 then begin
267 | WriteLn('no test data found!');
268 | WriteLn(Format('Unable to continue: please be sure to put at least one file into folder "%sData\" and the result file with the same filename in the "%sDataCheck\" folder.', [currentFolder, currentFolder]));
269 | Exit
270 | end else
271 | WriteLn(Format('%d test(s) will be run.', [slTests.Count]));
272 |
273 | WriteLn('Testing PKCS7Extractor.GuessFilename(Filename)...');
274 | for I := 0 to slTests.Count - 1 do begin
275 | S := PKCS7Extractor.GuessFilename(slTests[I]);
276 | if S = slTests[I] then
277 | S := 'failed.'
278 | else
279 | S := Format('"%s"', [S]);
280 | WriteLn(Format(' testing with file "%s"... %s', [slTests[I], S]))
281 | end;
282 |
283 | msInput := TMemoryStream.Create;
284 | msOutput := TMemoryStream.Create;
285 | try
286 | while slLibs.Count > 0 do begin
287 |
288 | WriteLn;
289 | WriteLn(Format('Testing with library located at "%s".', [slLibs[0]]));
290 |
291 | PKCS7Extractor.Unload;
292 | FreeLibrary(GetModuleHandle(LIBEAY32_LIBRARY));
293 | PKCS7Extractor.SetFolder(slLibs[0]);
294 | slLibs.Delete(0);
295 | Write('Testing PKCS7Extractor.Load... ');
296 | if PKCS7Extractor.Load then
297 | WriteLn('successfully loaded.')
298 | else
299 | WriteLn('NOT LOADED!');
300 |
301 | WriteLn(Format('Testing PKCS7LibraryLocation: "%s"', [PKCS7Extractor.GetFolder]));
302 | WriteLn(Format('Testing PKCS7LibraryVersion: "%s"', [PKCS7Extractor.GetVersion]));
303 |
304 | WriteLn('Testing PKCS7Extractor.Verify(Stream)...');
305 | for I := 0 to slTests.Count - 1 do begin
306 | Write(Format(' testing with file "%s"... ', [slTests[I]]));
307 | msInput.Clear;
308 | try
309 | msInput.LoadFromFile(currentFolder + 'Data\' + slTests[I]);
310 | msInput.Position := 0
311 | except
312 | WriteLn('unable to load input file.');
313 | Continue
314 | end;
315 | V := PKCS7Extractor.Verify(msInput);
316 | if V in [vsFull, vsPartial] then
317 | WriteLn(Format('data corresponds to a valid PKCS#7 message file. (%s)', [VERIFY_STATUS_TO_STRING[V]]))
318 | else
319 | WriteLn('not a valid PKCS#7 message file.');
320 | end;
321 |
322 | WriteLn('Testing PKCS7Extractor.Verify(Filename)...');
323 | for I := 0 to slTests.Count - 1 do begin
324 | Write(Format(' testing with file "%s"... ', [slTests[I]]));
325 | V := PKCS7Extractor.Verify(currentFolder + 'Data\' + slTests[I]);
326 | if V in [vsFull, vsPartial] then
327 | WriteLn(Format('data corresponds to a valid PKCS#7 message file. (%s)', [VERIFY_STATUS_TO_STRING[V]]))
328 | else
329 | WriteLn('not a valid PKCS#7 message file.');
330 | end;
331 |
332 | WriteLn('Testing PKCS7Extractor.SignatureMode(Stream)...');
333 | for I := 0 to slTests.Count - 1 do begin
334 | Write(Format(' testing with file "%s"... ', [slTests[I]]));
335 | msInput.Clear;
336 | try
337 | msInput.LoadFromFile(currentFolder + 'Data\' + slTests[I]);
338 | msInput.Position := 0
339 | except
340 | WriteLn('unable to load input file.');
341 | Continue
342 | end;
343 | M := PKCS7Extractor.SignatureMode(msInput);
344 | if M in [smPKCS7, smCMS] then
345 | WriteLn(Format('data corresponds to a valid PKCS#7 message file. (%s)', [SIGNATURE_MODE_TO_STRING[M]]))
346 | else
347 | WriteLn('not a valid PKCS#7 message file.');
348 | end;
349 |
350 | WriteLn('Testing PKCS7Extractor.SignatureMode(Filename)...');
351 | for I := 0 to slTests.Count - 1 do begin
352 | Write(Format(' testing with file "%s"... ', [slTests[I]]));
353 | M := PKCS7Extractor.SignatureMode(currentFolder + 'Data\' + slTests[I]);
354 | if M in [smPKCS7, smCMS] then
355 | WriteLn(Format('data corresponds to a valid PKCS#7 message file. (%s)', [SIGNATURE_MODE_TO_STRING[M]]))
356 | else
357 | WriteLn('not a valid PKCS#7 message file.');
358 | end;
359 |
360 | WriteLn('Testing PKCS7Extractor.Extract(Stream, Stream)...');
361 | for I := 0 to slTests.Count - 1 do begin
362 | Write(Format(' testing with file "%s"... ', [slTests[I]]));
363 | msInput.Clear;
364 | try
365 | msInput.LoadFromFile(currentFolder + 'Data\' + slTests[I]);
366 | msInput.Position := 0
367 | except
368 | WriteLn('unable to load input file.');
369 | Continue
370 | end;
371 |
372 | msOutput.Clear;
373 | if PKCS7Extractor.Extract(msInput, msOutput) then begin
374 | msInput.Clear;
375 | try
376 | msInput.LoadFromFile(currentFolder + 'DataCheck\' + slTests[I])
377 | except
378 | WriteLn('file has been extracted but can''t load check file for comparision.');
379 | Continue
380 | end;
381 | if (msInput.Size = msOutput.Size) and CompareMem(msInput.Memory, msOutput.Memory, msInput.Size) then
382 | WriteLn('success.')
383 | else
384 | WriteLn('extracted data does not correspond to check file content.')
385 | end else
386 | WriteLn('not a valid PKCS#7 message file.')
387 | end;
388 |
389 | WriteLn('Testing PKCS7Extractor.Extract(Filename, Filename)...');
390 | for I := 0 to slTests.Count - 1 do begin
391 | Write(Format(' testing with file "%s"... ', [slTests[I]]));
392 | if PKCS7Extractor.Extract(currentFolder + 'Data\' + slTests[I], tempFile) then begin
393 | msInput.Clear;
394 | try
395 | msInput.LoadFromFile(currentFolder + 'DataCheck\' + slTests[I])
396 | except
397 | WriteLn('file has been extracted but can''t load check file for comparision.');
398 | Continue
399 | end;
400 | msOutput.Clear;
401 | try
402 | msOutput.LoadFromFile(tempFile)
403 | except
404 | WriteLn('file has been extracted but can''t load temporary output file for comparision.');
405 | Continue
406 | end;
407 | if (msInput.Size = msOutput.Size) and CompareMem(msInput.Memory, msOutput.Memory, msInput.Size) then
408 | WriteLn('success.')
409 | else
410 | WriteLn('extracted data does not correspond to check file content.')
411 | end else
412 | WriteLn('not a valid PKCS#7 message file.')
413 | end;
414 |
415 | if Length(tempFolder) > 0 then begin
416 | WriteLn('Testing PKCS7Extractor.Extract(Filename)...');
417 | for I := 0 to slTests.Count - 1 do begin
418 | EmptyFolder(tempFolder);
419 | Write(Format(' testing with file "%s"... ', [slTests[I]]));
420 | if CopyFile(PChar(currentFolder + 'Data\' + slTests[I]), PChar(tempFolder + slTests[I]), True) then begin
421 | try
422 | S := PKCS7Extractor.Extract(tempFolder + slTests[I]);
423 | if Length(S) > 0 then begin
424 | try
425 | msInput.Clear;
426 | try
427 | msInput.LoadFromFile(currentFolder + 'DataCheck\' + slTests[I])
428 | except
429 | WriteLn('file has been extracted but can''t load check file for comparision.');
430 | Continue
431 | end;
432 | msOutput.Clear;
433 | try
434 | msOutput.LoadFromFile(S)
435 | except
436 | WriteLn('file has been extracted but can''t load temporary output file for comparision.');
437 | Continue
438 | end;
439 | if (msInput.Size = msOutput.Size) and CompareMem(msInput.Memory, msOutput.Memory, msInput.Size) then
440 | WriteLn('success.')
441 | else
442 | WriteLn('extracted data does not correspond to check file content.')
443 | finally
444 | DeleteFile(S)
445 | end
446 | end else
447 | WriteLn('not a valid PKCS#7 message file or destination filename can''t be guessed.')
448 | finally
449 | DeleteFile(tempFolder + slTests[I])
450 | end
451 | end else
452 | WriteLn('failed copying file itno temp folder.')
453 | end;
454 | EmptyFolder(tempFolder)
455 | end;
456 |
457 | WriteLn('Testing PKCS7Extractor.Extract(Stream, var String)...');
458 | for I := 0 to slTests.Count - 1 do begin
459 | Write(Format(' testing with file "%s"... ', [slTests[I]]));
460 | msInput.Clear;
461 | try
462 | msInput.LoadFromFile(currentFolder + 'Data\' + slTests[I]);
463 | msInput.Position := 0
464 | except
465 | WriteLn('unable to load input file.');
466 | Continue
467 | end;
468 | if PKCS7Extractor.Extract(msInput, S) then
469 | WriteLn(Format('success (string length %d characters).', [Length(S)]))
470 | else
471 | WriteLn('extracted data does not correspond to check file content.')
472 | end;
473 |
474 | WriteLn('Testing PKCS7Extractor.PKCS7ExtractToString(String, var String)...');
475 | for I := 0 to slTests.Count - 1 do begin
476 | Write(Format(' testing with file "%s"... ', [slTests[I]]));
477 | if PKCS7Extractor.ExtractToString(currentFolder + 'Data\' + slTests[I], S) then
478 | WriteLn(Format('success (string length %d characters).', [Length(S)]))
479 | else
480 | WriteLn('extracted data does not correspond to check file content.')
481 | end
482 |
483 | end
484 | finally
485 | msInput.Free;
486 | msOutput.Free
487 | end
488 | finally
489 | if FileExists(tempFile) then
490 | DeleteFile(tempFile);
491 | slLibs.Free;
492 | slTests.Free;
493 | EmptyFolder(tempFolder)
494 | end
495 | except
496 | on E: Exception do
497 | Writeln(E.ClassName, ': ', E.Message)
498 | end;
499 | {$IFDEF DEBUG}
500 | WriteLn;
501 | Write('Press [ENTER] key to continue... ');
502 | ReadLn
503 | {$ENDIF}
504 | end.
505 |
--------------------------------------------------------------------------------
/Tests/PKCS7ExtractorTest.lpi:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/Tests/create_datacheck.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 | CLS
3 | IF NOT EXIST .\OpenSSL\OpenSSL.exe (
4 | ECHO Unable to find .\OpenSSL\OpenSSL.exe
5 | GOTO TheEnd
6 | )
7 | .\OpenSSL\OpenSSL.exe help >NUL 2>&1
8 | IF %ERRORLEVEL% NEQ 0 (
9 | ECHO Unable to execute .\OpenSSL\OpenSSL.exe, please check!
10 | GOTO TheEnd
11 | )
12 | .\OpenSSL\OpenSSL.exe version
13 | ECHO.
14 | REM Faccio l'operazione per tutti i file .p7m che trovo nella sottocartella Data
15 | FOR /R ".\Data" %%f IN (*.p7m) DO (
16 | REM Se lo stesso file esiste nella cartella DataCheck lo elimino
17 | DEL /F /Q ".\DataCheck\%%~nf%%~xf" >NUL 2>&1
18 | REM Tento di estrarlo come S/MIME
19 | .\OpenSSL\OpenSSL.EXE smime -verify -noverify -in ".\Data\%%~nf%%~xf" -inform DER -out ".\DataCheck\%%~nf%%~xf" >NUL 2>&1
20 | REM Se il file è vuoto (lunghezza zero) lo elimino
21 | FOR /F %%a in (".\DataCheck\%%~nf%%~xf") DO (
22 | IF %%~za EQU 0 (
23 | DEL /F /Q %%a >NUL 2>&1
24 | )
25 | )
26 | REM Se il file non esiste tento di estrarlo come CMS
27 | IF NOT EXIST ".\DataCheck\%%~nf%%~xf" (
28 | .\OpenSSL\OpenSSL.EXE cms -verify -noverify -in ".\Data\%%~nf%%~xf" -inform DER -out ".\DataCheck\%%~nf%%~xf" >NUL 2>&1
29 | )
30 | REM Se il file è vuoto (lunghezza zero) lo elimino
31 | FOR /F %%a in (".\DataCheck\%%~nf%%~xf") DO (
32 | IF %%~za EQU 0 (
33 | DEL /F /Q %%a >NUL 2>&1
34 | )
35 | )
36 | REM Metto in output il risultato
37 | IF EXIST ".\DataCheck\%%~nf%%~xf" (
38 | ECHO %%~nf%%~xf extracted correctly
39 | ) ELSE (
40 | ECHO.
41 | ECHO Could not extract %%~nf%%~xf
42 | ECHO.
43 | )
44 | )
45 | :TheEnd
--------------------------------------------------------------------------------
/Utils/CCLib.TempFileStream.pas:
--------------------------------------------------------------------------------
1 | unit CCLib.TempFileStream;
2 |
3 | {***************************************************************************}
4 | { }
5 | { Christian Cristofori's Library for Delphi }
6 | { }
7 | { Copyright (C) Christian Cristofori, 1998-2020. }
8 | { }
9 | {***************************************************************************}
10 |
11 | interface
12 |
13 | uses
14 | Classes;
15 |
16 | type
17 | TTempFileSteam = class(THandleStream)
18 | private
19 | FFilename: String;
20 | public
21 | constructor Create; reintroduce;
22 | destructor Destroy; override;
23 |
24 | property FileName: String read FFilename;
25 | end;
26 |
27 | implementation
28 |
29 | uses
30 | Windows, SysUtils;
31 |
32 | { TTempFileSteam }
33 |
34 | constructor TTempFileSteam.Create;
35 | var
36 | hFile: THandle;
37 | szFolder, szFile: Array[0..MAX_PATH] of Char;
38 | begin
39 | FillChar(szFolder, SizeOf(szFolder), 0);
40 | if GetTempPath(MAX_PATH - 1, szFolder) = 0 then
41 | FillChar(szFolder, SizeOf(szFolder), 0);
42 | FillChar(szFile, SizeOf(szFile), 0);
43 | hFile := INVALID_HANDLE_VALUE;
44 | if GetTempFileName(szFolder, 'CCL', 0, szFile) > 0 then
45 | hFile := Windows.CreateFile(szFile, GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
46 | FILE_ATTRIBUTE_TEMPORARY or FILE_FLAG_RANDOM_ACCESS or FILE_FLAG_DELETE_ON_CLOSE, 0);
47 | if hFile = INVALID_HANDLE_VALUE then
48 | raise Exception.Create('Unable to create temporary file.');
49 | FFilename := szFile;
50 | inherited Create(hFile)
51 | end;
52 |
53 | destructor TTempFileSteam.Destroy;
54 | begin
55 | CloseHandle(Handle);
56 | inherited Destroy
57 | end;
58 |
59 | end.
60 |
--------------------------------------------------------------------------------