|
| 1 | +/* |
| 2 | + This file is part of the iText (R) project. |
| 3 | + Copyright (c) 1998-2023 Apryse Group NV |
| 4 | + Authors: Apryse Software. |
| 5 | +
|
| 6 | + This program is offered under a commercial and under the AGPL license. |
| 7 | + For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below. |
| 8 | +
|
| 9 | + AGPL licensing: |
| 10 | + This program is free software: you can redistribute it and/or modify |
| 11 | + it under the terms of the GNU Affero General Public License as published by |
| 12 | + the Free Software Foundation, either version 3 of the License, or |
| 13 | + (at your option) any later version. |
| 14 | +
|
| 15 | + This program is distributed in the hope that it will be useful, |
| 16 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 17 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 18 | + GNU Affero General Public License for more details. |
| 19 | +
|
| 20 | + You should have received a copy of the GNU Affero General Public License |
| 21 | + along with this program. If not, see <https://www.gnu.org/licenses/>. |
| 22 | + */ |
| 23 | +package com.itextpdf.signatures.sign; |
| 24 | + |
| 25 | +import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator; |
| 26 | +import com.itextpdf.commons.bouncycastle.IBouncyCastleFactory; |
| 27 | +import com.itextpdf.commons.bouncycastle.operator.AbstractOperatorCreationException; |
| 28 | +import com.itextpdf.commons.bouncycastle.pkcs.AbstractPKCSException; |
| 29 | +import com.itextpdf.commons.utils.FileUtil; |
| 30 | +import com.itextpdf.io.logs.IoLogMessageConstant; |
| 31 | +import com.itextpdf.kernel.geom.Rectangle; |
| 32 | +import com.itextpdf.kernel.pdf.PdfReader; |
| 33 | +import com.itextpdf.kernel.pdf.StampingProperties; |
| 34 | +import com.itextpdf.signatures.DigestAlgorithms; |
| 35 | +import com.itextpdf.signatures.IExternalSignature; |
| 36 | +import com.itextpdf.signatures.PdfPadesSigner; |
| 37 | +import com.itextpdf.signatures.PdfSigner; |
| 38 | +import com.itextpdf.signatures.PrivateKeySignature; |
| 39 | +import com.itextpdf.signatures.testutils.PemFileHelper; |
| 40 | +import com.itextpdf.signatures.testutils.SignaturesCompareTool; |
| 41 | +import com.itextpdf.signatures.testutils.TimeTestUtil; |
| 42 | +import com.itextpdf.signatures.testutils.builder.TestCrlBuilder; |
| 43 | +import com.itextpdf.signatures.testutils.builder.TestOcspResponseBuilder; |
| 44 | +import com.itextpdf.signatures.testutils.client.AdvancedTestCrlClient; |
| 45 | +import com.itextpdf.signatures.testutils.client.AdvancedTestOcspClient; |
| 46 | +import com.itextpdf.signatures.testutils.client.TestTsaClient; |
| 47 | +import com.itextpdf.test.ExtendedITextTest; |
| 48 | +import com.itextpdf.test.annotations.LogMessage; |
| 49 | +import com.itextpdf.test.annotations.LogMessages; |
| 50 | +import com.itextpdf.test.annotations.type.BouncyCastleIntegrationTest; |
| 51 | + |
| 52 | +import java.io.IOException; |
| 53 | +import java.security.GeneralSecurityException; |
| 54 | +import java.security.PrivateKey; |
| 55 | +import java.security.Security; |
| 56 | +import java.security.cert.Certificate; |
| 57 | +import java.security.cert.X509Certificate; |
| 58 | +import java.util.ArrayList; |
| 59 | +import java.util.Arrays; |
| 60 | +import java.util.List; |
| 61 | +import org.junit.Assert; |
| 62 | +import org.junit.BeforeClass; |
| 63 | +import org.junit.Test; |
| 64 | +import org.junit.experimental.categories.Category; |
| 65 | +import org.junit.runner.RunWith; |
| 66 | +import org.junit.runners.Parameterized; |
| 67 | + |
| 68 | +@RunWith(Parameterized.class) |
| 69 | +@Category(BouncyCastleIntegrationTest.class) |
| 70 | +public class PdfPadesAdvancedTest extends ExtendedITextTest { |
| 71 | + private static final IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.getFactory(); |
| 72 | + private static final String CERTS_SRC = "./src/test/resources/com/itextpdf/signatures/sign/PdfPadesAdvancedTest/certs/"; |
| 73 | + private static final String SOURCE_FOLDER = "./src/test/resources/com/itextpdf/signatures/sign/PdfPadesAdvancedTest/"; |
| 74 | + private static final String DESTINATION_FOLDER = "./target/test/com/itextpdf/signatures/sign/PdfPadesAdvancedTest/"; |
| 75 | + |
| 76 | + private static final char[] PASSWORD = "testpassphrase".toCharArray(); |
| 77 | + |
| 78 | + private final String signingCertName; |
| 79 | + private final String rootCertName; |
| 80 | + private final Boolean isOcspRevoked; |
| 81 | + private final String cmpFilePostfix; |
| 82 | + |
| 83 | + @BeforeClass |
| 84 | + public static void before() { |
| 85 | + Security.addProvider(FACTORY.getProvider()); |
| 86 | + createOrClearDestinationFolder(DESTINATION_FOLDER); |
| 87 | + } |
| 88 | + |
| 89 | + public PdfPadesAdvancedTest(Object signingCertName, Object rootCertName, Object isOcspRevoked, Object cmpFilePostfix) { |
| 90 | + this.signingCertName = (String) signingCertName; |
| 91 | + this.rootCertName = (String) rootCertName; |
| 92 | + this.isOcspRevoked = (Boolean) isOcspRevoked; |
| 93 | + this.cmpFilePostfix = (String) cmpFilePostfix; |
| 94 | + } |
| 95 | + |
| 96 | + @Parameterized.Parameters(name = "{3}: signing cert: {0}; root cert: {1}; revoked: {2}") |
| 97 | + public static Iterable<Object[]> createParameters() { |
| 98 | + List<Object[]> parameters = new ArrayList<>(); |
| 99 | + parameters.addAll(createParametersUsingRootName("rootCertNoCrlNoOcsp")); |
| 100 | + parameters.addAll(createParametersUsingRootName("rootCertCrlOcsp")); |
| 101 | + parameters.addAll(createParametersUsingRootName("rootCertCrlNoOcsp")); |
| 102 | + parameters.addAll(createParametersUsingRootName("rootCertOcspNoCrl")); |
| 103 | + return parameters; |
| 104 | + } |
| 105 | + |
| 106 | + private static List<Object[]> createParametersUsingRootName(String rootCertName) { |
| 107 | + return Arrays.asList( |
| 108 | + new Object[] {"signCertCrlOcsp.pem", rootCertName + ".pem", false, "_signCertCrlOcsp_" + rootCertName}, |
| 109 | + new Object[] {"signCertCrlOcsp.pem", rootCertName + ".pem", true, "_signCertCrlOcsp_" + rootCertName + "_revoked"}, |
| 110 | + new Object[] {"signCertOcspNoCrl.pem", rootCertName + ".pem", false, "_signCertOcspNoCrl_" + rootCertName}, |
| 111 | + new Object[] {"signCertOcspNoCrl.pem", rootCertName + ".pem", true, "_signCertOcspNoCrl_" + rootCertName + "_revoked"}, |
| 112 | + new Object[] {"signCertNoOcspNoCrl.pem", rootCertName + ".pem", false, "_signCertNoOcspNoCrl_" + rootCertName}, |
| 113 | + new Object[] {"signCertCrlNoOcsp.pem", rootCertName + ".pem", false, "_signCertCrlNoOcsp_" + rootCertName} |
| 114 | + ); |
| 115 | + } |
| 116 | + |
| 117 | + @Test |
| 118 | + @LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.OCSP_STATUS_IS_REVOKED), ignore = true) |
| 119 | + public void signWithAdvancedClientsTest() |
| 120 | + throws IOException, GeneralSecurityException, AbstractOperatorCreationException, AbstractPKCSException { |
| 121 | + String fileName = "signedWith" + cmpFilePostfix + ".pdf"; |
| 122 | + String outFileName = DESTINATION_FOLDER + fileName; |
| 123 | + String cmpFileName = SOURCE_FOLDER + "cmp_" + fileName; |
| 124 | + String srcFileName = SOURCE_FOLDER + "helloWorldDoc.pdf"; |
| 125 | + String signCertFileName = CERTS_SRC + signingCertName; |
| 126 | + String rootCertFileName = CERTS_SRC + rootCertName; |
| 127 | + String tsaCertFileName = CERTS_SRC + "tsCertRsa.pem"; |
| 128 | + |
| 129 | + Certificate signRsaCert = PemFileHelper.readFirstChain(signCertFileName)[0]; |
| 130 | + Certificate rootCert = PemFileHelper.readFirstChain(rootCertFileName)[0]; |
| 131 | + Certificate[] signRsaChain = new Certificate[2]; |
| 132 | + signRsaChain[0] = signRsaCert; |
| 133 | + signRsaChain[1] = rootCert; |
| 134 | + |
| 135 | + PrivateKey signRsaPrivateKey = PemFileHelper.readFirstKey(signCertFileName, PASSWORD); |
| 136 | + PrivateKey rootPrivateKey = PemFileHelper.readFirstKey(rootCertFileName, PASSWORD); |
| 137 | + Certificate[] tsaChain = PemFileHelper.readFirstChain(tsaCertFileName); |
| 138 | + PrivateKey tsaPrivateKey = PemFileHelper.readFirstKey(tsaCertFileName, PASSWORD); |
| 139 | + |
| 140 | + TestTsaClient testTsa = new TestTsaClient(Arrays.asList(tsaChain), tsaPrivateKey); |
| 141 | + |
| 142 | + AdvancedTestOcspClient testOcspClient = new AdvancedTestOcspClient(null); |
| 143 | + TestOcspResponseBuilder ocspBuilderMainCert = new TestOcspResponseBuilder((X509Certificate) signRsaChain[1], rootPrivateKey); |
| 144 | + if ((boolean) isOcspRevoked) { |
| 145 | + ocspBuilderMainCert.setCertificateStatus(FACTORY.createRevokedStatus(TimeTestUtil.TEST_DATE_TIME, |
| 146 | + FACTORY.createCRLReason().getKeyCompromise())); |
| 147 | + } |
| 148 | + TestOcspResponseBuilder ocspBuilderRootCert = new TestOcspResponseBuilder((X509Certificate) signRsaChain[1], rootPrivateKey); |
| 149 | + testOcspClient.addBuilderForCertIssuer((X509Certificate) signRsaChain[0], ocspBuilderMainCert); |
| 150 | + testOcspClient.addBuilderForCertIssuer((X509Certificate) signRsaChain[1], ocspBuilderRootCert); |
| 151 | + |
| 152 | + AdvancedTestCrlClient testCrlClient = new AdvancedTestCrlClient(); |
| 153 | + TestCrlBuilder crlBuilderMainCert = new TestCrlBuilder((X509Certificate) signRsaChain[1], rootPrivateKey); |
| 154 | + crlBuilderMainCert.addCrlEntry((X509Certificate) signRsaChain[0], FACTORY.createCRLReason().getKeyCompromise()); |
| 155 | + crlBuilderMainCert.addCrlEntry((X509Certificate) signRsaChain[1], FACTORY.createCRLReason().getKeyCompromise()); |
| 156 | + |
| 157 | + TestCrlBuilder crlBuilderRootCert = new TestCrlBuilder((X509Certificate) signRsaChain[1], rootPrivateKey); |
| 158 | + crlBuilderRootCert.addCrlEntry((X509Certificate) signRsaChain[1], FACTORY.createCRLReason().getKeyCompromise()); |
| 159 | + testCrlClient.addBuilderForCertIssuer((X509Certificate) signRsaChain[0], crlBuilderMainCert); |
| 160 | + testCrlClient.addBuilderForCertIssuer((X509Certificate) signRsaChain[1], crlBuilderRootCert); |
| 161 | + |
| 162 | + PdfSigner signer = createPdfSigner(srcFileName, outFileName); |
| 163 | + |
| 164 | + PdfPadesSigner padesSigner = new PdfPadesSigner(); |
| 165 | + padesSigner.setOcspClient(testOcspClient); |
| 166 | + padesSigner.setCrlClient(testCrlClient); |
| 167 | + |
| 168 | + IExternalSignature pks = |
| 169 | + new PrivateKeySignature(signRsaPrivateKey, DigestAlgorithms.SHA256, FACTORY.getProviderName()); |
| 170 | + padesSigner.signWithBaselineLTAProfile(signer, signRsaChain, pks, testTsa); |
| 171 | + |
| 172 | + PadesSigTest.basicCheckSignedDoc(outFileName, "Signature1"); |
| 173 | + |
| 174 | + Assert.assertNull(SignaturesCompareTool.compareSignatures(outFileName, cmpFileName)); |
| 175 | + } |
| 176 | + |
| 177 | + private PdfSigner createPdfSigner(String srcFileName, String outFileName) throws IOException { |
| 178 | + PdfSigner signer = new PdfSigner(new PdfReader(srcFileName), FileUtil.getFileOutputStream(outFileName), |
| 179 | + new StampingProperties()); |
| 180 | + signer.setFieldName("Signature1"); |
| 181 | + signer.getSignatureAppearance() |
| 182 | + .setPageRect(new Rectangle(50, 650, 200, 100)) |
| 183 | + .setReason("Test") |
| 184 | + .setLocation("TestCity") |
| 185 | + .setLayer2Text("Approval test signature.\nCreated by iText."); |
| 186 | + return signer; |
| 187 | + } |
| 188 | +} |
0 commit comments