0

I have a program that signs pdf's via smartcard. Right now there is a new card that I need to integrate, but there is an error with the signature of the PDF (The document has been altered or corrupted since the signature was applied). Error

I thought this is strange since there is no such error with the other cards.. The only difference between the cards is that this new one I use iaik to get the sign hash and not direct APDU commands, so, I'm in doubt if the signing problem is related with my implementation of the IAIK or I just need to change the way of signing with Itext 7 on this particular card.

Here is some code:

    public byte[] signPDFDocument(byte[] pdfData, String requestKey, String requestIV, UserCertificates uc, String xLocation, String yLocation, String height, String width, String pageToSign) throws IOException{

    int numPageToSign = Integer.parseInt(pageToSign);
    
    InputStream inputStream = new ByteArrayInputStream(pdfData); 
    ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 
    
    PdfReader reader = new PdfReader(inputStream);
    PdfSigner pdfSigner = new PdfSigner(reader, outStream, new StampingProperties());
    
    //check page to sign not outOfBounds
    if(numPageToSign > pdfSigner.getDocument().getNumberOfPages() || numPageToSign <= 0)
    {
        numPageToSign = 1;
    }

    //Certs Chain
    Certificate[] certChain = new Certificate[1];
    certChain[0] = uc.getCert();

    // Create the signature appearance
    PdfFont font = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN);
    PdfFont fontBold = PdfFontFactory.createFont(FontConstants.TIMES_BOLD);
    Rectangle rect = new Rectangle(Integer.parseInt(xLocation), Integer.parseInt(yLocation), Integer.parseInt(width), Integer.parseInt(height));
    PdfSignatureAppearance signatureAppearance = pdfSigner.getSignatureAppearance();
    signatureAppearance
            // Specify if the appearance before field is signed will be used
            // as a background for the signed field. The "false" value is the default value.
            .setReuseAppearance(false)
            .setPageRect(rect)
            .setPageNumber(numPageToSign)
            .setReasonCaption("")
            .setLocationCaption("");
    pdfSigner.setFieldName("signature");
    
    
    PdfFormXObject n0 = signatureAppearance.getLayer0();
    PdfCanvas n0Canvas = new PdfCanvas(n0, pdfSigner.getDocument());
    PdfFormXObject n2 = signatureAppearance.getLayer2();
    Canvas n2Canvas = new Canvas(n2, pdfSigner.getDocument());
    CertificateInfo.X500Name x500name = CertificateInfo.getSubjectFields((X509Certificate)certChain[0]);
    String name = null;
    if (x500name != null) {
        name = x500name.getField("CN");
        if (name == null)
            name = x500name.getField("E");
    }
    
    //Signature
    Text first = new Text("Assinado por: ").setFont(font).setFontSize(9);
    Text second = new Text(name).setFontSize(8).setFont(fontBold);
    Paragraph paragraph = new Paragraph().add(first).add(second);
    paragraph.setMarginBottom(0.07f);
    n2Canvas.add(paragraph);
   
    
    //Date
    Text date = new Text("Data: ").setFont(font).setFontSize(9);
    Text date2 = new Text(new SimpleDateFormat("dd-MM-yyyy HH:mm  Z").format(Calendar.getInstance().getTime())).setFont(fontBold).setFontSize(8);
    paragraph = new Paragraph().add(date).add(date2);
    n2Canvas.add(paragraph);

    n2Canvas.close();
    

    IExternalDigest digest = new BouncyCastleDigest();
    IExternalSignature externalSignature = new SmartCardSignaturePDF();
  //  IExternalSignature externalSignature = new PrivateKeySignature(pk,"SHA-256",p.getName());
   // OCSPVerifier ocspVerifier = new OCSPVerifier(null, null); 
  //  IOcspClient ocspClient = new OcspClientBouncyCastle(ocspVerifier);
    
   
      
    try {
        pdfSigner.signDetached(digest, externalSignature, certChain, null, null, null, 0, CryptoStandard.CADES);
    } catch (IOException | GeneralSecurityException e) {

    }

    return outStream.toByteArray();     
}

And the External Signature

    public class SmartCardSignaturePDF implements IExternalSignature{

    @Override
    public String getHashAlgorithm() {
        return DigestAlgorithms.SHA256;
    }

    @Override
    public String getEncryptionAlgorithm() {
        return "RSA";
    }

    @Override
    public byte[] sign(byte[] message) throws GeneralSecurityException {
        try {
            
            return signData(MessageDigest.getInstance("SHA-256").digest(message));
        } catch (HardwareException | SignatureException e) {
        }
        return null;
    }

}

And the code for the signature from the card

    private byte[] signData(byte[] input_data) throws HardwareException {
    
    //Key Mechanism, Label and ID
    Mechanism KeyPairGenerationMechanism = Mechanism.get(PKCS11Constants.CKM_RSA_PKCS);
    
    
    char [] signTemplate = "Sign".toCharArray();
    byte[] id = "Sign".getBytes();
    
    //Private Key Template
    RSAPrivateKey rsaPrivateKeyTemplate = new RSAPrivateKey();
    //rsaPrivate
    rsaPrivateKeyTemplate.getToken().setBooleanValue(Boolean.TRUE);
    rsaPrivateKeyTemplate.getId().setByteArrayValue(id);
    rsaPrivateKeyTemplate.getLabel().setCharArrayValue(signTemplate);

    //Public Key Template
    RSAPublicKey rsaPublicKeyTemplate = new RSAPublicKey();
    //rsaPublicKeyTemplate.getVerify().setBooleanValue(value);
    rsaPublicKeyTemplate.getToken().setBooleanValue(Boolean.TRUE);
    rsaPublicKeyTemplate.getId().setByteArrayValue(id);
    rsaPublicKeyTemplate.getLabel().setCharArrayValue(signTemplate);
    
    try {

        
        this.cegerCardsession.findObjectsInit(rsaPrivateKeyTemplate);
        
        PKCS11Object[] key = this.cegerCardsession.findObjects(1024);

        if(key.length == 0)
        {
            KeyPairGenerationMechanism = Mechanism.get(PKCS11Constants.CKM_RSA_PKCS_KEY_PAIR_GEN);  
            KeyPair keyPair =  this.cegerCardsession.generateKeyPair(KeyPairGenerationMechanism, rsaPublicKeyTemplate, rsaPrivateKeyTemplate);
            KeyPairGenerationMechanism = Mechanism.get(PKCS11Constants.CKM_RSA_PKCS);
            this.cegerCardsession.signInit(KeyPairGenerationMechanism, keyPair.getPrivateKey());
        }
        else
        {
            PrivateKey PKey = new PrivateKey();
            PKey = (PrivateKey) key[0];
            this.cegerCardsession.signInit(KeyPairGenerationMechanism, PKey);
        }
        byte[] signedData = this.cegerCardsession.sign(input_data);

        return signedData;
    } catch (TokenException e) {
        throw new HardwareException(e.getMessage(), e.getCause());
    }
}

Also, a link with a signed pdf file and the original one Pdf's

I'm completly stuck for several days now and I have no idea on how to fix this :S

  • @mkl some help? :S – Miguel SIlva Sep 07 '22 at 16:42
  • Please provide enough code so others can better understand or reproduce the problem. – Community Sep 07 '22 at 17:27
  • I'lltry and look into this tomorrow. – mkl Sep 07 '22 at 21:26
  • With some further testing I think the problem is related with the private key, but still, I can't extract the right token of the private key to sign it..(the card always returns null) @mkl – Miguel SIlva Sep 09 '22 at 09:46
  • Indeed, the signing key and the certificate with the public key don't match. The signature only is 1024 bits while the public key is a 2048 bit key. But your code simply uses the first private key found. Have you checked whether the `key` array has more than one entry? Then you probably simply choose the wrong one. Also you define a template to search a matching public key. Maybe you should execute that, too, and check the results against your assumed certificates. – mkl Sep 09 '22 at 10:51
  • Thats the thing, I created a keypair and tried to associated it to the certificate(it's dumb :)) but I only did that because I cant find any key identifiers inside the card, do you think it can be a problem from the card itself for not having any key identifiers associated to the certificates? – Miguel SIlva Sep 09 '22 at 15:19
  • *"I cant find any key identifiers inside the card"* - how have you searched them? Only via your code above or by inspecting the card using some pkcs12 explorer tool? – mkl Sep 10 '22 at 06:20
  • Via code, the software that gave me the pkcs11 dll and also the adobe pkcs11 signer.. All of them don't show me the key identifiers.. – Miguel SIlva Sep 12 '22 at 08:40
  • Consider using [Pkcs11Admin](https://www.pkcs11admin.net/) or a similar tool to inspect what's available on the card via PKCS#11. – mkl Sep 12 '22 at 17:08

0 Answers0