I have implemented a feature to digitally sign a document using iText 8.0.5 in Java. However, after signing the document, it shows that the document has been altered or modified.
How to properly digitally sign the document using iText 8.0.5?
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Calendar;
import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.signatures.BouncyCastleDigest;
import com.itextpdf.signatures.DigestAlgorithms;
import com.itextpdf.signatures.IExternalSignature;
import com.itextpdf.signatures.PdfSigner;
import com.itextpdf.signatures.PdfSigner.CryptoStandard;
import com.itextpdf.signatures.SignatureFieldAppearance;
public class PdfEmbedder {
public String pdfSigner(String src, String dest, String signatureValue, String certificateString) throws Exception {
signatureValue = signatureValue.trim().replaceAll("[^A-Za-z0-9+/=]", "");
byte[] decodedSignatureValue;
try {
decodedSignatureValue = Base64.getDecoder().decode(signatureValue);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid Base64 encoded signature value", e);
}
certificateString = certificateString.trim()
.replaceAll("-----BEGIN CERTIFICATE-----|-----END CERTIFICATE-----", "")
.replaceAll("\\s+", "");
byte[] decodedCertificate = Base64.getDecoder().decode(certificateString);
Security.addProvider(new BouncyCastleProvider());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
InputStream certificateInputStream = new ByteArrayInputStream(decodedCertificate);
X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(certificateInputStream);
PdfReader reader = new PdfReader(src);
PdfWriter writer = new PdfWriter(dest);
PdfSigner signer = new PdfSigner(reader, writer, new PdfSigner.StampingProperties().useAppendMode());
Calendar signingDate = Calendar.getInstance();
PdfDocument pdfDocument = signer.getDocument();
PdfPage firstPage = pdfDocument.getFirstPage();
Rectangle pageSize = firstPage.getPageSize();
float sigWidth = 200;
float sigHeight = 50;
float xPos = pageSize.getWidth() - (sigWidth + 20);
float yPos = 10;
Rectangle rect = new Rectangle(xPos, yPos, sigWidth, sigHeight);
if (firstPage.getRotation() == 90) {
rect = new Rectangle(pageSize.getWidth() - sigHeight, pageSize.getHeight() - sigWidth, sigHeight, sigWidth);
}
signer.setPageRect(rect).setPageNumber(1).setSignDate(signingDate);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss XXX");
String formattedDate = sdf.format(signingDate.getTime());
SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.setContent("Digitally signed by " + getSignerName(certificate) + "\nDate: " + formattedDate);
appearance.setFontSize(8);
signer.setSignatureAppearance(appearance);
signer.setFieldName("sig");
IExternalSignature externalSignature = new IExternalSignature() {
@Override
public byte[] sign(byte[] messageHash) throws GeneralSecurityException {
return decodedSignatureValue;
}
@Override
public String getDigestAlgorithmName() {
return DigestAlgorithms.SHA256;
}
@Override
public String getSignatureAlgorithmName() {
return "RSA";
}
@Override
public ISignatureMechanismParams getSignatureMechanismParameters() {
return null;
}
};
X509Certificate[] chain = new X509Certificate[]{certificate};
signer.signDetached(new BouncyCastleDigest(), externalSignature, chain, null, null, null, 8192, CryptoStandard.CMS);
return "Document signed successfully with certificate";
}
public static String getSignerName(X509Certificate certificate) {
X500Principal principal = certificate.getSubjectX500Principal();
String dn = principal.getName();
for (String part : dn.split(",")) {
if (part.trim().startsWith("CN=")) {
return part.trim().substring(3);
}
}
return "Unknown";
}
}