Skip to content

Commit 4b6c5c3

Browse files
committed
Sign the document as pdfa if the source doucment is pdfa. Add a test.
DEVSIX-1850
1 parent 4e00255 commit 4b6c5c3

File tree

6 files changed

+155
-7
lines changed

6 files changed

+155
-7
lines changed

sign/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
<artifactId>forms</artifactId>
2424
<version>${project.version}</version>
2525
</dependency>
26+
<dependency>
27+
<groupId>com.itextpdf</groupId>
28+
<artifactId>pdfa</artifactId>
29+
<version>${project.version}</version>
30+
</dependency>
2631
<dependency>
2732
<groupId>com.itextpdf</groupId>
2833
<artifactId>kernel</artifactId>

sign/src/main/java/com/itextpdf/signatures/PdfSigner.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ This file is part of the iText (R) project.
5757
import com.itextpdf.io.util.StreamUtil;
5858
import com.itextpdf.kernel.PdfException;
5959
import com.itextpdf.kernel.geom.Rectangle;
60+
import com.itextpdf.kernel.pdf.PdfAConformanceLevel;
6061
import com.itextpdf.kernel.pdf.PdfArray;
6162
import com.itextpdf.kernel.pdf.PdfDate;
6263
import com.itextpdf.kernel.pdf.PdfDeveloperExtension;
@@ -75,6 +76,7 @@ This file is part of the iText (R) project.
7576
import com.itextpdf.kernel.pdf.StampingProperties;
7677
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
7778
import com.itextpdf.kernel.pdf.annot.PdfWidgetAnnotation;
79+
import com.itextpdf.pdfa.PdfADocument;
7880
import org.bouncycastle.asn1.esf.SignaturePolicyIdentifier;
7981
import org.slf4j.Logger;
8082
import org.slf4j.LoggerFactory;
@@ -254,10 +256,10 @@ public PdfSigner(PdfReader reader, OutputStream outputStream, String path, boole
254256
}
255257
if (path == null) {
256258
temporaryOS = new ByteArrayOutputStream();
257-
document = new PdfDocument(reader, new PdfWriter(temporaryOS), properties);
259+
document = initDocument(reader, new PdfWriter(temporaryOS), properties);
258260
} else {
259261
this.tempFile = FileUtil.createTempFile(path);
260-
document = new PdfDocument(reader, new PdfWriter(FileUtil.getFileOutputStream(tempFile)), properties);
262+
document = initDocument(reader, new PdfWriter(FileUtil.getFileOutputStream(tempFile)), properties);
261263
}
262264

263265
originalOS = outputStream;
@@ -269,6 +271,15 @@ public PdfSigner(PdfReader reader, OutputStream outputStream, String path, boole
269271
closed = false;
270272
}
271273

274+
protected PdfDocument initDocument(PdfReader reader, PdfWriter writer, StampingProperties properties) {
275+
PdfAConformanceLevel conformanceLevel = reader.getPdfAConformanceLevel();
276+
if (null == conformanceLevel) {
277+
return new PdfDocument(reader, writer, properties);
278+
} else {
279+
return new PdfADocument(reader, writer, properties);
280+
}
281+
}
282+
272283
/**
273284
* Gets the signature date.
274285
*
@@ -490,7 +501,7 @@ public void setFieldLockDict(PdfSigFieldLock fieldLock) {
490501
*/
491502
public void signDetached(IExternalDigest externalDigest, IExternalSignature externalSignature, Certificate[] chain, Collection<ICrlClient> crlList, IOcspClient ocspClient,
492503
ITSAClient tsaClient, int estimatedSize, CryptoStandard sigtype) throws IOException, GeneralSecurityException {
493-
signDetached(externalDigest, externalSignature, chain, crlList, ocspClient, tsaClient, estimatedSize, sigtype, (SignaturePolicyIdentifier)null);
504+
signDetached(externalDigest, externalSignature, chain, crlList, ocspClient, tsaClient, estimatedSize, sigtype, (SignaturePolicyIdentifier) null);
494505
}
495506

496507
/**
@@ -507,7 +518,7 @@ public void signDetached(IExternalDigest externalDigest, IExternalSignature exte
507518
* @param externalDigest an implementation that provides the digest
508519
* @param estimatedSize the reserved size for the signature. It will be estimated if 0
509520
* @param sigtype Either Signature.CMS or Signature.CADES
510-
* @param signaturePolicy the signature policy (for EPES signatures)
521+
* @param signaturePolicy the signature policy (for EPES signatures)
511522
* @throws IOException
512523
* @throws GeneralSecurityException
513524
*/
@@ -530,7 +541,7 @@ public void signDetached(IExternalDigest externalDigest, IExternalSignature exte
530541
* @param externalDigest an implementation that provides the digest
531542
* @param estimatedSize the reserved size for the signature. It will be estimated if 0
532543
* @param sigtype Either Signature.CMS or Signature.CADES
533-
* @param signaturePolicy the signature policy (for EPES signatures)
544+
* @param signaturePolicy the signature policy (for EPES signatures)
534545
* @throws IOException
535546
* @throws GeneralSecurityException
536547
*/
@@ -956,7 +967,7 @@ protected void preClose(Map<PdfName, Integer> exclusionSizes) throws IOException
956967
os.writeLong(range[k]).write(' ');
957968
}
958969
os.write(']');
959-
System.arraycopy(bos.toByteArray(), 0, bout, (int) byteRangePosition, (int)bos.size());
970+
System.arraycopy(bos.toByteArray(), 0, bout, (int) byteRangePosition, (int) bos.size());
960971
} else {
961972
try {
962973
raf = FileUtil.getRandomAccessFile(tempFile);
@@ -1025,7 +1036,7 @@ protected void close(PdfDictionary update) throws IOException {
10251036
if (bous.size() > lit.getBytesCount())
10261037
throw new IllegalArgumentException("The key is too big");
10271038
if (tempFile == null) {
1028-
System.arraycopy(bous.toByteArray(), 0, bout, (int) lit.getPosition(), (int)bous.size());
1039+
System.arraycopy(bous.toByteArray(), 0, bout, (int) lit.getPosition(), (int) bous.size());
10291040
} else {
10301041
raf.seek(lit.getPosition());
10311042
raf.write(bous.toByteArray(), 0, (int) bous.size());
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package com.itextpdf.signatures.sign;
2+
3+
import com.itextpdf.kernel.font.PdfFont;
4+
import com.itextpdf.kernel.font.PdfFontFactory;
5+
import com.itextpdf.kernel.geom.Rectangle;
6+
import com.itextpdf.kernel.pdf.PdfReader;
7+
import com.itextpdf.kernel.utils.CompareTool;
8+
import com.itextpdf.signatures.BouncyCastleDigest;
9+
import com.itextpdf.signatures.DigestAlgorithms;
10+
import com.itextpdf.signatures.IExternalSignature;
11+
import com.itextpdf.signatures.PdfSignatureAppearance;
12+
import com.itextpdf.signatures.PdfSigner;
13+
import com.itextpdf.signatures.PrivateKeySignature;
14+
import com.itextpdf.signatures.testutils.Pkcs12FileHelper;
15+
import com.itextpdf.test.ExtendedITextTest;
16+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
17+
import org.junit.Assert;
18+
import org.junit.Before;
19+
import org.junit.BeforeClass;
20+
import org.junit.Rule;
21+
import org.junit.Test;
22+
import org.junit.rules.ExpectedException;
23+
24+
import java.io.FileOutputStream;
25+
import java.io.IOException;
26+
import java.security.GeneralSecurityException;
27+
import java.security.KeyStoreException;
28+
import java.security.NoSuchAlgorithmException;
29+
import java.security.PrivateKey;
30+
import java.security.Security;
31+
import java.security.UnrecoverableKeyException;
32+
import java.security.cert.Certificate;
33+
import java.security.cert.CertificateException;
34+
import java.util.Arrays;
35+
import java.util.HashMap;
36+
import java.util.List;
37+
import java.util.Map;
38+
39+
public class PdfASigningTest extends ExtendedITextTest {
40+
41+
public static final String sourceFolder = "./src/test/resources/com/itextpdf/signatures/sign/PdfASigningTest/";
42+
public static final String destinationFolder = "./target/test/com/itextpdf/signatures/sign/PdfASigningTest/";
43+
public static final String keystorePath = "./src/test/resources/com/itextpdf/signatures/certs/signCertRsa01.p12";
44+
public static final char[] password = "testpass".toCharArray();
45+
public static final String FONT = "./src/test/resources/com/itextpdf/signatures/font/FreeSans.ttf";
46+
47+
private Certificate[] chain;
48+
private PrivateKey pk;
49+
50+
@Rule
51+
public ExpectedException junitExpectedException = ExpectedException.none();
52+
53+
@BeforeClass
54+
public static void before() {
55+
Security.addProvider(new BouncyCastleProvider());
56+
createOrClearDestinationFolder(destinationFolder);
57+
}
58+
59+
@Before
60+
public void init() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException {
61+
pk = Pkcs12FileHelper.readFirstKey(keystorePath, password, password);
62+
chain = Pkcs12FileHelper.readFirstChain(keystorePath, password);
63+
}
64+
65+
@Test
66+
public void simpleSigningTest() throws GeneralSecurityException, IOException, InterruptedException {
67+
String src = sourceFolder + "simplePdfADocument.pdf";
68+
String fileName = "simpleSignature.pdf";
69+
String dest = destinationFolder + fileName;
70+
71+
int x = 36;
72+
int y = 548;
73+
int w = 200;
74+
int h = 100;
75+
Rectangle rect = new Rectangle(x, y, w, h);
76+
77+
String fieldName = "Signature1";
78+
sign(src, fieldName, dest, chain, pk,
79+
DigestAlgorithms.SHA256, PdfSigner.CryptoStandard.CADES, "Test 1", "TestCity", rect, false, false, PdfSigner.NOT_CERTIFIED, 12f);
80+
81+
Assert.assertNull(new CompareTool().compareVisually(dest, sourceFolder + "cmp_" + fileName, destinationFolder,
82+
"diff_", getTestMap(new Rectangle(67, 575, 155, 15))));
83+
}
84+
85+
86+
protected void sign(String src, String name, String dest,
87+
Certificate[] chain, PrivateKey pk,
88+
String digestAlgorithm, PdfSigner.CryptoStandard subfilter,
89+
String reason, String location, Rectangle rectangleForNewField, boolean setReuseAppearance, boolean isAppendMode) throws GeneralSecurityException, IOException {
90+
sign(src, name, dest, chain, pk, digestAlgorithm, subfilter, reason, location, rectangleForNewField, setReuseAppearance, isAppendMode, PdfSigner.NOT_CERTIFIED, null);
91+
}
92+
93+
protected void sign(String src, String name, String dest,
94+
Certificate[] chain, PrivateKey pk,
95+
String digestAlgorithm, PdfSigner.CryptoStandard subfilter,
96+
String reason, String location, Rectangle rectangleForNewField, boolean setReuseAppearance, boolean isAppendMode, int certificationLevel, Float fontSize)
97+
throws GeneralSecurityException, IOException {
98+
99+
PdfReader reader = new PdfReader(src);
100+
PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), isAppendMode);
101+
102+
signer.setCertificationLevel(certificationLevel);
103+
104+
PdfFont font = PdfFontFactory.createFont(FONT, "WinAnsi", true);
105+
106+
// Creating the appearance
107+
PdfSignatureAppearance appearance = signer.getSignatureAppearance()
108+
.setReason(reason)
109+
.setLocation(location)
110+
.setLayer2Font(font)
111+
.setReuseAppearance(setReuseAppearance);
112+
113+
if (rectangleForNewField != null) {
114+
appearance.setPageRect(rectangleForNewField);
115+
}
116+
if (fontSize != null) {
117+
appearance.setLayer2FontSize((float) fontSize);
118+
}
119+
120+
signer.setFieldName(name);
121+
// Creating the signature
122+
IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, BouncyCastleProvider.PROVIDER_NAME);
123+
signer.signDetached(new BouncyCastleDigest(), pks, chain, null, null, null, 0, subfilter);
124+
}
125+
126+
private static Map<Integer, List<Rectangle>> getTestMap(Rectangle ignoredArea) {
127+
Map<Integer, List<Rectangle>> result = new HashMap<Integer, List<Rectangle>>();
128+
result.put(1, Arrays.asList(ignoredArea));
129+
return result;
130+
}
131+
132+
}
Binary file not shown.

0 commit comments

Comments
 (0)