001/* 002 * Copyright (C) 2016 Hobrasoft s.r.o. 003 * 004 * This program is free software: you can redistribute it and/or modify 005 * it under the terms of the GNU Affero General Public License as published by 006 * the Free Software Foundation, either version 3 of the License, or 007 * (at your option) any later version. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU Affero General Public License for more details. 013 * 014 * You should have received a copy of the GNU Affero General Public License 015 * along with this program. If not, see <http://www.gnu.org/licenses/>. 016 */ 017package cz.hobrasoft.pdfmu; 018 019import com.tngtech.java.junit.dataprovider.DataProvider; 020import com.tngtech.java.junit.dataprovider.DataProviderRunner; 021import com.tngtech.java.junit.dataprovider.UseDataProvider; 022import cz.hobrasoft.pdfmu.jackson.CertificateResult; 023import cz.hobrasoft.pdfmu.jackson.Inspect; 024import cz.hobrasoft.pdfmu.jackson.Signature; 025import cz.hobrasoft.pdfmu.jackson.SignatureMetadata; 026import cz.hobrasoft.pdfmu.operation.OperationException; 027import cz.hobrasoft.pdfmu.operation.OperationInspect; 028import java.io.File; 029import java.io.IOException; 030import java.text.DateFormat; 031import java.text.ParseException; 032import java.text.SimpleDateFormat; 033import java.util.ArrayList; 034import java.util.Arrays; 035import java.util.Date; 036import java.util.LinkedHashMap; 037import java.util.List; 038import java.util.Locale; 039import java.util.Map; 040import java.util.concurrent.TimeUnit; 041import org.junit.Assert; 042import org.junit.Rule; 043import org.junit.Test; 044import org.junit.contrib.java.lang.system.Assertion; 045import org.junit.contrib.java.lang.system.EnvironmentVariables; 046import org.junit.runner.RunWith; 047 048/** 049 * @author Filip Bártek 050 */ 051@RunWith(DataProviderRunner.class) 052public class MainSignTest extends MainTest { 053 054 @Rule 055 public final EnvironmentVariables environmentVariables 056 = new EnvironmentVariables(); 057 058 @Test 059 public void testNoInput() throws IOException { 060 String[] args = new String[]{ 061 "sign" 062 }; 063 exit.expectSystemExitWithStatus(14); 064 Main.main(args); 065 assert false; 066 } 067 068 @Test 069 public void testNoKeystore() throws IOException { 070 File inFile = BLANK_12_PDF.getFile(folder); 071 final File outFile = newFile("out.pdf", false); 072 073 String[] args = new String[]{ 074 "sign", 075 inFile.getAbsolutePath(), 076 "--out", 077 outFile.getAbsolutePath() 078 }; 079 080 exit.expectSystemExitWithStatus(41); 081 exit.checkAssertionAfterwards(new Assertion() { 082 @Override 083 public void checkAssertion() { 084 Assert.assertFalse(outFile.exists()); 085 } 086 }); 087 Main.main(args); 088 assert false; 089 } 090 091 @DataProvider 092 public static Object[][] dataProviderEmpty() { 093 return new Object[][]{ 094 new Object[]{"empty.p12", null}, 095 new Object[]{"empty.pfx", null}, 096 new Object[]{"empty.jks", null}, 097 new Object[]{"empty.jceks", "jceks"} 098 }; 099 } 100 101 @Test 102 @UseDataProvider 103 public void testEmpty(String keystoreFileName, String keystoreType) 104 throws IOException { 105 File inFile = BLANK_12_PDF.getFile(folder); 106 File keystoreFile = new FileResource(keystoreFileName).getFile(folder); 107 final File outFile = newFile("out.pdf", false); 108 109 List<String> argsList = new ArrayList<>(); 110 argsList.add("sign"); 111 argsList.add(inFile.getAbsolutePath()); 112 argsList.add("--out"); 113 argsList.add(outFile.getAbsolutePath()); 114 argsList.add("--keystore"); 115 argsList.add(keystoreFile.getAbsolutePath()); 116 if (keystoreType != null) { 117 argsList.add("--keystore-type"); 118 argsList.add(keystoreType); 119 } 120 121 exit.expectSystemExitWithStatus(51); 122 exit.checkAssertionAfterwards(new Assertion() { 123 @Override 124 public void checkAssertion() { 125 Assert.assertFalse(outFile.exists()); 126 } 127 }); 128 Main.main(argsList.toArray(new String[]{})); 129 assert false; 130 } 131 132 private static void assertDateBetween(Date actual, Date begin, Date end) { 133 assert actual != null; 134 assert begin != null; 135 // The dates saved in PDF files only have second precision, 136 // while new Date() has millisecond precision. 137 // We compare with a 1 second delta to compensate 138 Assert.assertTrue(actual.getTime() - begin.getTime() 139 > TimeUnit.SECONDS.toMillis(-1)); 140 assert end != null; 141 Assert.assertFalse(actual.after(end)); 142 } 143 144 // Example content of the properties dictionary: 145 // Producer => iText® 5.5.6 ©2000-2015 iText Group NV (AGPL-version); modified using iText® 5.5.6 ©2000-2015 iText Group NV (AGPL-version) 146 // CreationDate => D:20160525204745+02'00' 147 // ModDate => D:20160601102240+02'00' 148 private static void assertDefaultPropertiesValid( 149 Map<String, String> properties, String expectedCreationDate, 150 Date modDateBegin, Date modDateEnd) throws ParseException { 151 Assert.assertNotNull(properties); 152 Assert.assertEquals(3, properties.size()); 153 Assert.assertTrue(properties.containsKey("Producer")); 154 String expectedProducer = "iText® 5.5.6 ©2000-2015 iText Group NV (AGPL-version); modified using iText® 5.5.6 ©2000-2015 iText Group NV (AGPL-version)"; 155 Assert.assertEquals(expectedProducer, properties.get("Producer")); 156 Assert.assertTrue(properties.containsKey("CreationDate")); 157 assert expectedCreationDate != null; 158 Assert.assertEquals(expectedCreationDate, properties.get("CreationDate")); 159 Assert.assertTrue(properties.containsKey("ModDate")); 160 DateFormat dateFormat = new SimpleDateFormat("'D:'yyyyMMddHHmmssX'''00'''", Locale.ROOT); 161 Date actualModDate = dateFormat.parse(properties.get("ModDate")); 162 assertDateBetween(actualModDate, modDateBegin, modDateEnd); 163 } 164 165 @Test 166 public void test1p12() throws IOException { 167 final PdfFileResource inFileResource = BLANK_12_PDF; 168 File inFile = inFileResource.getFile(folder); 169 File keystoreFile = new FileResource("1.p12").getFile(folder); 170 final File outFile = newFile("out.pdf", false); 171 172 List<String> argsList = new ArrayList<>(); 173 argsList.add("sign"); 174 argsList.add(inFile.getAbsolutePath()); 175 argsList.add("--out"); 176 argsList.add(outFile.getAbsolutePath()); 177 argsList.add("--keystore"); 178 argsList.add(keystoreFile.getAbsolutePath()); 179 180 final Date modDateBegin = new Date(); 181 182 exit.expectSystemExitWithStatus(0); 183 exit.checkAssertionAfterwards(new Assertion() { 184 @Override 185 public void checkAssertion() throws OperationException, IOException, ParseException { 186 Date modDateEnd = new Date(); 187 Assert.assertTrue(outFile.exists()); 188 Inspect inspect = OperationInspect.getInstance().execute(outFile); 189 assert inspect != null; 190 assert inFileResource.version != null; 191 Assert.assertEquals(inFileResource.version, inspect.version); 192 assertDefaultPropertiesValid(inspect.properties, 193 inFileResource.creationDate, modDateBegin, modDateEnd); 194 Assert.assertNotNull(inspect.signatures); 195 Assert.assertEquals(1, inspect.signatures.nRevisions.intValue()); 196 Assert.assertNotNull(inspect.signatures.signatures); 197 Assert.assertEquals(1, inspect.signatures.signatures.size()); 198 { 199 Signature signature = inspect.signatures.signatures.get(0); 200 Assert.assertNotNull(signature); 201 Assert.assertEquals("Signature1", signature.id); 202 Assert.assertTrue(signature.coversWholeDocument); 203 Assert.assertEquals(1, signature.revision.intValue()); 204 { 205 SignatureMetadata metadata = signature.metadata; 206 Assert.assertNotNull(metadata); 207 Assert.assertNull(metadata.name); 208 Assert.assertEquals("", metadata.reason); 209 Assert.assertEquals("", metadata.location); 210 Assert.assertNotNull(metadata.date); 211 DateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ROOT); 212 // Example: "Wed Jun 01 11:22:38 CEST 2016" 213 Date actualDate = dateFormat.parse(metadata.date); 214 assertDateBetween(actualDate, modDateBegin, modDateEnd); 215 } 216 Assert.assertNotNull(signature.certificates); 217 Assert.assertEquals(1, signature.certificates.size()); 218 { 219 CertificateResult certificate = signature.certificates.get(0); 220 Assert.assertNotNull(certificate); 221 Assert.assertEquals("X.509", certificate.type); 222 Assert.assertTrue(certificate.selfSigned); 223 Map<String, List<String>> dn = new LinkedHashMap<>(); 224 dn.put("CN", Arrays.asList(new String[]{"CN1"})); 225 dn.put("E", Arrays.asList(new String[]{"E1"})); 226 dn.put("OU", Arrays.asList(new String[]{"OU1"})); 227 dn.put("O", Arrays.asList(new String[]{"O1"})); 228 dn.put("L", Arrays.asList(new String[]{"L1"})); 229 dn.put("ST", Arrays.asList(new String[]{"ST1"})); 230 dn.put("C", Arrays.asList(new String[]{"C1"})); 231 Assert.assertEquals(dn, certificate.subject); 232 Assert.assertEquals(dn, certificate.issuer); 233 } 234 } 235 } 236 }); 237 Main.main(argsList.toArray(new String[]{})); 238 assert false; 239 } 240 241 @Test 242 public void testKeyIncorrect() throws IOException { 243 final PdfFileResource inFileResource = BLANK_12_PDF; 244 File inFile = inFileResource.getFile(folder); 245 File keystoreFile = new FileResource("1.p12").getFile(folder); 246 final File outFile = newFile("out.pdf", false); 247 248 List<String> argsList = new ArrayList<>(); 249 argsList.add("sign"); 250 argsList.add(inFile.getAbsolutePath()); 251 argsList.add("--out"); 252 argsList.add(outFile.getAbsolutePath()); 253 argsList.add("--keystore"); 254 argsList.add(keystoreFile.getAbsolutePath()); 255 argsList.add("--key-alias"); 256 argsList.add("incorrect-alias"); 257 258 exit.expectSystemExitWithStatus(52); 259 exit.checkAssertionAfterwards(new Assertion() { 260 @Override 261 public void checkAssertion() { 262 Assert.assertFalse(outFile.exists()); 263 } 264 }); 265 Main.main(argsList.toArray(new String[]{})); 266 assert false; 267 } 268 269 @Test 270 public void testKeyCorrect() throws IOException { 271 final PdfFileResource inFileResource = BLANK_12_PDF; 272 File inFile = inFileResource.getFile(folder); 273 File keystoreFile = new FileResource("1.p12").getFile(folder); 274 final File outFile = newFile("out.pdf", false); 275 276 List<String> argsList = new ArrayList<>(); 277 argsList.add("sign"); 278 argsList.add(inFile.getAbsolutePath()); 279 argsList.add("--out"); 280 argsList.add(outFile.getAbsolutePath()); 281 argsList.add("--keystore"); 282 argsList.add(keystoreFile.getAbsolutePath()); 283 argsList.add("--key-alias"); 284 argsList.add("cn1"); 285 286 exit.expectSystemExitWithStatus(0); 287 exit.checkAssertionAfterwards(new Assertion() { 288 @Override 289 public void checkAssertion() { 290 Assert.assertTrue(outFile.exists()); 291 } 292 }); 293 Main.main(argsList.toArray(new String[]{})); 294 assert false; 295 } 296 297 @Test 298 public void testPasswordMissing() throws IOException { 299 final PdfFileResource inFileResource = BLANK_12_PDF; 300 File inFile = inFileResource.getFile(folder); 301 File keystoreFile = new FileResource("1-changeit.p12").getFile(folder); 302 final File outFile = newFile("out.pdf", false); 303 304 List<String> argsList = new ArrayList<>(); 305 argsList.add("sign"); 306 argsList.add(inFile.getAbsolutePath()); 307 argsList.add("--out"); 308 argsList.add(outFile.getAbsolutePath()); 309 argsList.add("--keystore"); 310 argsList.add(keystoreFile.getAbsolutePath()); 311 argsList.add("--keystore-type"); 312 argsList.add("pkcs12"); 313 314 exit.expectSystemExitWithStatus(43); 315 exit.checkAssertionAfterwards(new Assertion() { 316 @Override 317 public void checkAssertion() { 318 Assert.assertFalse(outFile.exists()); 319 } 320 }); 321 Main.main(argsList.toArray(new String[]{})); 322 assert false; 323 } 324 325 @Test 326 public void testPasswordIncorrect() throws IOException { 327 final PdfFileResource inFileResource = BLANK_12_PDF; 328 File inFile = inFileResource.getFile(folder); 329 File keystoreFile = new FileResource("1-changeit.p12").getFile(folder); 330 final File outFile = newFile("out.pdf", false); 331 332 List<String> argsList = new ArrayList<>(); 333 argsList.add("sign"); 334 argsList.add(inFile.getAbsolutePath()); 335 argsList.add("--out"); 336 argsList.add(outFile.getAbsolutePath()); 337 argsList.add("--keystore"); 338 argsList.add(keystoreFile.getAbsolutePath()); 339 argsList.add("--keystore-type"); 340 argsList.add("pkcs12"); 341 argsList.add("--keystore-password"); 342 argsList.add("incorrect-password"); 343 344 exit.expectSystemExitWithStatus(43); 345 exit.checkAssertionAfterwards(new Assertion() { 346 @Override 347 public void checkAssertion() { 348 Assert.assertFalse(outFile.exists()); 349 } 350 }); 351 Main.main(argsList.toArray(new String[]{})); 352 assert false; 353 } 354 355 @Test 356 public void testPasswordCmdlineSuccess() throws IOException { 357 final PdfFileResource inFileResource = BLANK_12_PDF; 358 File inFile = inFileResource.getFile(folder); 359 File keystoreFile = new FileResource("1-changeit.p12").getFile(folder); 360 final File outFile = newFile("out.pdf", false); 361 362 List<String> argsList = new ArrayList<>(); 363 argsList.add("sign"); 364 argsList.add(inFile.getAbsolutePath()); 365 argsList.add("--out"); 366 argsList.add(outFile.getAbsolutePath()); 367 argsList.add("--keystore"); 368 argsList.add(keystoreFile.getAbsolutePath()); 369 argsList.add("--keystore-type"); 370 argsList.add("pkcs12"); 371 argsList.add("--keystore-password"); 372 argsList.add("changeit"); 373 374 exit.expectSystemExitWithStatus(0); 375 exit.checkAssertionAfterwards(new Assertion() { 376 @Override 377 public void checkAssertion() { 378 Assert.assertTrue(outFile.exists()); 379 } 380 }); 381 Main.main(argsList.toArray(new String[]{})); 382 assert false; 383 } 384 385 @Test 386 public void testPasswordEnvvarDefaultSuccess() throws IOException { 387 final PdfFileResource inFileResource = BLANK_12_PDF; 388 File inFile = inFileResource.getFile(folder); 389 File keystoreFile = new FileResource("1-changeit.p12").getFile(folder); 390 final File outFile = newFile("out.pdf", false); 391 392 List<String> argsList = new ArrayList<>(); 393 argsList.add("sign"); 394 argsList.add(inFile.getAbsolutePath()); 395 argsList.add("--out"); 396 argsList.add(outFile.getAbsolutePath()); 397 argsList.add("--keystore"); 398 argsList.add(keystoreFile.getAbsolutePath()); 399 argsList.add("--keystore-type"); 400 argsList.add("pkcs12"); 401 402 environmentVariables.set("PDFMU_STOREPASS", "changeit"); 403 404 exit.expectSystemExitWithStatus(0); 405 exit.checkAssertionAfterwards(new Assertion() { 406 @Override 407 public void checkAssertion() { 408 Assert.assertTrue(outFile.exists()); 409 } 410 }); 411 Main.main(argsList.toArray(new String[]{})); 412 assert false; 413 } 414 415 @Test 416 public void testPasswordEnvvarCustomSuccess() throws IOException { 417 final PdfFileResource inFileResource = BLANK_12_PDF; 418 File inFile = inFileResource.getFile(folder); 419 File keystoreFile = new FileResource("1-changeit.p12").getFile(folder); 420 final File outFile = newFile("out.pdf", false); 421 422 List<String> argsList = new ArrayList<>(); 423 argsList.add("sign"); 424 argsList.add(inFile.getAbsolutePath()); 425 argsList.add("--out"); 426 argsList.add(outFile.getAbsolutePath()); 427 argsList.add("--keystore"); 428 argsList.add(keystoreFile.getAbsolutePath()); 429 argsList.add("--keystore-type"); 430 argsList.add("pkcs12"); 431 argsList.add("--keystore-password-envvar"); 432 argsList.add("PDFMU_STOREPASS_CUSTOM_ENVVAR"); 433 434 environmentVariables.set("PDFMU_STOREPASS_CUSTOM_ENVVAR", "changeit"); 435 436 exit.expectSystemExitWithStatus(0); 437 exit.checkAssertionAfterwards(new Assertion() { 438 @Override 439 public void checkAssertion() { 440 Assert.assertTrue(outFile.exists()); 441 } 442 }); 443 Main.main(argsList.toArray(new String[]{})); 444 assert false; 445 } 446}