I've been working on digital signature for PDF documents using Portuguese citizen card, and here is what I have:
public void signCAdES(...) {
String pkcs11Config = "name=GemPC" + "\n" + "library=C:\\WINDOWS\\SysWOW64\\pteidpkcs11.dll";
ByteArrayInputStream configStream = new ByteArrayInputStream(pkcs11Config.getBytes());
Provider pkcs11Provider = new sun.security.pkcs11.SunPKCS11(configStream);
//provider_name: SunPKCS11-GemPC
Security.addProvider(pkcs11Provider);
javax.security.auth.callback.CallbackHandler cmdLineHdlr = new DialogCallbackHandler();
KeyStore.Builder builder = KeyStore.Builder.newInstance("PKCS11", pkcs11Provider,
new KeyStore.CallbackHandlerProtection(cmdLineHdlr));
KeyStore ks= builder.getKeyStore();
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', new File(tempPath), true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
appearance.setReason(reason);
appearance.setLocation(location);
appearance.setCertificationLevel(level);
String alias = "CITIZEN SIGNATURE CERTIFICATE";
//certificates from electronic card and resources folder
Certificate[] certs = getSignatureCertificatesChain(ks);
PrivateKey pk = (PrivateKey) ks.getKey(alias, null);
ExternalSignature es = new PrivateKeySignature(pk, "SHA-1", pkcs11Provider.getName());
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance, digest, es, certs, null, null, null, 0, MakeSignature.CryptoStandard.CADES);
}
I have to build the certificates chain (getSignatureCertificatesChain(ks)) too since ks.getCertificateChain("CITIZEN SIGNATURE CERTIFICATE") only gives one certificate, and then the card itself doesn't have all the certificates so I have to get the missing ones at pki.cartaodecidadao.pt website and put them in a resources folder. Basically, I build my chain using both certificates in the card and in the resources folder, by linking them with the values in certificate.getIssuerX500Principal().getName() and certificate.getSubjecX500Principal().getName() (different cards can have different certificates of the same types, since the validity can vary so in one same type there can be 004 or 008 for example).
From what I understood, itext support for CAdES (MakeSignature.CryptoStandard.CADES) is more recent, but you need to use this since using MakeSignature.CryptoStandard.CMS might result in a signature that does not satisfy all the standards for CAdES (for example, missing the signing-certificate attribute - see http://docbox.etsi.org/ESI/Open/Latest_Drafts/prEN-319122-1v003-CAdES-core-STABLE-DRAFT.pdf).
The only minor issue with this code thought, is that there might be some optional attributes missing. I have used a validator from this tool https://github.com/arhs/sd-dss and while the signature generated passes the validation, it still gives a warning that the attribute issuer-serial is missing. I've created a post in hope anyone knows how to include the attribute here: CAdES Digital Signature