Skip to content

Commit 17d6ae3

Browse files
committed
Add options to append page, page number for signature and template for signature text
1 parent 377cc18 commit 17d6ae3

File tree

4 files changed

+442
-16
lines changed

4 files changed

+442
-16
lines changed

pdfsign/LTV/AdobeLtvEnabling.cs

Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
using iTextSharp.text;
2+
using iTextSharp.text.error_messages;
3+
using iTextSharp.text.pdf;
4+
using iTextSharp.text.pdf.security;
5+
using Org.BouncyCastle.Asn1;
6+
using Org.BouncyCastle.Asn1.Ocsp;
7+
using Org.BouncyCastle.Asn1.X509;
8+
using Org.BouncyCastle.Ocsp;
9+
using Org.BouncyCastle.X509;
10+
using System;
11+
using System.Collections.Generic;
12+
using System.IO;
13+
using System.Net;
14+
using System.Security.Cryptography;
15+
using System.Text;
16+
17+
namespace pdfsign
18+
{
19+
class AdobeLtvEnabling
20+
{
21+
22+
//
23+
// member variables
24+
//
25+
PdfStamper pdfStamper;
26+
ISet<X509Certificate> seenCertificates = new HashSet<X509Certificate>();
27+
IDictionary<PdfName, ValidationData> validated = new Dictionary<PdfName, ValidationData>();
28+
29+
public static List<X509Certificate> extraCertificates = new List<X509Certificate>();
30+
31+
/**
32+
* Use this constructor with a {@link PdfStamper} in append mode. Otherwise
33+
* the existing signatures will be damaged.
34+
*/
35+
public AdobeLtvEnabling(PdfStamper pdfStamper)
36+
{
37+
this.pdfStamper = pdfStamper;
38+
}
39+
40+
/**
41+
* Call this method to have LTV information added to the {@link PdfStamper}
42+
* given in the constructor.
43+
*/
44+
public void enable(IOcspClient ocspClient, ICrlClient crlClient)
45+
{
46+
AcroFields fields = pdfStamper.AcroFields;
47+
bool encrypted = pdfStamper.Reader.IsEncrypted();
48+
49+
List<String> names = fields.GetSignatureNames();
50+
foreach (String name in names)
51+
{
52+
PdfPKCS7 pdfPKCS7 = fields.VerifySignature(name);
53+
PdfDictionary signatureDictionary = fields.GetSignatureDictionary(name);
54+
X509Certificate certificate = pdfPKCS7.SigningCertificate;
55+
addLtvForChain(certificate, ocspClient, crlClient, getSignatureHashKey(signatureDictionary, encrypted));
56+
}
57+
58+
outputDss();
59+
}
60+
61+
//
62+
// the actual LTV enabling methods
63+
//
64+
void addLtvForChain(X509Certificate certificate, IOcspClient ocspClient, ICrlClient crlClient, PdfName key)
65+
{
66+
if (seenCertificates.Contains(certificate))
67+
return;
68+
seenCertificates.Add(certificate);
69+
ValidationData validationData = new ValidationData();
70+
71+
while (certificate != null)
72+
{
73+
Console.WriteLine(certificate.SubjectDN);
74+
X509Certificate issuer = getIssuerCertificate(certificate);
75+
validationData.certs.Add(certificate.GetEncoded());
76+
byte[] ocspResponse = ocspClient.GetEncoded(certificate, issuer, null);
77+
if (ocspResponse != null)
78+
{
79+
Console.WriteLine(" with OCSP response");
80+
validationData.ocsps.Add(ocspResponse);
81+
X509Certificate ocspSigner = getOcspSignerCertificate(ocspResponse);
82+
if (ocspSigner != null)
83+
{
84+
Console.WriteLine(" signed by {0}\n", ocspSigner.SubjectDN);
85+
}
86+
addLtvForChain(ocspSigner, ocspClient, crlClient, getOcspHashKey(ocspResponse));
87+
}
88+
else
89+
{
90+
ICollection<byte[]> crl = crlClient.GetEncoded(certificate, null);
91+
if (crl != null && crl.Count > 0)
92+
{
93+
Console.WriteLine(" with {0} CRLs\n", crl.Count);
94+
foreach (byte[] crlBytes in crl)
95+
{
96+
validationData.crls.Add(crlBytes);
97+
addLtvForChain(null, ocspClient, crlClient, getCrlHashKey(crlBytes));
98+
}
99+
}
100+
}
101+
certificate = issuer;
102+
}
103+
104+
validated[key] = validationData;
105+
}
106+
107+
void outputDss()
108+
{
109+
PdfWriter writer = pdfStamper.Writer;
110+
PdfReader reader = pdfStamper.Reader;
111+
112+
PdfDictionary dss = new PdfDictionary();
113+
PdfDictionary vrim = new PdfDictionary();
114+
PdfArray ocsps = new PdfArray();
115+
PdfArray crls = new PdfArray();
116+
PdfArray certs = new PdfArray();
117+
118+
writer.AddDeveloperExtension(PdfDeveloperExtension.ESIC_1_7_EXTENSIONLEVEL5);
119+
writer.AddDeveloperExtension(new PdfDeveloperExtension(PdfName.ADBE, new PdfName("1.7"), 8));
120+
121+
PdfDictionary catalog = reader.Catalog;
122+
pdfStamper.MarkUsed(catalog);
123+
foreach (PdfName vkey in validated.Keys)
124+
{
125+
PdfArray ocsp = new PdfArray();
126+
PdfArray crl = new PdfArray();
127+
PdfArray cert = new PdfArray();
128+
PdfDictionary vri = new PdfDictionary();
129+
foreach (byte[] b in validated[vkey].crls)
130+
{
131+
PdfStream ps = new PdfStream(b);
132+
ps.FlateCompress();
133+
PdfIndirectReference iref = writer.AddToBody(ps, false).IndirectReference;
134+
crl.Add(iref);
135+
crls.Add(iref);
136+
}
137+
foreach (byte[] b in validated[vkey].ocsps)
138+
{
139+
PdfStream ps = new PdfStream(buildOCSPResponse(b));
140+
ps.FlateCompress();
141+
PdfIndirectReference iref = writer.AddToBody(ps, false).IndirectReference;
142+
ocsp.Add(iref);
143+
ocsps.Add(iref);
144+
}
145+
foreach (byte[] b in validated[vkey].certs)
146+
{
147+
PdfStream ps = new PdfStream(b);
148+
ps.FlateCompress();
149+
PdfIndirectReference iref = writer.AddToBody(ps, false).IndirectReference;
150+
cert.Add(iref);
151+
certs.Add(iref);
152+
}
153+
if (ocsp.Length > 0)
154+
vri.Put(PdfName.OCSP, writer.AddToBody(ocsp, false).IndirectReference);
155+
if (crl.Length > 0)
156+
vri.Put(PdfName.CRL, writer.AddToBody(crl, false).IndirectReference);
157+
if (cert.Length > 0)
158+
vri.Put(PdfName.CERT, writer.AddToBody(cert, false).IndirectReference);
159+
vri.Put(PdfName.TU, new PdfDate());
160+
vrim.Put(vkey, writer.AddToBody(vri, false).IndirectReference);
161+
}
162+
dss.Put(PdfName.VRI, writer.AddToBody(vrim, false).IndirectReference);
163+
if (ocsps.Length > 0)
164+
dss.Put(PdfName.OCSPS, writer.AddToBody(ocsps, false).IndirectReference);
165+
if (crls.Length > 0)
166+
dss.Put(PdfName.CRLS, writer.AddToBody(crls, false).IndirectReference);
167+
if (certs.Length > 0)
168+
dss.Put(PdfName.CERTS, writer.AddToBody(certs, false).IndirectReference);
169+
catalog.Put(PdfName.DSS, writer.AddToBody(dss, false).IndirectReference);
170+
}
171+
172+
//
173+
// VRI signature hash key calculation
174+
//
175+
static PdfName getCrlHashKey(byte[] crlBytes)
176+
{
177+
X509Crl crl = new X509Crl(CertificateList.GetInstance(crlBytes));
178+
byte[] signatureBytes = crl.GetSignature();
179+
DerOctetString octetString = new DerOctetString(signatureBytes);
180+
byte[] octetBytes = octetString.GetEncoded();
181+
byte[] octetHash = hashBytesSha1(octetBytes);
182+
PdfName octetName = new PdfName(Utilities.ConvertToHex(octetHash));
183+
return octetName;
184+
}
185+
186+
static PdfName getOcspHashKey(byte[] basicResponseBytes)
187+
{
188+
BasicOcspResponse basicResponse = BasicOcspResponse.GetInstance(Asn1Sequence.GetInstance(basicResponseBytes));
189+
byte[] signatureBytes = basicResponse.Signature.GetBytes();
190+
DerOctetString octetString = new DerOctetString(signatureBytes);
191+
byte[] octetBytes = octetString.GetEncoded();
192+
byte[] octetHash = hashBytesSha1(octetBytes);
193+
PdfName octetName = new PdfName(Utilities.ConvertToHex(octetHash));
194+
return octetName;
195+
}
196+
197+
static PdfName getSignatureHashKey(PdfDictionary dic, bool encrypted)
198+
{
199+
PdfString contents = dic.GetAsString(PdfName.CONTENTS);
200+
byte[] bc = contents.GetOriginalBytes();
201+
if (PdfName.ETSI_RFC3161.Equals(PdfReader.GetPdfObject(dic.Get(PdfName.SUBFILTER))))
202+
{
203+
using (Asn1InputStream din = new Asn1InputStream(bc))
204+
{
205+
Asn1Object pkcs = din.ReadObject();
206+
bc = pkcs.GetEncoded();
207+
}
208+
}
209+
byte[] bt = hashBytesSha1(bc);
210+
return new PdfName(Utilities.ConvertToHex(bt));
211+
}
212+
213+
static byte[] hashBytesSha1(byte[] b)
214+
{
215+
SHA1 sha = new SHA1CryptoServiceProvider();
216+
return sha.ComputeHash(b);
217+
}
218+
219+
//
220+
// OCSP response helpers
221+
//
222+
static X509Certificate getOcspSignerCertificate(byte[] basicResponseBytes)
223+
{
224+
BasicOcspResponse borRaw = BasicOcspResponse.GetInstance(Asn1Sequence.GetInstance(basicResponseBytes));
225+
BasicOcspResp bor = new BasicOcspResp(borRaw);
226+
227+
foreach (X509Certificate x509Certificate in bor.GetCerts())
228+
{
229+
if (bor.Verify(x509Certificate.GetPublicKey()))
230+
return x509Certificate;
231+
}
232+
233+
return null;
234+
}
235+
236+
static byte[] buildOCSPResponse(byte[] BasicOCSPResponse)
237+
{
238+
DerOctetString doctet = new DerOctetString(BasicOCSPResponse);
239+
Asn1EncodableVector v2 = new Asn1EncodableVector();
240+
v2.Add(OcspObjectIdentifiers.PkixOcspBasic);
241+
v2.Add(doctet);
242+
DerEnumerated den = new DerEnumerated(0);
243+
Asn1EncodableVector v3 = new Asn1EncodableVector();
244+
v3.Add(den);
245+
v3.Add(new DerTaggedObject(true, 0, new DerSequence(v2)));
246+
DerSequence seq = new DerSequence(v3);
247+
return seq.GetEncoded();
248+
}
249+
250+
//
251+
// X509 certificate related helpers
252+
//
253+
static X509Certificate getIssuerCertificate(X509Certificate certificate)
254+
{
255+
String url = getCACURL(certificate);
256+
if (url != null && url.Length > 0)
257+
{
258+
HttpWebRequest con = (HttpWebRequest)WebRequest.Create(url);
259+
HttpWebResponse response = (HttpWebResponse)con.GetResponse();
260+
if (response.StatusCode != HttpStatusCode.OK)
261+
throw new IOException(MessageLocalization.GetComposedMessage("invalid.http.response.1", (int)response.StatusCode));
262+
//Get Response
263+
Stream inp = response.GetResponseStream();
264+
byte[] buf = new byte[1024];
265+
MemoryStream bout = new MemoryStream();
266+
while (true)
267+
{
268+
int n = inp.Read(buf, 0, buf.Length);
269+
if (n <= 0)
270+
break;
271+
bout.Write(buf, 0, n);
272+
}
273+
inp.Close();
274+
275+
var cert2 = new System.Security.Cryptography.X509Certificates.X509Certificate2(bout.ToArray());
276+
277+
return new X509Certificate(X509CertificateStructure.GetInstance(cert2.GetRawCertData()));
278+
}
279+
280+
try
281+
{
282+
certificate.Verify(certificate.GetPublicKey());
283+
return null;
284+
}
285+
catch (Exception e)
286+
{
287+
}
288+
289+
foreach (X509Certificate candidate in extraCertificates)
290+
{
291+
try
292+
{
293+
certificate.Verify(candidate.GetPublicKey());
294+
return candidate;
295+
}
296+
catch (Exception e)
297+
{
298+
}
299+
}
300+
301+
return null;
302+
}
303+
304+
static String getCACURL(X509Certificate certificate)
305+
{
306+
try
307+
{
308+
Asn1Object obj = getExtensionValue(certificate, X509Extensions.AuthorityInfoAccess.Id);
309+
if (obj == null)
310+
{
311+
return null;
312+
}
313+
314+
Asn1Sequence AccessDescriptions = (Asn1Sequence)obj;
315+
for (int i = 0; i < AccessDescriptions.Count; i++)
316+
{
317+
Asn1Sequence AccessDescription = (Asn1Sequence)AccessDescriptions[i];
318+
if (AccessDescription.Count != 2)
319+
{
320+
continue;
321+
}
322+
else
323+
{
324+
if ((AccessDescription[0] is DerObjectIdentifier) && ((DerObjectIdentifier)AccessDescription[0]).Id.Equals("1.3.6.1.5.5.7.48.2"))
325+
{
326+
String AccessLocation = getStringFromGeneralName((Asn1Object)AccessDescription[1]);
327+
return AccessLocation == null ? "" : AccessLocation;
328+
}
329+
}
330+
}
331+
}
332+
catch
333+
{
334+
}
335+
return null;
336+
}
337+
338+
static Asn1Object getExtensionValue(X509Certificate certificate, String oid)
339+
{
340+
byte[] bytes = certificate.GetExtensionValue(new DerObjectIdentifier(oid)).GetDerEncoded();
341+
if (bytes == null)
342+
{
343+
return null;
344+
}
345+
Asn1InputStream aIn = new Asn1InputStream(new MemoryStream(bytes));
346+
Asn1OctetString octs = (Asn1OctetString)aIn.ReadObject();
347+
aIn = new Asn1InputStream(new MemoryStream(octs.GetOctets()));
348+
return aIn.ReadObject();
349+
}
350+
351+
private static String getStringFromGeneralName(Asn1Object names)
352+
{
353+
Asn1TaggedObject taggedObject = (Asn1TaggedObject)names;
354+
return Encoding.GetEncoding(1252).GetString(Asn1OctetString.GetInstance(taggedObject, false).GetOctets());
355+
}
356+
357+
//
358+
// inner class
359+
//
360+
class ValidationData
361+
{
362+
public IList<byte[]> crls = new List<byte[]>();
363+
public IList<byte[]> ocsps = new List<byte[]>();
364+
public IList<byte[]> certs = new List<byte[]>();
365+
}
366+
367+
}
368+
}

pdfsign/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@
3232
// You can specify all the values or you can default the Build and Revision Numbers
3333
// by using the '*' as shown below:
3434
// [assembly: AssemblyVersion("1.0.*")]
35-
[assembly: AssemblyVersion("1.2.0.0")]
36-
[assembly: AssemblyFileVersion("1.2.0.0")]
35+
[assembly: AssemblyVersion("1.3.0.0")]
36+
[assembly: AssemblyFileVersion("1.3.0.0")]

0 commit comments

Comments
 (0)