PaDES Signing High Level API
Intro
In our latest iText 8 release, we’ve added some high-level classes to help customers sign their documents according to the PAdES signing levels or profiles. By using the PdfPadesSigner
class (Java/.NET), developers can easily create signatures according to a specific PAdES profile.
In the code sample below, we show how you can create a Level B and Level LTA signature using the new objects.
PAdES Levels
PAdES-B-B level
This is a basic signature. It remains valid as long as the certificate used to sign it is also valid (i.e. not expired or revoked).
PAdES-B-T level
This signature is an extension of the basic signature. It includes a cryptographic timestamp token to prove that the signed document existed at a given point in time.
PAdES-B-LT level
This signature builds on the previous one by incorporating all materials required to validate the signed document. This typically includes signing certificates, timestamp certificates, and revocation data (like CRL and OCSP responses). This makes it possible to validate the signed document using the contents of the file itself.
PAdES-B-LTA level
This signature provides long-term availability and integrity of validation material. It builds on the previous level by adding a cryptographic timestamp token to the document itself and the validation material (also called a “document timestamp”).
This establishes evidence that the validation material existed at that point in time, letting a signature validator determine that no certificates were revoked or expired at the time the signature was created.
In theory, it’s possible to periodically add further document timestamp tokens. This lets a document signature remain valid long after initial certificates–and even signing algorithms–have expired or were deemed insecure.
Code Samples
import com.itextpdf.commons.bouncycastle.operator.AbstractOperatorCreationException; import com.itextpdf.commons.bouncycastle.pkcs.AbstractPKCSException; import com.itextpdf.kernel.pdf.PdfReader; import com.itextpdf.signatures.*; import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.PrivateKey; import java.security.Security; import java.security.cert.Certificate; import java.security.cert.X509Certificate; public class PadesSigningExample { public static final String BASE_URI = "src/main/resources/release/"; public static final String CERTS_URI=BASE_URI+"certs/"; public static final String PASSWORD="testpassphrase"; public static void main(String[] args) throws Exception { var m = new PadesSigningExample(); String resource_uri = BASE_URI, inputFile = resource_uri + "input.pdf", outputFile = resource_uri + "out.pdf"; // m.baselineBProfileExample(inputFile, outputFile); m.baselineLtaProfileExample(inputFile, outputFile); } public void baselineBProfileExample(String inFile, String outFile) throws IOException, GeneralSecurityException { String certPath = "src/main/resources/release/testcert.pfx"; String password = "password"; PdfPadesSigner padesSigner = new PdfPadesSigner(new PdfReader(inFile),new FileOutputStream(outFile)); SignerProperties properties = new SignerProperties(); properties.setFieldName("signatureField"); BouncyCastleProvider providerBC = new BouncyCastleProvider(); Security.addProvider(providerBC); KeyStore ks = KeyStore.getInstance("pkcs12"); InputStream fis = Files.newInputStream(Paths.get(certPath)); ks.load(fis, password.toCharArray()); String alias = ks.aliases().nextElement(); Certificate[] chain = ks.getCertificateChain(alias); PrivateKey pk = (PrivateKey) ks.getKey(alias, password.toCharArray()); TSAClientBouncyCastle tsa = new TSAClientBouncyCastle("https://rfc3161.ai.moda"); padesSigner.signWithBaselineLTAProfile(properties,chain,pk,tsa); } void baselineLtaProfileExample(String inFile, String outFile) throws IOException, GeneralSecurityException, AbstractOperatorCreationException, AbstractPKCSException { String signCertFileName = CERTS_URI+ "signCertRsa01.pem"; String caCertFileName = CERTS_URI+"rootRsa.pem"; BouncyCastleProvider providerBC = new BouncyCastleProvider(); Security.addProvider(providerBC); Certificate[] signRsaChain = PemFileHelper.readFirstChain(signCertFileName); PrivateKey signRsaPrivateKey = PemFileHelper.readFirstKey(signCertFileName, PASSWORD.toCharArray()); X509Certificate caCert = (X509Certificate) PemFileHelper.readFirstChain(caCertFileName)[0]; PrivateKey caPrivateKey = PemFileHelper.readFirstKey(caCertFileName, PASSWORD.toCharArray()); var pkSig = new PrivateKeySignature(signRsaPrivateKey, DigestAlgorithms.SHA256,BouncyCastleProvider.PROVIDER_NAME); PdfPadesSigner padesSigner = new PdfPadesSigner(new PdfReader(inFile),new FileOutputStream(outFile)); SignerProperties signerProperties = new SignerProperties(); ICrlClient crlClient = new TestCrlClient().addBuilderForCertIssuer(caCert, caPrivateKey); TestOcspClient ocspClient = new TestOcspClient().addBuilderForCertIssuer(caCert, caPrivateKey); TSAClientBouncyCastle tsa = new TSAClientBouncyCastle("https://rfc3161.ai.moda"); padesSigner.setCrlClient(crlClient) .setOcspClient(ocspClient); padesSigner.signWithBaselineLTAProfile(signerProperties,signRsaChain,pkSig,tsa); }
using iText.Bouncycastleconnector; using iText.Commons.Bouncycastle; using iText.Commons.Bouncycastle.Cert; using iText.Commons.Bouncycastle.Crypto; using iText.Commons.Bouncycastle.Openssl; using iText.Commons.Utils; using iText.Kernel.Pdf; using iText.Signatures; using itext8_project.s81866; using Org.BouncyCastle.Pkcs; namespace itext8_project.ReleaseExample.itext802; public class PadesSigningExample{ public const String URI = "../../../resources/ReleaseExample/"; public const String CERTS_URI = URI + "certs/"; public const String PASSWORD = "testpassphrase"; public void BaselineBProfileExample(String inFile, String outFile){ String certPath = CERTS_URI+"certs/signCertRsa01.pem"; PdfPadesSigner padesSigner = new PdfPadesSigner(new PdfReader(inFile), new FileStream(outFile, FileMode.Create)); SignerProperties properties = new SignerProperties(); properties.SetFieldName("signatureField"); var chain = PemFileHelper.ReadFirstChain(certPath); var privateKey = PemFileHelper.ReadFirstKey(certPath, PASSWORD.ToCharArray()); var pkSig = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA256); TSAClientBouncyCastle tsa = new TSAClientBouncyCastle("https://rfc3161.ai.moda"); // padesSigner.SignWithBaselineBProfile(properties, chain, pkSig); // padesSigner.SignWithBaselineLTAProfile(properties,chain,pkSig,tsa); } public void BaseLineLtaExample(String inFile, String outFile){ String srcFileName = URI + "helloWorldDoc.pdf"; String signCertFileName = CERTS_URI + "signCertRsa01.pem"; String caCertFileName = CERTS_URI +"rootRsa.pem"; PdfPadesSigner padesSigner = new PdfPadesSigner(new PdfReader(inFile), new FileStream(outFile, FileMode.Create)); SignerProperties properties = new SignerProperties(); properties.SetFieldName("signatureField"); IX509Certificate[] signRsaChain = PemFileHelper.ReadFirstChain(signCertFileName); IPrivateKey signRsaPrivateKey = PemFileHelper.ReadFirstKey(signCertFileName, PASSWORD.ToCharArray()); IX509Certificate caCert = (IX509Certificate)PemFileHelper.ReadFirstChain(caCertFileName)[0]; IPrivateKey caPrivateKey = PemFileHelper.ReadFirstKey(caCertFileName, PASSWORD.ToCharArray()); SignerProperties signerProperties = new SignerProperties(); TSAClientBouncyCastle tsa = new TSAClientBouncyCastle("https://rfc3161.ai.moda"); ICrlClient crlClient = new TestCrlClient().AddBuilderForCertIssuer(caCert, caPrivateKey); TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, caPrivateKey); padesSigner.SetOcspClient(ocspClient) .SetCrlClient(crlClient); var privateKey = PemFileHelper.ReadFirstKey(signCertFileName, PASSWORD.ToCharArray()); var pkSig = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA256); padesSigner.SignWithBaselineLTAProfile(signerProperties, signRsaChain, pkSig, tsa); } class PemFileHelper{ private static readonly IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.GetFactory(); private PemFileHelper(){ } public static IX509Certificate[] ReadFirstChain(String pemFileName){ return ReadCertificates(pemFileName).ToArray(new IX509Certificate[0]); } public static IPrivateKey ReadFirstKey(String pemFileName, char[] keyPass){ return ReadPrivateKey(pemFileName, keyPass); } public static List<IX509Certificate> InitStore(String pemFileName){ IX509Certificate[] chain = ReadFirstChain(pemFileName); return chain.Length > 0 ? new List<IX509Certificate>{chain[0]} : chain.ToList(); } private static IList<IX509Certificate> ReadCertificates(String pemFileName){ using (TextReader file = new StreamReader(pemFileName)){ IPemReader parser = FACTORY.CreatePEMParser(file, null); Object readObject = parser.ReadObject(); IList<IX509Certificate> certificates = new List<IX509Certificate>(); while (readObject != null){ if (readObject is IX509Certificate){ certificates.Add((IX509Certificate) readObject); } readObject = parser.ReadObject(); } return certificates; } } private static IPrivateKey ReadPrivateKey(String pemFileName, char[] keyPass){ using (TextReader file = new StreamReader(pemFileName)){ IPemReader parser = FACTORY.CreatePEMParser(file, keyPass); Object readObject = parser.ReadObject(); while (!(readObject is IPrivateKey) && readObject != null){ readObject = parser.ReadObject(); } return (IPrivateKey) readObject; } } } }