Hi,
I encountered a PDF signed with an AdES signature that failed signature
validation.
The problem turned out to be a failure to parse the signature certificates.
Examining PdfPKCS7, this appears to be a known problem.
In the code, where the problem occurred, I found:
// the certificates
/*
This should work, but that's not always the case because of a bug in
BouncyCastle:
*/
X509CertParser cr = new X509CertParser();
cr.engineInit(new ByteArrayInputStream(contentsKey));
certs = cr.engineReadAll();
/*
The following workaround was provided by Alfonso Massa, but it
doesn't always work either.
ASN1Set certSet = null;
ASN1Set crlSet = null;
while (content.getObjectAt(next) instanceof ASN1TaggedObject) {
ASN1TaggedObject tagged =
(ASN1TaggedObject)content.getObjectAt(next);
switch (tagged.getTagNo()) {
case 0:
certSet = ASN1Set.getInstance(tagged, false);
break;
case 1:
crlSet = ASN1Set.getInstance(tagged, false);
break;
default:
throw new IllegalArgumentException("unknown tag value "
+ tagged.getTagNo());
}
++next;
}
certs = new ArrayList<Certificate>(certSet.size());
CertificateFactory certFact =
CertificateFactory.getInstance("X.509", new BouncyCastleProvider());
for (Enumeration en = certSet.getObjects();
en.hasMoreElements();) {
ASN1Primitive obj =
((ASN1Encodable)en.nextElement()).toASN1Primitive();
if (obj instanceof ASN1Sequence) {
ByteArrayInputStream stream = new
ByteArrayInputStream(obj.getEncoded());
X509Certificate x509Certificate =
(X509Certificate)certFact.generateCertificate(stream);
stream.close();
certs.add(x509Certificate);
}
}
*/
And sure enough, the BouncyCastle X509CertParser function engineReadAll();
fails when initated with the full cms object.
I then tried to invoke the workaround provided by Alfonso Massa (commenting
away the 3 lines that should work).
And that failed too.
So I started digging deeper, and could not understand why the workaround
failed. Because it looked all right.
Then I spotted the preceding lines of code:
int next = 3;
while (content.getObjectAt(next) instanceof ASN1TaggedObject)
++next;
And of course!
These lines of codes steps the "next" counter passed the certificates.
Prohibiting the fix from Massa from catching the certs.
Once I removed the while statement here, the fix from Massa worked
perfectly.
I think this is easily missed. IMO the code should at least look like this:
int next = 3;
// the certificates
/*
This should work, but that's not always the case because of a bug in
BouncyCastle:
*/
X509CertParser cr = new X509CertParser();
cr.engineInit(new ByteArrayInputStream(contentsKey));
certs = cr.engineReadAll();
while (content.getObjectAt(next) instanceof ASN1TaggedObject)
++next;
/*
The following workaround was provided by Alfonso Massa, but it
doesn't always work either.
ASN1Set certSet = null;
ASN1Set crlSet = null;
while (content.getObjectAt(next) instanceof ASN1TaggedObject) {
ASN1TaggedObject tagged =
(ASN1TaggedObject)content.getObjectAt(next);
switch (tagged.getTagNo()) {
case 0:
certSet = ASN1Set.getInstance(tagged, false);
break;
case 1:
crlSet = ASN1Set.getInstance(tagged, false);
break;
default:
throw new IllegalArgumentException("unknown tag value "
+ tagged.getTagNo());
}
++next;
}
certs = new ArrayList<Certificate>(certSet.size());
CertificateFactory certFact =
CertificateFactory.getInstance("X.509", new BouncyCastleProvider());
for (Enumeration en = certSet.getObjects();
en.hasMoreElements();) {
ASN1Primitive obj =
((ASN1Encodable)en.nextElement()).toASN1Primitive();
if (obj instanceof ASN1Sequence) {
ByteArrayInputStream stream = new
ByteArrayInputStream(obj.getEncoded());
X509Certificate x509Certificate =
(X509Certificate)certFact.generateCertificate(stream);
stream.close();
certs.add(x509Certificate);
}
}
*/
This would make it a lot clearer that you need to remove the while loop
updating the counter next if you invoke the fix from Massa, as that counter
increment i duplicated in Massa's code.
I placed the while loop that increments the counter passed all tagged object
after the certificate parsing code, as that to me is the logical order.
First I catch the certs, then I step the counter passed the certs (and other
tagged objects).
I ran Massa's fix on all test signed PDFs I have, and I could not find a
single one where Massa's fix failed to retrieve the certs. Is perhaps the
reason why it is said to "doesn't always work either" because of the same
problem I had?
If so, then I would suggest including Massa's fix by default instead, as it
at least in my tests, is far more likely to succeed, than the
X509CertParser.
Finally, a small nit.
What confused me a bit in the debugging is a confusing variable naming in
this part of PdfPKCS7
The variable "signedData" is actually the CMS/pkcs7 object, and the variable
"content" is actually the SignedData object.
To make it easier to review the code, I renamed "signedData" to "cms", and
"content" to "signedData".
Stefan Santesson
3xA Security AB
Scheelevägen 17, 223 70, Lund
http://AAA-sec.com
ste...@aaa-sec.com
+46-767 861337
------------------------------------------------------------------------------
Precog is a next-generation analytics platform capable of advanced
analytics on semi-structured data. The platform includes APIs for building
apps and a phenomenal toolset for data science. Developers can use
our toolset for easy data analysis & visualization. Get a free account!
http://www2.precog.com/precogplatform/slashdotnewsletter
_______________________________________________
iText-questions mailing list
iText-questions@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/itext-questions
iText(R) is a registered trademark of 1T3XT BVBA.
Many questions posted to this list can (and will) be answered with a reference
to the iText book: http://www.itextpdf.com/book/
Please check the keywords list before you ask for examples:
http://itextpdf.com/themes/keywords.php