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