iText 7 adding ltv to existing signature - java

I'm new to iText 7,
I've tried adding ltv information to existing signature by using addLtv() method (code provided below)...If I understand right, this method adds crl or ocsp parameters to existing signatures or timestamps and then timestamps the document, but pdf document that is generated is not ltv enabled. However, I am able to generate ltv enabled signature if I sign document with ocsp or crl list in the first place, that leads me to believe that this issue is not related to some certificate missing or not added properly. So the addLtv() method, for some reason does not work for me. I hope I'm making sense :D
Any help or suggestion is greatly apriciated :)
private void addLtv(String src, String dest, IOcspClient ocsp, ICrlClient crl, ITSAClient tsa) throws Exception {
PdfReader r = new PdfReader(src);
FileOutputStream fos = new FileOutputStream(dest);
PdfDocument pdfDoc = new PdfDocument(new PdfReader(src), new PdfWriter(dest));
PdfSigner ps= new PdfSigner(r, fos, true);
LtvVerification v = new LtvVerification(pdfDoc);
SignatureUtil signatureUtil = new SignatureUtil(pdfDoc);
List<String> names = signatureUtil.getSignatureNames();
String sigName = names.get(names.size() - 1);
PdfPKCS7 pkcs7 = signatureUtil.verifySignature(sigName);
if (pkcs7.isTsp()) {
v.addVerification(sigName, ocsp, crl,
LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.CRL,
LtvVerification.CertificateInclusion.YES);
}
else {
for (String name : names) {
v.addVerification(name, ocsp, crl,
LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP,
LtvVerification.CertificateInclusion.YES);
v.merge();
}
}
ps.timestamp(tsa, null);
}

The conceptual issue in your code is that after adding LTV information you want to finalize the process by time stamping. While this is what you would want to do with a PDF which already is in a LTV workflow, it is not what you want to do with a PDF all signatures of which you want Adobe Reader to declare as "LTV-enabled".
(One can consider Adobe's term "LTV-enabled" to mean "ready to enter a LTV workflow," i.e. not yet actually being in a LTV workflow but including all validation information required to enter it without any further ado.)
There are other issues, too, in your method, e.g. you create two separate objects which write to dest, the PdfSigner ps via fos and the PdfDocument pdfDoc via its PdfWriter. Eventually you'll either find the output of only one of these objects in the file or a mish-mash of both.
Thus, here is a working port of my old iText 5 method addLtvNoTS (from this answer) to iText 7:
void addLtvNoTS(InputStream src, OutputStream dest, IOcspClient ocsp, ICrlClient crl, LtvVerification.Level timestampLevel, LtvVerification.Level signatureLevel) throws IOException, GeneralSecurityException
{
PdfDocument pdfDoc = new PdfDocument(new PdfReader(src), new PdfWriter(dest), new StampingProperties().useAppendMode());
LtvVerification v = new LtvVerification(pdfDoc);
SignatureUtil signatureUtil = new SignatureUtil(pdfDoc);
List<String> names = signatureUtil.getSignatureNames();
String sigName = names.get(names.size() - 1);
PdfPKCS7 pkcs7 = signatureUtil.verifySignature(sigName);
if (pkcs7.isTsp())
{
v.addVerification(sigName, ocsp, crl, LtvVerification.CertificateOption.WHOLE_CHAIN,
timestampLevel, LtvVerification.CertificateInclusion.YES);
}
else
{
for (String name : names)
{
v.addVerification(name, ocsp, crl, LtvVerification.CertificateOption.WHOLE_CHAIN,
signatureLevel, LtvVerification.CertificateInclusion.YES);
}
}
v.merge();
pdfDoc.close();
}
(EnableLtv method addLtvNoTS)
As you can see I made the LtvVerification.Level values for time stamps and signatures method parameters.
This is because iText's method LtvVerification.addVerification only adds LTV information for the immediate PDF signatures and document timestamps, not for already embedded or now added OCSP response or CRL signatures.
Often this is no problem, often certificates used for signing OCSP responses are marked in a way that indicates that no revocation checks are required, and certificates used for signing CRLs usually are the implicitly trusted CA certificates themselves.
In case of my test signature, though, the OCSP responses are signed by signatures which do require revocation checks. To make things work without significant additional coding, I used CRLs all over and for that introduced the parameters.
Actually I even had to give the CRL URL explicitly to the CrlClientOnline because of iText by default only using the first CRL distribution point in a certificate and at the same time not supporting LDAP URLs.
The bottom line: You also may have to tweak your code around current iText limits or be the one to improve the iText code itself to reliably produce LTV-enabled PDFs given arbitrary valid input...

Related

PDFBox Add Validation Information on Certification Signature

how to add validation information on PDF during signing if signature use Certification
SigUtils.setMDPPermission(doc, signature, 1);
cause warning message on function tell addValidationInformation.validateSignature(inPath, outFile) :
PDF is certified to forbid changes, some readers may report the document as invalid despite that the PDF specification allows DSS additions
i call addValidationInformation function after signing doc, signing.signPDF();
what i have tried with this function :
private void makeLTV() {
try {
COSDictionary catalogDict = doc.getDocumentCatalog().getCOSObject();
catalogDict.setNeedToBeUpdated(true);
byte[][] certs = new byte[certificateChain.length][];
for (int i = 0; i < certificateChain.length; i++) {
certs[i] = certificateChain[i].getEncoded();
}
// Assign byte array for storing certificate in DSS Store.
List<CRL> crlList = new ArrayList<CRL>();
List<OCSPResp> ocspList = new ArrayList<OCSPResp>();
for (int i = 0; i < certificateChain.length; i++) {
X509Certificate cert = (X509Certificate) certificateChain[i];
if (!cert.getIssuerDN().equals(cert.getSubjectDN())) {
X509Certificate issuerCert = (X509Certificate) certificateChain[i + 1];
if (issuerCert != null) {
OCSPResp ocspResp;
ocspResp = new GetOcspResp().getOcspResp(cert, issuerCert);
if (ocspResp != null) {
ocspList.add(ocspResp);
}
}
crlList.addAll(new DssHelper().readCRLsFromCert(cert));
}
}
byte[][] crls = new byte[crlList.size()][];
for (int i = 0; i < crlList.size(); i++) {
crls[i] = ((X509CRL) crlList.get(i)).getEncoded();
LogSystem.info("set CRL data");
}
byte[][] ocsps = new byte[ocspList.size()][];
for (int i = 0; i < ocspList.size(); i++) {
ocsps[i] = ocspList.get(i).getEncoded();
}
Iterable<byte[]> certifiates = Arrays.asList(certs);
COSDictionary dss = new DssHelper().createDssDictionary(certifiates, Arrays.asList(crls),
Arrays.asList(ocsps));
catalogDict.setItem(COSName.getPDFName("DSS"), dss);
} catch (Exception e) {
// TODO Auto-generated catch block
LogSystem.error(e.toString());
e.printStackTrace();
}
before doc.addSignature(signature, signatureInterface, signatureOptions);
PDF is certified to forbid changes, some readers may report the document as invalid despite that the PDF specification allows DSS additions
Well, the LTV level addition is indeed allowed to PDF documents even with restricted MDP permissions. See "Table 257 — Entries in the DocMDP transform parameters dictionary" of ISO 32000-2:
P number (Optional) The access permissions granted for this document. Changes to a PDF that are incremental updates which include only the data necessary to add DSS’s 12.8.4.3, "Document Security Store (DSS)" and/or document timestamps 12.8.5, "Document timestamp (DTS) dictionary" to the document shall not be considered as changes to the document as defined in the choices below.
So technically speaking you are allowed to add validation data to a PDF signature.
However, you need to take into account the practical aspect of this change. For example, the most commonly used application for reading electronic signatures Adobe, more likely, will invalidate such change. The problem here, that the "extension" of a signature with a validation data and/or a timestamp may involve other changes within a PDF document, which may be not considered as part of the allowed changes, as there is no formal guidance on either how to create such signature nor as validate them.
For your information, ETSI standardization group is currently working on a new set of standards that should provide a formal guidance for validation of different AdES signature formats. So maybe in the future this part will be clarified.
For the implementation part of the LTV level addition, I would recommend to add validation data to your signature after the production time of a first timestamp (signature-time-stamp or a PDF document timestamp), that will ensure that the revocation data is fresh and can be considered during the validation process (see "5.2.5 Revocation freshness checker" of EN 319 102-1).
For this you will need to add the revocation data to a signed document within a new incremental update (revision). For this you will need to use the method
pdDocument.saveIncremental(...)
executed on a signed PDDocument.

add custom value for signature file using pdfbox java

How i will add my custom value as like (xyz123) when adding signature.Because when i adding signature that time i am not able to add custom file for signature. the field "signature1" automatically added inside the document.
My output file screenshot attached bello:
Instead of "signature1", I want to add my custom value as (xyz123)
throws IOException {
PDSignature pdSignature = new PDSignature();
pdSignature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
pdSignature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
pdSignature.setName("jvmfy");
pdSignature.setReason("Learn how to sign pdf with jvmfy.com!");
pdSignature.setLocation("location");
// the signing date, needed for valid signature
pdSignature.setSignDate(Calendar.getInstance());
// register signature dictionary and sign interface
document.addSignature(pdSignature, signature);
// write incremental (only for signing purpose)
// use saveIncremental to add signature, using plain save method may break up a
// document
document.saveIncremental(output);
}
private void signDetached(SignatureInterface signature, PDDocument document, OutputStream output)
throws IOException {
PDSignature pdSignature = new PDSignature();
pdSignature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
pdSignature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
pdSignature.setSignDate(Calendar.getInstance());
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
if (acroForm == null)
{
acroForm = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(acroForm);
}
PDSignatureField signatureField = new PDSignatureField(acroForm);
signatureField.setPartialName("xyz123");
signatureField.setValue(pdSignature);
signatureField.getWidgets().get(0).setPage(document.getPage(0));
document.addSignature(pdSignature, signature);
document.saveIncremental(output);
}```
You need your PDSignatureField object... when you have it, do this:
signatureField.setPartialName("xyz123");
If the code doesn't create its own PDSignatureField object (as in the example for invisible signature fields), PDFBox does it for you. You can get all PDSignatureField objects by calling PDDocument.getSignatureFields().
If you created the file yourself, then there is only one such field. If you are signing existing files, then it's more tricky, then I'd recommend that you compare the field names or the results of getCOSObject() (i.e. create two sets). Don't assume that the last one is the right one (in some cases it isn't).
Or you create the field yourself:
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm(null);
if (acroForm == null)
{
acroForm = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(acroForm);
}
PDSignatureField signatureField = new PDSignatureField(acroForm);
signatureField.setValue(signature);
signatureField.getWidgets().get(0).setPage(document.getPage(0));
acroForm.getFields().add(signatureField);
// page annotation, only needed if PDF/A
document.getPage(0).getAnnotations().add(signatureField.getWidgets().get(0));
document.getPage(0).getCOSObject().setNeedToBeUpdated(true);

Adding PDF revocation information as an signed attribute pdfRevocationInfoArchival OID 1.2.840.113583.1.1.8

We have added pdfRevocationInfoArchival OID (1.2.840.113583.1.1.8) as a signed attribute while generating signature. While building this attribute we used external CRL file (ca-crl.crl) and builds the OID 1.2.840.113583.1.1.8 as ASN1 object. After signing pdf and timestamping signature, everything works fine. But we are not able to understand the identifier (adbe-revocationInfoArchival) which added is in the PDF is correct and which can be used to verify this PDF by fetching this OID. Also we are not able to check that this attribute is in proper format in which PDF can verify it. Is there any tool or utility available to check this attribute which is inserted as a signed attribute is valid.
If any PDF tool/utility is available to visualize attribute are available in PDF please share.
We have build the issuers CRL info at position [0] is this correct to way add the CRL in this OID? I have share the code below code snippet
The following object identifier identifies Adobe's revocation information attribute:
adbe-revocationInfoArchival OBJECT IDENTIFIER ::=
{ adbe(1.2.840.113583) acrobat(1) security(1) 8 }
Adobe's Revocation Information attribute value has ASN.1 type RevocationInfoArchival:
/**
** RevocationInfoArchival ::= SEQUENCE {
** crl [0] EXPLICIT SEQUENCE of CRLs, OPTIONAL
** ocsp [1] EXPLICIT SEQUENCE of OCSP Responses, OPTIONAL
** otherRevInfo [2] EXPLICIT SEQUENCE of OtherRevInfo, OPTIONAL
}
*/
Adobe's Revocation Information attribute value building using in Java Bouncy Castle API:
private ASN1EncodableVector genPdfInfoArchival(ASN1EncodableVector v) {
ASN1EncodableVector v1 = new ASN1EncodableVector();
List<X509CRL> crls = new ArrayList<X509CRL>();
ASN1InputStream t = null;
try {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
CRL crl = certFactory.generateCRL(new FileInputStream(new File("e://app//esp//crl//NSDLe-GovCA2019-Test-2.crl")));
System.out.println("crl:" + crl);
crls.add((X509CRL)crl);
if (!crls.isEmpty()) {
ASN1EncodableVector v11 = new ASN1EncodableVector();
for (Iterator<X509CRL> i = crls.iterator(); i.hasNext();) {
t = new ASN1InputStream(new ByteArrayInputStream(i.next().getEncoded()));
v11.add(t.readObject());
}
//0 for CRL
v1.add(new DERTaggedObject(true, 0, new DERSequence(v11)));
}
}
return v1;}
After building OID adding it in SignerInforGeneratorBuilder as a signed attribute and generating the signature and then adding this signature in PDF
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ASN1EncodableVector signedAttr = new ASN1EncodableVector();
String ID_ADBE_REVOCATION = "1.2.840.113583.1.1.8";
//TODO add message digest for sgning - nikhilW
Attribute attr = new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(IOUtils.toByteArray(hashdata))));
signedAttr.add(attr);
//TODO generate pdf info archival and add it as CMS signed attribute - nikhilW
ASN1EncodableVector pdfInfo = genPdfInfoArchival(signedAttr);
Attribute ar = new Attribute(new ASN1ObjectIdentifier(ID_ADBE_REVOCATION), new DERSet (new DERSequence(pdfInfo)));
signedAttr.add(ar);
List<Certificate> certList = new ArrayList<Certificate>();
certList.addAll(Arrays.asList(certificateChain));
Store certs = new JcaCertStore(certList);
DefaultSignedAttributeTableGenerator sa = new DefaultSignedAttributeTableGenerator(new AttributeTable(signedAttr));
SignerInfoGeneratorBuilder builder = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider());
builder.setSignedAttributeGenerator(sa);
Please find below google drive link contains sample signed pdf file hello_signed_ts_pdfarchivalinfo.pdf
pdf_sample_signed
Any help would be appreciated.
I have checked for the attribute added in pdf using below java source code. Also there is iText Java utility which will debug pdf object tree iText RUP or download it from my google drive link Download iTextRUP Java Jar run it with java -jar jar-name may it will help debuging pdf objects.
Pdf Archival Info Retrieval Source Code Returns CRL stream object at position [0]
private void getPdfRevoInfoArch(SignerInformation signerInform) {
AttributeTable at = signerInform.getSignedAttributes();
ASN1Encodable arch = at.get(new ASN1ObjectIdentifier("1.2.840.113583.1.1.8")).getAttrValues().getObjectAt(0);
//ASN1Encodable arch1 = at.get(new ASN1ObjectIdentifier("1.2.840.113583.1.1.8")).getAttrValues().getObjectAt(1);
System.out.println("arc:" + arch);
System.out.println("archSize:" + at.get(new ASN1ObjectIdentifier("1.2.840.113583.1.1.8")).getAttrValues().size());
}

"AT least one signature is invalid" error message after removing signature from pdf using itext

I have a digitally signed pdf with multiple signatures. Now I want to remove only one of the signatures. I am using itext for the same. The code is as follows:
PdfReader reader = new PdfReader(src_path);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest_path));
{
AcroFields.Item item = stamper.getAcroFields().getFieldItem(fieldname);
ClearSignatureDictionary(item.getMerged(0));
ClearSignatureDictionary(item.getWidget(0));
ClearSignatureDictionary(item.getValue(0));
}
}
private static void ClearSignatureDictionary(PdfDictionary dic)
{
dic.remove(PdfName.AP);
dic.remove(PdfName.AS);
dic.remove(PdfName.V);
dic.remove(PdfName.DV);
dic.remove(PdfName.SV);
dic.remove(PdfName.FF);
dic.put(PdfName.F, new PdfNumber(4));
}
But when I open the document with the removed signature, it gives me the following error on Acrobat reader
"At least one of the signatures is invalid"
You can not remove keys from a dictionary in a signed document, and expect the signatures to remain valid. You can only remove the last signature that was added. If a document was signed by multiple people, and you want to remove the first signature, all subsequent signatures will be broken.
This image explains why:
This image shows that every new digital signature keeps the original bytes intact. With every new signature new bytes are added. Rev1 represents the bytes of a document that has 1 digital signature. Rev2 represents the bytes of a document that has 2 digital signatures. The second digital signatures signs Rev1 completely. If you'd remove the first signature, the second signature would become invalid.
A digital signature is a special type of form field. With iText, you can get the names of the signature form fields of a PDF like this:
PdfReader reader = new PdfReader(path);
AcroFields fields = reader.getAcroFields();
ArrayList<String> names = fields.getSignatureNames();
You can only remove the signature that covers the whole document, for instance, if we have "sig1", "sig2", and "sig3" (added in that order), only fields.signatureCoversWholeDocument("sig3") will return true.
You can get the total number of revisions like this: fields.getTotalRevisions() and a specific revision like this: fields.getRevision("sig1") (provided that there's a signature field named "sig1").
Suppose that the image represents your document, and you have to remove 1 signature, then you can only remove the third signature by removing all the bytes that were added in revision 3 (Rev3). With iText, that means going back to revision 2 (Rev2). That revision was signed using the signature field sig2.
You can extract this revision like this:
FileOutputStream os = new FileOutputStream("revision2.pdf");
byte bb[] = new byte[1028];
InputStream ip = fields.extractRevision("sig2");
int n = 0;
while ((n = ip.read(bb)) > 0)
os.write(bb, 0, n);
os.close();
ip.close();
The file revision2.pdf will be the file signed by "sig1" and "sig2" without the bytes that were added when creating "sig3".

Sign CAdES using BouncyCastle using JAVA

According to several posts I've found out it's now possible to perform CAdES using BouncyCastle but there is hardly any documentation on the topic.
For starters I want to perform CAdES-BES without any optional signed attributes on a file with a file based certificate.
In response to dander:
I have something that might be helpful, you have your SignerInformation, you need to extend it, first you need to create an attribute from the timestamp, I'll assume you already have a TimeStampResponse as tspResp
TimeStampToken token = tsresp.getTimeStampToken();
Attribute timeStamp = new Attribute(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken, new DERSet(ASN1Object.fromByteArray(token.getEncoded())));
Then you need to extend your SignerInformation
AttributeTable unsigned = signerInformation.getUnsignedAttributes();
Hashtable<ASN1ObjectIdentifier, Attribute> unsignedAttrHash = null;
if (unsigned == null) {
unsignedAttrHash = new Hashtable<ASN1ObjectIdentifier, Attribute>();
} else {
unsignedAttrHash = signerInformation.getUnsignedAttributes().toHashtable();
}
unsignedAttrHash.put(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken, signatureTimeStamp);
SignerInformation newsi = SignerInformation.replaceUnsignedAttributes(si, new AttributeTable(
unsignedAttrHash));
I think that's about it.
Here is how I got the signin-certificate attribute
Attribute signingCertificateAttribute;
MessageDigest dig = MessageDigest.getInstance(DigestAlgorithm().getName(),
new BouncyCastleProvider());
byte[] certHash = dig.digest(SigningCertificate().getEncoded());
if (DigestAlgorithm() == DigestAlgorithm.SHA1) {
SigningCertificate sc = new SigningCertificate(new ESSCertID(certHash));
signingCertificateAttribute = new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificate, new DERSet(sc));
} else {
ESSCertIDv2 essCert = new ESSCertIDv2(new AlgorithmIdentifier(DigestAlgorithm().getOid()), certHash);
SigningCertificateV2 scv2 = new SigningCertificateV2(new ESSCertIDv2[] { essCert });
signingCertificateAttribute = new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificateV2, new DERSet(scv2));
}
Hope it helps
CAdES is an extension of CMS (aka PKCS7), which is possible to do with BouncyCastle. RFC5126 contains everything needed for a CAdES signature, also, I recommend lookup info on ASN.1 since most of the parts are described in that format.
I am currently in hunt for the same answer you are looking for and found that the book Beginning Cryptography with Java by David Hook gives a lot of detailed information you might need.
Useful code could be found on "https://joinup.ec.europa.eu/"
Take a look on CAdESProfileBES.java.
Someone put the same code on Fork of the original SD - Digital Signature Service.

Categories