I'm now getting an invalid digital signature that I created on PDFs we
generate (via wkhtmltopdf and PDFBox 1.8.7). It says "At least one
signature is invalid" but I previously could create them with valid
signatures. This occurred when going from BouncyCastle 1.50 to 1.51,
and if I go back to 1.50, it works fine.
The invalid signature complains that the "Document has been altered or
corrupted since it was signed".
Here's a link to an existing PDF that has an invalid signature:
http://open.esignforms.com/pdfboxlist/MyDocumentsGOOD.pdf (using BC 1.50)
http://open.esignforms.com/pdfboxlist/MyDocumentsBAD.pdf (using BC 1.51)
I am using Java 7.
Here are the relevant Java code:
boolean signPdf(File pdfFile, File signedPdfFile)
{
FileInputStream fis = null;
FileOutputStream fos = null;
PDDocument doc = null;
try
{
fis = new FileInputStream(pdfFile);
fos = new FileOutputStream(signedPdfFile);
int readCount;
byte[] buffer = new byte[8 * 1024];
while ((readCount = fis.read(buffer)) != -1)
{
fos.write(buffer, 0, readCount);
}
fis.close();
fis = new FileInputStream(signedPdfFile);
doc = PDDocument.load(pdfFile);
PDSignature signature = new PDSignature();
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
signature.setName("Open eSignForms Export PDF Integrity
Lock");
signature.setLocation(Application.getInstance().getExternalContextPath());
signature.setReason("Used to ensure that an exported PDF
has not been tampered with since its generation by Open eSignForms
deployment id: " +
Application.getInstance().getDeployId());
signature.setSignDate(Calendar.getInstance());
doc.addSignature(signature, this);
doc.saveIncremental(fis, fos);
return true;
}
catch( Exception e )
{
_logger.error("signPdf() - Failed to sign the PDF",e);
return false;
}
finally
{
if ( fis != null ) try { fis.close(); } catch( Exception e
) {}
if ( fos != null ) try { fos.close(); } catch( Exception e
) {}
if ( doc != null ) try { doc.close(); } catch( Exception e
) {}
}
}
@Override
public byte[] sign(InputStream is) throws SignatureException,
IOException
{
Application app = Application.getInstance();
try
{
String provider = app.getPublicKeyGenerator().getProvider();
SignatureKey signatureKey = app.getSignatureKey();
X509Certificate cert = signatureKey.getX509Certificate();
Store certStore = new JcaCertStore(Arrays.asList(cert));
CMSTypedDataInputStream input = new
CMSTypedDataInputStream(is);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha512Signer = new
JcaContentSignerBuilder(PublicKeyGenerator.SIGNATURE_ALGORITHM).setProvider(provider).build(signatureKey.getPrivateKey());
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
new
JcaDigestCalculatorProviderBuilder().setProvider(provider).build()).build(sha512Signer,
cert));
gen.addCertificates(certStore);
CMSSignedData signedData = gen.generate(input, false);
return signedData.getEncoded();
}
catch (Exception e)
{
_logger.error("sign() - Problem while preparing PDF
signature",e);
return null;
}
}
class CMSTypedDataInputStream implements CMSTypedData
{
InputStream in;
public CMSTypedDataInputStream(InputStream is)
{
in = is;
}
@Override
public ASN1ObjectIdentifier getContentType()
{
return PKCSObjectIdentifiers.data;
}
@Override
public Object getContent()
{
return null;
}
@Override
public void write(OutputStream out) throws IOException,
CMSException
{
byte[] buffer = new byte[8 * 1024];
int read;
while( (read = in.read(buffer)) != -1 )
{
out.write(buffer, 0, read);
}
in.close();
}
}