Question

After following the "Signing a document using a smart card and PKCS#11" topic in http://itextpdf.com/book/digitalsignatures and creating a code sample similar to the provided one, the signed file signature is invalid in Adobe Reader, the signature appearance has the name of the non-repudiation certificate (i.e., the name of the eID owner) but in Adobe Reader's Signature Panel shows:

Signed by Unknown

The error occured while validating: Error message

I'm using a Gemalto PinPad and the Portuguese eID pteidpkcs11.dll installed with the eID middleware software, located in C:\Windows\System32.

I've tried:

  • Null checking
  • Manually creating the Certificate chain, as the Certificate[] returned by ks.getCertificateChain("CITIZEN SIGNATURE CERTIFICATE"); only has the signature certificate
Was it helpful?

Solution 2

The provided code sample tries to get the PrivateKey of the signature certificate, I found it odd but figured it was just used as a reference. Navigating through the stack trace of the exception that is triggered when the user cancels the process in the PinPad gave me the following idea, which, fortunately, solved this:

  1. Create a custom com.itextpdf.text.pdf.security.ExternalSignature implementation
  2. Implement an utility class that, using the sun.security.pkcs11.wrapper.PKCS11 wrapper, interacts with your eID pkcs11 dll (in my case, pteidpkcs11.dll) and provides a signing method that receives a byte[] message which is then sent to the SmartCard reader to be signed, and returns the byte[] result of this operation
  3. Use the utility class in your CustomExternalSignature.sign(...)

Some tips that you can use if you're developing for the Portuguese eID Cartão Cidadão:

  • For the second item of the previous list, I'm using the PTeID4JPKCS11 class from an opensource project named pteid4j created by André Barbosa, you just need to call PTeID4JPKCS11.getInstance().sign(...);
  • Regarding the Hash and Encryption algorithm required by the ExternalSignature interface, the hash is SHA-1 and the Encryption RSA

OTHER TIPS

As an alternative you can sign with the portuguese eid card (Cartão de Cidadão) using only a java component available on www.poreid.org. It is also available on maven central repository with the artifactid poreid

Here is an example based on the sample provided in the itext documentation

public void createPdf(String filename) throws IOException, DocumentException {
    Document document = new Document();
    PdfWriter.getInstance(document, new FileOutputStream(filename));
    document.open();
    document.add(new Paragraph("Assinado com o Cartão de Cidadão!"));
    document.close();
}

public void signPdf(String src, String dest)
    throws IOException, DocumentException, GeneralSecurityException {
    KeyStore ks = KeyStore.getInstance(POReIDConfig.POREID);
    ks.load(null);
    PrivateKey pk = (PrivateKey) ks.getKey(POReIDConfig.AUTENTICACAO, null);
    Certificate[] chain = ks.getCertificateChain(POReIDConfig.AUTENTICACAO);

    // reader and stamper
    PdfReader reader = new PdfReader(src);
    FileOutputStream os = new FileOutputStream(dest);
    PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');

    // appearance
    PdfSignatureAppearance appearance = stamper .getSignatureAppearance();
    appearance.setReason("qualquer motivo");
    appearance.setLocation("qualquer localização");
    appearance.setVisibleSignature(new Rectangle(72, 732, 144, 780), 1, "primeira assinatura");

    // digital signature
    ExternalSignature es = new PrivateKeySignature(pk, "SHA-256", POReIDConfig.POREID);
    ExternalDigest digest = new ProviderDigest(null); // find provider
    MakeSignature.signDetached(appearance, digest, es, chain, null, null, null, 0, CryptoStandard.CMS);
}


public static void main(String[] args) throws DocumentException, IOException, GeneralSecurityException {
    Security.addProvider(new POReIDProvider());

    App exemplo = new App();
    exemplo.createPdf("/home/quim/exemplo.pdf");
    exemplo.signPdf("/home/quim/exemplo.pdf","/home/quim/exemplo.assinado.pdf");
}

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

 ks.getCertificateChain("CITIZEN SIGNATURE CERTIFICATE") 

should return the certificate chain.

Where I can find a working online tool?

Suggestion: Make a call to the organization responsible for the middleware and ask for support.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top