001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.pdf.pd;
031:
032: import java.util.ArrayList;
033: import java.util.Iterator;
034: import java.util.List;
035:
036: import de.intarsys.pdf.cds.CDSDate;
037: import de.intarsys.pdf.cos.COSArray;
038: import de.intarsys.pdf.cos.COSBasedObject;
039: import de.intarsys.pdf.cos.COSDocumentElement;
040: import de.intarsys.pdf.cos.COSName;
041: import de.intarsys.pdf.cos.COSObject;
042: import de.intarsys.pdf.cos.COSObjectKey;
043: import de.intarsys.pdf.cos.COSObjectProxy;
044: import de.intarsys.pdf.cos.COSRuntimeException;
045: import de.intarsys.pdf.cos.COSString;
046: import de.intarsys.pdf.crypt.COSSecurityException;
047: import de.intarsys.pdf.crypt.ISystemSecurityHandler;
048:
049: /**
050: * This class represents the signature object referenced for example in an
051: * AcroForm signature field.
052: */
053: public class PDSignature extends PDObject {
054: /**
055: * The meta class implementation
056: */
057: public static class MetaClass extends PDObject.MetaClass {
058: protected MetaClass(Class instanceClass) {
059: super (instanceClass);
060: }
061:
062: protected COSBasedObject doCreateCOSBasedObject(COSObject object) {
063: return new PDSignature(object);
064: }
065: }
066:
067: /** The meta class instance */
068: public static final MetaClass META = new MetaClass(MetaClass.class
069: .getDeclaringClass());
070:
071: public static final COSName CN_Type_Sig = COSName.constant("Sig"); //$NON-NLS-1$
072:
073: public static final COSName DK_Filter = COSName.constant("Filter"); //$NON-NLS-1$
074:
075: public static final COSName DK_SubFilter = COSName
076: .constant("SubFilter"); //$NON-NLS-1$
077:
078: public static final COSName DK_Contents = COSName
079: .constant("Contents"); //$NON-NLS-1$
080: public static final COSName DK_Cert = COSName.constant("Cert"); //$NON-NLS-1$
081:
082: public static final COSName DK_ByteRange = COSName
083: .constant("ByteRange"); //$NON-NLS-1$
084:
085: public static final COSName DK_Reference = COSName
086: .constant("Reference"); //$NON-NLS-1$
087:
088: public static final COSName DK_Changes = COSName
089: .constant("Changes"); //$NON-NLS-1$
090:
091: public static final COSName DK_Name = COSName.constant("Name"); //$NON-NLS-1$
092:
093: public static final COSName DK_M = COSName.constant("M"); //$NON-NLS-1$
094:
095: public static final COSName DK_Location = COSName
096: .constant("Location"); //$NON-NLS-1$
097:
098: public static final COSName DK_Reason = COSName.constant("Reason"); //$NON-NLS-1$
099:
100: public static final COSName DK_ContactInfo = COSName
101: .constant("ContactInfo"); //$NON-NLS-1$
102:
103: public static final COSName DK_R = COSName.constant("R"); //$NON-NLS-1$
104:
105: public static final COSName DK_V = COSName.constant("V"); //$NON-NLS-1$
106:
107: public static final COSName DK_Prop_Build = COSName
108: .constant("Prop_Build"); //$NON-NLS-1$
109:
110: public static final COSName DK_Prop_AuthTime = COSName
111: .constant("Prop_Auth_Time"); //$NON-NLS-1$
112:
113: public static final COSName DK_Prop_AuthType = COSName
114: .constant("Prop_AuthType"); //$NON-NLS-1$
115:
116: private PDAFSignatureField acroFormField;
117:
118: private byte[] decryptedContentBytes;
119:
120: protected PDSignature(COSObject object) {
121: super (object);
122: }
123:
124: /**
125: * @return the byte range for the signature or null
126: */
127: public COSArray cosGetByteRange() {
128: return cosGetField(DK_ByteRange).asArray();
129: }
130:
131: /*
132: * (non-Javadoc)
133: *
134: * @see de.intarsys.pdf.pd.PDObject#cosGetExpectedType()
135: */
136: protected COSName cosGetExpectedType() {
137: return CN_Type_Sig;
138: }
139:
140: public PDAFSignatureField getAcroFormField() {
141: return acroFormField;
142: }
143:
144: public PDBuildProperties getBuildProperties() {
145: return (PDBuildProperties) PDBuildProperties.META
146: .createFromCos(cosGetField(DK_Prop_Build));
147: }
148:
149: /**
150: * <p>
151: * Get a List of certificates, the first one is the certificate of the
152: * signer himself. Followed by certificates of certificate authoritys.
153: * </p>
154: * <p>
155: * A certificate is stored as DER encoded byte[]
156: * </p>
157: *
158: * @return a List of certificates or null
159: */
160: public List getCert() {
161: List result = null;
162: COSObject cert = cosGetField(DK_Cert);
163: if (!cert.isNull()) {
164: result = new ArrayList();
165: if (cert instanceof COSString) {
166: result.add(((COSString) cert).byteValue());
167: return result;
168: }
169: if (cert instanceof COSArray) {
170: COSArray certArray = (COSArray) cert;
171: for (Iterator i = certArray.iterator(); i.hasNext();) {
172: COSString value = ((COSObject) i.next()).asString();
173: if (value != null) {
174: result.add(value.byteValue());
175: }
176: }
177: }
178: }
179: return result;
180: }
181:
182: /**
183: * @return how to contact the signer or null
184: */
185: public String getContactInfo() {
186: return getFieldString(DK_ContactInfo, null);
187: }
188:
189: public byte[] getContentBytes() {
190: COSDocumentElement content = cosGetDict().basicGet(DK_Contents);
191: if (content instanceof COSString) {
192: if (getDoc().isEncrypted()) {
193: return getDecryptedContentBytes((COSString) content);
194: }
195: return ((COSString) content).byteValue();
196: }
197: if (content instanceof COSObjectProxy) {
198: COSString string = content.dereference().asString();
199: return string == null ? null : string.byteValue();
200: }
201: return null;
202: }
203:
204: /**
205: * @return the date the signature took place
206: */
207: public CDSDate getDate() {
208: return CDSDate.createFromCOS(cosGetField(DK_M).asString());
209: }
210:
211: private byte[] getDecryptedContentBytes(COSString content) {
212: if (decryptedContentBytes != null) {
213: return decryptedContentBytes;
214: }
215: // The content was never encrypted, but the parser decrypted it,
216: // so we encrypt it to reverse the decryption.
217: ISystemSecurityHandler securityHandler = cosGetDoc().stGetDoc()
218: .getSystemSecurityHandler();
219: COSObjectKey key = cosGetDict().getIndirectObject().getKey();
220: byte[] contentBytes = content.byteValue();
221: try {
222: decryptedContentBytes = securityHandler.encryptStream(key,
223: cosGetDict(), contentBytes);
224: } catch (COSSecurityException e) {
225: cosGetObject().handleException(
226: new COSRuntimeException(
227: "error reading signature bytes", e)); //$NON-NLS-1$
228: }
229: return decryptedContentBytes;
230: }
231:
232: /**
233: * Filter is a name for the original signature creator, for example:
234: * Adobe.PPKLite
235: *
236: * @return name of the signature creator
237: */
238: public COSName getFilter() {
239: return cosGetField(DK_Filter).asName();
240: }
241:
242: /**
243: * @return where the document was signed or null
244: */
245: public String getLocation() {
246: return getFieldString(DK_Location, null);
247: }
248:
249: /**
250: * @return name of the signer or null
251: */
252: public String getName() {
253: return getFieldString(DK_Name, null);
254: }
255:
256: /**
257: * @return reason for signing this document or null
258: */
259: public String getReason() {
260: return getFieldString(DK_Reason, null);
261: }
262:
263: /**
264: * SubFilter is the name of a encoding and storage algorithm.
265: *
266: * @return the name of the encoding algorithm
267: */
268: public COSName getSubFilter() {
269: return cosGetField(DK_SubFilter).asName();
270: }
271:
272: public void setAcroFormField(PDAFSignatureField acroFormField) {
273: this .acroFormField = acroFormField;
274: }
275:
276: public void setBuildProperties(PDBuildProperties buildProperties) {
277: setFieldObject(DK_Prop_Build, buildProperties);
278: }
279:
280: /**
281: * Sets certificates in the /Cert field.
282: *
283: * @param certificate
284: * a DER encoded byte[]
285: */
286: public void setCert(byte[] certificate) {
287: if (certificate == null) {
288: return;
289: }
290: COSString certString = COSString.createHex(certificate);
291: cosSetField(DK_Cert, certString);
292: }
293:
294: /**
295: * Sets certificates in the /Cert field.
296: *
297: * @param certificates
298: * a list of DER encoded byte[]
299: */
300: public void setCert(List certificates) {
301: if ((certificates == null) || certificates.isEmpty()) {
302: return;
303: }
304: if (certificates.size() == 1) {
305: setCert((byte[]) certificates.get(0));
306: return;
307: }
308: COSArray certList = COSArray.create(certificates.size());
309: for (Iterator i = certificates.iterator(); i.hasNext();) {
310: COSString certString = COSString.createHex((byte[]) i
311: .next());
312: certList.add(certString);
313: }
314: cosSetField(DK_Cert, certList);
315: }
316:
317: /**
318: * @param contactInfo
319: * how to contact the signer, may be null
320: */
321: public void setContactInfo(String contactInfo) {
322: setFieldString(DK_ContactInfo, contactInfo);
323: }
324:
325: public void setDate(CDSDate date) {
326: setFieldObject(DK_M, date);
327: }
328:
329: /**
330: * Set the name of the signature creator, for example: Adobe.PPKLite
331: *
332: * @param filter
333: * name of the signature creator
334: */
335: public void setFilter(COSName filter) {
336: cosSetField(DK_Filter, filter);
337: }
338:
339: /**
340: * @param location
341: * location the signer signed the document, may be null
342: */
343: public void setLocation(String location) {
344: setFieldString(DK_Location, location);
345: }
346:
347: /**
348: * @param name
349: * name of the signer, may be null
350: */
351: public void setName(String name) {
352: setFieldString(DK_Name, name);
353: }
354:
355: /**
356: * @param reason
357: * reason why is document was signed, may be null
358: */
359: public void setReason(String reason) {
360: setFieldString(DK_Reason, reason);
361: }
362:
363: /**
364: * Set the name of the encoding algorithm
365: *
366: * @param subfilter
367: * name of the encoding algorithm
368: */
369: public void setSubFilter(COSName subfilter) {
370: cosSetField(DK_SubFilter, subfilter);
371: }
372: }
|