├── 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 | <UseAppBundle Value="False"/> 17 | <ResourceType Value="res"/> 18 | </General> 19 | <BuildModes Count="1"> 20 | <Item1 Name="Default" Default="True"/> 21 | </BuildModes> 22 | <PublishOptions> 23 | <Version Value="2"/> 24 | </PublishOptions> 25 | <RunParams> 26 | <FormatVersion Value="2"/> 27 | <Modes Count="1"> 28 | <Mode0 Name="default"/> 29 | </Modes> 30 | </RunParams> 31 | <RequiredPackages Count="1"> 32 | <Item1> 33 | <PackageName Value="LCL"/> 34 | </Item1> 35 | </RequiredPackages> 36 | <Units Count="3"> 37 | <Unit0> 38 | <Filename Value="PKCS7ExtractorDemo.dpr"/> 39 | <IsPartOfProject Value="True"/> 40 | </Unit0> 41 | <Unit1> 42 | <Filename Value="MainFormUnit.pas"/> 43 | <IsPartOfProject Value="True"/> 44 | <ComponentName Value="FormMain"/> 45 | <HasResources Value="True"/> 46 | <ResourceBaseClass Value="Form"/> 47 | </Unit1> 48 | <Unit2> 49 | <Filename Value="..\Source\PKCS7Extractor.pas"/> 50 | <IsPartOfProject Value="True"/> 51 | </Unit2> 52 | </Units> 53 | </ProjectOptions> 54 | <CompilerOptions> 55 | <Version Value="11"/> 56 | <PathDelim Value="\"/> 57 | <SearchPaths> 58 | <IncludeFiles Value="..\Source"/> 59 | <OtherUnitFiles Value="..\Source"/> 60 | <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> 61 | </SearchPaths> 62 | <Parsing> 63 | <SyntaxOptions> 64 | <SyntaxMode Value="delphi"/> 65 | </SyntaxOptions> 66 | </Parsing> 67 | <Linking> 68 | <Options> 69 | <Win32> 70 | <GraphicApplication Value="True"/> 71 | </Win32> 72 | </Options> 73 | </Linking> 74 | <Other> 75 | <CustomOptions Value="-dBorland -dVer150 -dDelphi7 -dCompiler6_Up -dPUREPASCAL"/> 76 | </Other> 77 | </CompilerOptions> 78 | <Debugging> 79 | <Exceptions Count="3"> 80 | <Item1> 81 | <Name Value="EAbort"/> 82 | </Item1> 83 | <Item2> 84 | <Name Value="ECodetoolError"/> 85 | </Item2> 86 | <Item3> 87 | <Name Value="EFOpenError"/> 88 | </Item3> 89 | </Exceptions> 90 | </Debugging> 91 | </CONFIG> 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 | ![Libreria PKCS#7 Extractor per Delphi / Free Pascal / Lazarus](Documentation/pkcs7extractory_logo.png) 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 | ![Schermata della applicazione demo](Documentation/pkcs7extractory_demo.png) 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. <https://fsf.org/> 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 | ![PKCS#7 Extractor library for Delphi / Free Pascal / Lazarus](Documentation/pkcs7extractory_logo.png) 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 | ![Screenshot of demo application](Documentation/pkcs7extractory_demo.png) 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 <http://www.gnu.org/licenses/>. } 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 <http://www.gnu.org/licenses/>. } 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 | <?xml version="1.0" encoding="UTF-8"?> 2 | <CONFIG> 3 | <ProjectOptions> 4 | <Version Value="11"/> 5 | <PathDelim Value="\"/> 6 | <General> 7 | <Flags> 8 | <MainUnitHasCreateFormStatements Value="False"/> 9 | <MainUnitHasScaledStatement Value="False"/> 10 | <UseDefaultCompilerOptions Value="True"/> 11 | </Flags> 12 | <SessionStorage Value="InProjectDir"/> 13 | <MainUnit Value="0"/> 14 | <Title Value="My Application"/> 15 | <UseAppBundle Value="False"/> 16 | <ResourceType Value="res"/> 17 | </General> 18 | <BuildModes Count="1"> 19 | <Item1 Name="Default" Default="True"/> 20 | </BuildModes> 21 | <PublishOptions> 22 | <Version Value="2"/> 23 | <UseFileFilters Value="True"/> 24 | </PublishOptions> 25 | <RunParams> 26 | <FormatVersion Value="2"/> 27 | <Modes Count="0"/> 28 | </RunParams> 29 | <Units Count="1"> 30 | <Unit0> 31 | <Filename Value="PKCS7ExtractorTest.dpr"/> 32 | <IsPartOfProject Value="True"/> 33 | </Unit0> 34 | </Units> 35 | </ProjectOptions> 36 | <CompilerOptions> 37 | <Version Value="11"/> 38 | <PathDelim Value="\"/> 39 | <Target> 40 | <Filename Value="PKCS7ExtractorTest"/> 41 | </Target> 42 | <SearchPaths> 43 | <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> 44 | </SearchPaths> 45 | </CompilerOptions> 46 | <Debugging> 47 | <Exceptions Count="3"> 48 | <Item1> 49 | <Name Value="EAbort"/> 50 | </Item1> 51 | <Item2> 52 | <Name Value="ECodetoolError"/> 53 | </Item2> 54 | <Item3> 55 | <Name Value="EFOpenError"/> 56 | </Item3> 57 | </Exceptions> 58 | </Debugging> 59 | </CONFIG> 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 | --------------------------------------------------------------------------------