I'm sign document, using Itext. I have that method:
public static void sign(String src, String dest, Certificate[] chain,PrivateKey pk, String digestAlgorithm, String provider,CryptoStandard subfilter, TSAClient tsa )
{
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
// Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(new Rectangle(10, 20, 100, 200), 1, "sig");
// Creating the signature
ExternalDigest digest = new BouncyCastleDigest();
ExternalSignature signature = new PrivateKeySignature(pk,
digestAlgorithm, provider);
MakeSignature.signDetached(appearance, digest, signature, chain, null,null, tsa, 0, subfilter);
// ALREADY SIGNED. ADD LTB NOW.
CrlClient crlClient = new CrlClientOnline("http://crl.mycrl.com/mycrl.crl");
OcspClient ocspClient=new OcspClientBouncyCastle();
addLtv(DEST, DEST2, ocspClient, crlClient, tsa);
}
and I sign:
sign(SRC, String.format(DEST, 1), chain, pk, DigestAlgorithms.SHA256, provider.getName(), CryptoStandard.CMS, "For Testing", " location", tsa);
everything works. PDF is signed well.
but then, I can't add ltv. I use code from itext documentation:
public static void addLtv(String src, String dest, OcspClient ocsp, CrlClient crl,
TSAClient tsa) throws IOException, DocumentException,
GeneralSecurityException {
PdfReader r = new PdfReader(src);
FileOutputStream fos = new FileOutputStream(dest);
PdfStamper stp = PdfStamper.createSignature(r, fos, '\0', null, true);
LtvVerification v = stp.getLtvVerification();
AcroFields fields = stp.getAcroFields();
List<String> names = fields.getSignatureNames();
String sigName = names.get(names.size() - 1);
PdfPKCS7 pkcs7 = fields.verifySignature(sigName);
if (pkcs7.isTsp()) {
v.addVerification(sigName, ocsp, crl,
LtvVerification.CertificateOption.SIGNING_CERTIFICATE,
LtvVerification.Level.OCSP_CRL,
LtvVerification.CertificateInclusion.NO);
}
else {
for (String name : names) {
v.addVerification(name, ocsp, crl,
LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL,
LtvVerification.CertificateInclusion.NO);
}
}
PdfSignatureAppearance sap = stp.getSignatureAppearance();
LtvTimestamp.timestamp(sap, tsa, null);
}
EDITED:
everything works, but at line
LtvTimestamp.timestamp(sap, tsa, null);
I have that error:
Exception in thread "main" java.io.IOException: Not enough space
that's my pdf:
https://www.dropbox.com/s/o05rw6ubiuslm4j/DOC_SIGNED.pdf
Exception in thread "main" java.io.IOException: Not enough space
at com.itextpdf.text.pdf.security.LtvTimestamp.timestamp(LtvTimestamp.java:103)
at ge.digital.signature.DocumentSigner.DigitalSignature.addLtv(MySignature.java:132)
at ge.digital.signature.DocumentSigner.DigitalSignature.main(MySignature.java:163)
That IOException occurs when the space reserved in the PDF for the integration of the time stamp does not suffice. Thus, you have to change the method getTokenSizeEstimate of the TSAClient instance tsa you call your sign method with to return a larger estimate of the time stamp size.
In case of the TSAClientBouncyCastle implementation of TSAClient, e.g., you can make it return arbitrary estimates instead of the default 4096 if you use the constructor with four arguments:
public TSAClientBouncyCastle(String url, String username, String password, int tokSzEstimate)
Some background: When integrating a signature or a document time stamp into a PDF, you first prepare the PDF with a gap of a given size, then calculate the hash of everything but that gap, then sign or time stamp that hash, and finally integrate the generated signature or time stamp into that gap, e.g.
(That /ByteRange entry is part of the signed content. Thus, one cannot enlarge the gap afterwards.)
Therefore, you have to use some estimate of the size of the generated signature or time stamp before its generation.
In case of document time stamps, this estimate is provided by the TSAClient method getTokenSizeEstimate.
PS: For still more details cf. this answer, the Adobe document Digital Signatures in a PDF (from which I copied the figure above), and the PDF specification ISO 32000-1:2008 made available here by Adobe.
If you are not using TSAClientBouncyCastle and you created your own TSAClient you must set the preferred size of signature, creating a org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions class, then set something like signatureOptions.setPreferredSignatureSize(8192*2)
Related
I'm trying to sign PDF documents with the library iText 5.5.5.
I have two certificates to sign documents, with one certificate all works perfect and you have a valid signed document. But the problem is with the second certificate, because you can sign the document but if I open the document with Adobe Reader the signature is invalid.
I'm using the same code for both certificates but I don't know why doesn´t works with one certificate. Here is the code that I use to sign the documents with iText 5.5.5
String pkcs11Config = "name=NAME" + "\n" + "library=PATH";
ByteArrayInputStream configStream = new ByteArrayInputStream(pkcs11Config.getBytes());
Provider pkcs11Provider = new sun.security.pkcs11.SunPKCS11(configStream);
Security.addProvider(pkcs11Provider);
KeyStore ks = KeyStore.getInstance("PKCS11");
ks.load(null, password);
PdfReader reader = new PdfReader(pdfInput);
FileOutputStream os = new FileOutputStream(pdfOutputTemp);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', null, true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
...
PrivateKey key = (PrivateKey) ks.getKey(alias, pass);
ExternalSignature es = new PrivateKeySignature(key, DigestAlgorithms.SHA256, null);
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, es, chain, crlList, null, tsc, 0, CryptoStandard.CADES);
I also attach two signed documents, one signed with the certificate that has no problems and the other with the certificate that invalidates the signature.
Invalid Document
Valid Document
I appreciate the help you can give me.Thank you
For a project I have to digitally sign PDFs on an additionally created page by multiple people in a workflow. To realize this we're using the iText 7 libraries with following code, based on Bruno Lowagie's examples:
public static void main(String[] args) throws IOException, GeneralSecurityException, XMPException {
String path = "F:/Java/keystores/testPdfSign";
char[] pass = "test".toCharArray();
KeyStore ks = KeyStore.getInstance("pkcs12", "SunJSSE");
ks.load(new FileInputStream(path), pass);
String alias = "";
Enumeration<String> aliases = ks.aliases();
while (alias.equals("tester")==false && aliases.hasMoreElements())
{
alias = aliases.nextElement();
}
PrivateKey pk = (PrivateKey) ks.getKey(alias, pass);
Certificate[] chain = ks.getCertificateChain(alias);
PDFSign app = new PDFSign();
app.sign(SRC, DEST, chain, pk, DigestAlgorithms.SHA1, "SunJSSE", PdfSigner.CryptoStandard.CMS, "Test", "Test", null, null, null, 0);
}
public void sign(String src, String dest,
Certificate[] chain, PrivateKey pk,
String digestAlgorithm, String provider, PdfSigner.CryptoStandard subfilter,
String reason, String location,
Collection<ICrlClient> crlList,
IOcspClient ocspClient,
ITSAClient tsaClient,
int estimatedSize)
throws GeneralSecurityException, IOException, XMPException {
// Creating the reader and the signer
PdfDocument document = new PdfDocument(new PdfReader(SRC), new PdfWriter(DEST+"_temp"));
if (initial == true)
{
document.addNewPage();
}
int pageCount = document.getNumberOfPages();
document.close();
PdfSigner signer = new PdfSigner(new PdfReader(DEST+"_temp"), new FileOutputStream(DEST), true);
// Creating the appearance
if (initial == true)
{
signer.setCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS);
}
PdfSignatureAppearance appearance = signer.getSignatureAppearance()
.setReason(reason)
.setLocation(location)
.setReuseAppearance(false);
Rectangle rect = new Rectangle(10, 400, 100, 100);
appearance
.setPageRect(rect)
.setPageNumber(pageCount);
appearance.setRenderingMode(RenderingMode.NAME_AND_DESCRIPTION);
signer.setFieldName(signer.getNewSigFieldName());
// Creating the signature
IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
ProviderDigest digest = new ProviderDigest(provider);
signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
}
This results in an invalid signature in the new signed version of the PDF, as Adobe Acrobat Reader says it has been edited after signing. Surprisingly when I open the file in Foxit Reader it says it hasn't been modified and is valid.
Also what I tried was to leave out the first step of adding a new page and just sign on the last page of the original document, then the signature is valid in Adobe Reader, but no solution for my situation, as an extra page is a must have.
The other thing I tried was not setting the certificationLevel to CERTIFIED_FORM_FILLING_AND_ANNOTATIONS, but just leaving it at the default NOT_CERTIFIED, this way I also had a valid signature on a new page, but this is not a solution either, because it won't let me add any additional signatures later on.
Does someone have an idea what could be the reason for Adobe Reader rating the signature as invalid and/or has a solution to this problem?
Thanks in Advance
David
In short
I could not reproduce the OP's issue. Running his code (with slight adaptions to local circumstances) resulted in a java.security.NoSuchAlgorithmException: no such algorithm: SHA1 for provider SunJSSE. Having replaced the provider argument "SunJSSE" to the sign call with "BC", on the other hand, the code creates a properly certified PDF.
The adapted code
I usually examine code from stackoverflow in the form of a JUnit test; this implies a few changes. Furthermore, the OP's code contained a number of variables which were referenced but not defined; these had to be given a definition. Finally i load the file to sign from a resource as stream, not from the file system as file.
Thus:
final static File RESULT_FOLDER = new File("target/test-outputs", "signature");
#BeforeClass
public static void setUpBeforeClass() throws Exception
{
RESULT_FOLDER.mkdirs();
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
}
#Test
public void testSignLikeXinDHA() throws GeneralSecurityException, IOException, XMPException
{
String path = "keystores/demo-rsa2048.p12";
char[] pass = "demo-rsa2048".toCharArray();
KeyStore ks = KeyStore.getInstance("pkcs12", "SunJSSE");
ks.load(new FileInputStream(path), pass);
String alias = "";
Enumeration<String> aliases = ks.aliases();
while (alias.equals("demo") == false && aliases.hasMoreElements())
{
alias = aliases.nextElement();
}
PrivateKey pk = (PrivateKey) ks.getKey(alias, pass);
Certificate[] chain = ks.getCertificateChain(alias);
try ( InputStream resource = getClass().getResourceAsStream("/mkl/testarea/itext7/content/test.pdf"))
{
sign(resource, new File(RESULT_FOLDER, "test_XinDHA_signed_initial.pdf").getAbsolutePath(),
chain, pk, DigestAlgorithms.SHA1, /*"SunJSSE"*/"BC", PdfSigner.CryptoStandard.CMS, "Test", "Test",
null, null, null, 0, true);
}
}
public void sign(InputStream src, String dest, Certificate[] chain, PrivateKey pk, String digestAlgorithm,
String provider, PdfSigner.CryptoStandard subfilter, String reason, String location,
Collection<ICrlClient> crlList, IOcspClient ocspClient, ITSAClient tsaClient, int estimatedSize,
boolean initial)
throws GeneralSecurityException, IOException, XMPException
{
// Creating the reader and the signer
PdfDocument document = new PdfDocument(new PdfReader(src), new PdfWriter(dest + "_temp"));
if (initial == true)
{
document.addNewPage();
}
int pageCount = document.getNumberOfPages();
document.close();
PdfSigner signer = new PdfSigner(new PdfReader(dest + "_temp"), new FileOutputStream(dest), true);
// Creating the appearance
if (initial == true)
{
signer.setCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS);
}
PdfSignatureAppearance appearance = signer.getSignatureAppearance().setReason(reason).setLocation(location)
.setReuseAppearance(false);
Rectangle rect = new Rectangle(10, 400, 100, 100);
appearance.setPageRect(rect).setPageNumber(pageCount);
appearance.setRenderingMode(RenderingMode.NAME_AND_DESCRIPTION);
signer.setFieldName(signer.getNewSigFieldName());
// Creating the signature
IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
ProviderDigest digest = new ProviderDigest(provider);
signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
}
(AddPageAndSign.java)
Running the code
I ran the code using a fairly recent Oracle Java 8 with Unlimited Strength JavaTM Cryptography Extension Policy Files, BouncyCastle 1.49, and iText either in version 7.0.0 or 7.0.1-SNAPSHOT (the current development branch).
(Definitively use an Oracle Java as downloaded from their web site, some variants of the Oracle JDK (supplied by some Linux distributions) contains changes in the security providers which can break your code.)
Running the code using the provider argument "SunJSSE" to the sign call results in
java.security.NoSuchAlgorithmException: no such algorithm: SHA1 for provider SunJSSE
at sun.security.jca.GetInstance.getService(GetInstance.java:87)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:206)
at java.security.Security.getImpl(Security.java:698)
at java.security.MessageDigest.getInstance(MessageDigest.java:227)
at com.itextpdf.signatures.SignUtils.getMessageDigest(SignUtils.java:134)
at com.itextpdf.signatures.DigestAlgorithms.getMessageDigest(DigestAlgorithms.java:182)
at com.itextpdf.signatures.ProviderDigest.getMessageDigest(ProviderDigest.java:69)
at com.itextpdf.signatures.SignUtils.getMessageDigest(SignUtils.java:127)
at com.itextpdf.signatures.PdfSigner.signDetached(PdfSigner.java:528)
at mkl.testarea.itext7.signature.AddPageAndSign.sign(AddPageAndSign.java:125)
at mkl.testarea.itext7.signature.AddPageAndSign.testSignLikeXinDHA(AddPageAndSign.java:81)
Running the code using the provider argument "BC" to the sign call results in a properly certified PDF with the signature visualization on an extra page:
Why using SunJSSE doesn't make sense
The exception I get with the "SunJSSE" provider actually is not surprising as that provider does not provide a SHA1 algorithm.
According to its documentation by Oracle, it provides no MessageDigest algorithm as such at all, merely in combination as a signature algorithm (SHA1withRSA).
Thus, the IExternalSignature defined in sign as
IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
will work because here SHA1withRSA will be used, but the ProviderDigest defined there as
ProviderDigest digest = new ProviderDigest(provider);
will fail because it attempts to use the message digest algorithm SHA1.
As an aside
You use SHA1. As this message digest algorithm is less and less trusted in the context of signature creation, that is not a good idea. I would advise switching to an algorithm of the SHA2 famaily.
I am try to add a digital signature to a pdf using java api, and signature was read by epass2003 token. so,here i done this job(add digital signature to pdf),
and its working fine, but when i open this pdf document in another system it shows
the "Atleast one signature has problem", bu in my system validate sign correctly please help me.I have attached my code below please find it.
public class Test {
public static void main(String args[]) throws IOException, GeneralSecurityException, DocumentException, CertificateVerificationException{
// Create instance of SunPKCS11 provider
String userFile = "C:/results/test.pdf";
String userFile_signed = "C:/results/test_signed.pdf";
String pkcs11Config = "name=eToken\nlibrary=C:\\Windows\\System32\\eps2003csp11.dll";
java.io.ByteArrayInputStream pkcs11ConfigStream = new java.io.ByteArrayInputStream(pkcs11Config.getBytes());
sun.security.pkcs11.SunPKCS11 providerPKCS11 = new sun.security.pkcs11.SunPKCS11(pkcs11ConfigStream);
java.security.Security.addProvider(providerPKCS11);
// Get provider KeyStore and login with PIN
String pin = "12345678";
java.security.KeyStore keyStore = java.security.KeyStore.getInstance("PKCS11", providerPKCS11);
keyStore.load(null, pin.toCharArray());
// Enumerate items (certificates and private keys) in the KeyStore
java.util.Enumeration<String> aliases = keyStore.aliases();
String alias = null;
while (aliases.hasMoreElements()) {
alias = aliases.nextElement();
System.out.println(alias);
}
PrivateKey pk = (PrivateKey)keyStore.getKey(alias, "12345678".toCharArray());
Certificate[] chain = keyStore.getCertificateChain(alias);
OcspClient ocspClient = new OcspClientBouncyCastle();
TSAClient tsaClient = null;
for (int i = 0; i < chain.length; i++) {
X509Certificate cert = (X509Certificate)chain[i];
String tsaUrl = CertificateUtil.getTSAURL(cert);
if (tsaUrl != null) {
tsaClient = new TSAClientBouncyCastle(tsaUrl);
break;
}
}
List<CrlClient> crlList = new ArrayList<CrlClient>();
crlList.add(new CrlClientOnline(chain));
Test t = new Test();
t.sign(userFile, userFile_signed, chain, pk, DigestAlgorithms.SHA256, providerPKCS11.getName(),
CryptoStandard.CMS, "Test", "Signature", crlList, ocspClient, tsaClient, 0);
}
public void sign(String src, String dest,
Certificate[] chain, PrivateKey pk,
String digestAlgorithm, String provider, CryptoStandard subfilter,
String reason, String location,
Collection<CrlClient> crlList,
OcspClient ocspClient,
TSAClient tsaClient,
int estimatedSize)
throws GeneralSecurityException, IOException, DocumentException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
// Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(reason);
appearance.setLocation(location);
appearance.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, "sig");
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
}
}
so above is my code please help me.
Looking at the signature properties one sees:
This dialog states the problem:
The signer's identity is unknown because it has not been included in your list of trusted certificates and none of its parent certificates are trusted certificates.
Furthermore a look at the signer's certificate shows:
Thus, your code only embeds the signer certificate itself, not its certificate path (otherwise they would have shown in the certificate viewer window). Unfortunately the issuer certificate (SafeScrypt sub-CA for RCAI Class 2 2014) is not immediately trusted, neither is that certificate's issuer (SafeScrypt CA 2014), but that certificate's issuer (CCA India 2014) in turn is.
Most likely on your computer either the whole certificate chain is known or at least up to a certificate which is explicitly trusted.
To get the same effect on other computers which only know the root certificate, simply add the certificates for "SafeScrypt sub-CA for RCAI Class 2 2014" and "SafeScrypt CA 2014" to your Certificate[] chain.
I have a XFA-based PDF with some fields need to be signed. However, these fields are embedded in the XFA form, so I can't sign it using the following codes
public void sign(String keystore, char[] password, String src, String name, String dest)
throws GeneralSecurityException, IOException, DocumentException {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(new FileInputStream(keystore), password);
String alias = (String)ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias, password);
Certificate[] chain = ks.getCertificateChain(alias);
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', null, true);
// Creating the appearance
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setVisibleSignature(name);
// Creating the signature
PrivateKeySignature pks = new PrivateKeySignature(pk, DigestAlgorithms.SHA256, "BC");
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, pks, chain, null, null, null, 0, MakeSignature.CryptoStandard.CMS);
}
For example, I have a field named "root[0].mainpage[0].root[2].DefaultPage[0].Page1[0].SignArea[0].GSA[0].GSF-shinfo_sh-tmp03_sh_sf[0]". When I tried to put it to the argument "name", I got the error message
"Exception in thread "main" java.lang.IllegalArgumentException: The field root[0].mainpage[0].root[2].DefaultPage[0].Page1[0].SignArea[0].GSA[0].GSF-shinfo_sh-tmp03_sh_sf[0] does not exist."
I've worked on this problem for a week, but I haven't come up with a solution. Is there any ideas about this issue? Thanks!
PS:This is the download url of the file.
In my app I have an option for signing pdf files . There are two options for pdf signing , one where a new signature is created and the second where I need to sign empty signature field. I did the part of creating a new signature field and it works fine , now I am having problems with signing empty signature fields . This is my code
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null) ;
//ovo smo ubacili
Enumeration en = ks.aliases();
// String alias = (String)en.nextElement();
PrivateKey key = (PrivateKey)ks.getKey(alias, "password".toCharArray());
java.security.cert.Certificate[] chain = ks.getCertificateChain(alias);
//location of pdf document to sign
PdfReader reader = new PdfReader(jTextField1.getText());
String [] delovi=jTextField1.getText().split("\\\\");
String potisaniFajl=delovi[delovi.length-1];
new File(System.getProperty("user.home") + "\\Desktop\\Potpisani Fajlovi\\").mkdirs();
//signed pdf location
FileOutputStream fout = new FileOutputStream(System.getProperty("user.home") + "\\Desktop\\Potpisani Fajlovi\\"+potisaniFajl);
PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0',null,true);
PdfSignatureAppearance appearance = stp.getSignatureAppearance();
appearance.setCrypto(null, chain, null, PdfSignatureAppearance.SELF_SIGNED);
//appearance.setCrypto(key, chain, null,PdfSignatureAppearance.WINCER_SIGNED);
//appearance.setCrypto(null, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
appearance.setReason("Potpis kompenzacije");
appearance.setLocation("Foobar");
//lokacija potpisa
appearance.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, "dva");
appearance.setExternalDigest(new byte[128], null, "RSA");
appearance.preClose();
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initSign(key);
byte buf[] = new byte[8192];
int n;
InputStream inp = appearance.getRangeStream();
while ((n = inp.read(buf)) > 0) {
signature.update(buf, 0, n);
}
PdfPKCS7 sig = appearance.getSigStandard().getSigner();
sig.setExternalDigest(signature.sign(), null, "RSA");
PdfDictionary dic = new PdfDictionary();
dic.put(PdfName.CONTENTS,new PdfString(sig.getEncodedPKCS1()).setHexWriting(true));
appearance.close(dic);
This code adds a new signature , what changes I need to do to sign empty signature field name "GoodSignature"
what changes I need to do to sign empty signature field name "GoodSignature"
In your current code you call an overload of PdfSignatureAppearance.setVisibleSignature for creating a new signature field:
appearance.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, "dva");
It is documented (JavaDocs) as:
/**
* Sets the signature to be visible. It creates a new visible signature field.
* #param pageRect the position and dimension of the field in the page
* #param page the page to place the field. The fist page is 1
* #param fieldName the field name or <CODE>null</CODE> to generate automatically a new field name
*/
public void setVisibleSignature(Rectangle pageRect, int page, String fieldName)
For your new task you have to use instead this overload:
/**
* Sets the signature to be visible. An empty signature field with the same name must already exist.
* #param fieldName the existing empty signature field name
*/
public void setVisibleSignature(String fieldName)
In your case the call would be :
appearance.setVisibleSignature("GoodSignature");
That being said I'd advise you read Digital Signatures for PDF documents, A White Paper by Bruno Lowagie (iText Software). Your code seems to make use of numerous outdated techniques and you should update.