KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > pdfbox > pdmodel > encryption > PublicKeySecurityHandler


1 /**
2  * Copyright (c) 2003-2006, www.pdfbox.org
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  * 3. Neither the name of pdfbox; nor the names of its
14  * contributors may be used to endorse or promote products derived from this
15  * software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * http://www.pdfbox.org
29  *
30  */

31
32 package org.pdfbox.pdmodel.encryption;
33
34 import java.io.ByteArrayInputStream JavaDoc;
35 import java.io.ByteArrayOutputStream JavaDoc;
36 import java.io.IOException JavaDoc;
37 import java.security.AlgorithmParameterGenerator JavaDoc;
38 import java.security.AlgorithmParameters JavaDoc;
39 import java.security.GeneralSecurityException JavaDoc;
40 import java.security.KeyStoreException JavaDoc;
41 import java.security.MessageDigest JavaDoc;
42 import java.security.NoSuchAlgorithmException JavaDoc;
43 import java.security.NoSuchProviderException JavaDoc;
44 import java.security.SecureRandom JavaDoc;
45 import java.security.Security JavaDoc;
46 import java.security.cert.X509Certificate JavaDoc;
47 import java.util.Iterator JavaDoc;
48
49 import javax.crypto.Cipher;
50 import javax.crypto.KeyGenerator;
51 import javax.crypto.SecretKey;
52
53 import org.bouncycastle.asn1.ASN1InputStream;
54 import org.bouncycastle.asn1.DERObject;
55 import org.bouncycastle.asn1.DERObjectIdentifier;
56 import org.bouncycastle.asn1.DEROctetString;
57 import org.bouncycastle.asn1.DEROutputStream;
58 import org.bouncycastle.asn1.DERSet;
59 import org.bouncycastle.asn1.cms.ContentInfo;
60 import org.bouncycastle.asn1.cms.EncryptedContentInfo;
61 import org.bouncycastle.asn1.cms.EnvelopedData;
62 import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
63 import org.bouncycastle.asn1.cms.KeyTransRecipientInfo;
64 import org.bouncycastle.asn1.cms.RecipientIdentifier;
65 import org.bouncycastle.asn1.cms.RecipientInfo;
66 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
67 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
68 import org.bouncycastle.asn1.x509.TBSCertificateStructure;
69 import org.bouncycastle.cms.CMSEnvelopedData;
70 import org.bouncycastle.cms.CMSException;
71 import org.bouncycastle.cms.RecipientInformation;
72 import org.bouncycastle.jce.provider.BouncyCastleProvider;
73 import org.pdfbox.cos.COSString;
74 import org.pdfbox.exceptions.CryptographyException;
75 import org.pdfbox.pdmodel.PDDocument;
76
77 /**
78  * This class implements the public key security handler
79  * described in the PDF specification.
80  *
81  * @see PDF Spec 1.6 p104
82  *
83  * @see PublicKeyProtectionPolicy to see how to protect document with this security handler.
84  *
85  * @author Benoit Guillon (benoit.guillon@snv.jussieu.fr)
86  * @version $Revision: 1.3 $
87  */

88 public class PublicKeySecurityHandler extends SecurityHandler
89 {
90     
91     /**
92      * The filter name.
93      */

94     public static final String JavaDoc FILTER = "Adobe.PubSec";
95     
96     private static final String JavaDoc SUBFILTER = "adbe.pkcs7.s4";
97     
98     private PublicKeyProtectionPolicy policy = null;
99     
100     /**
101      * Constructor.
102      */

103     public PublicKeySecurityHandler()
104     {
105     }
106     
107     /**
108      * Constructor used for encryption.
109      *
110      * @param p The protection policy.
111      */

112     public PublicKeySecurityHandler(PublicKeyProtectionPolicy p)
113     {
114         policy = p;
115         this.keyLength = policy.getEncryptionKeyLength();
116     }
117     
118     /**
119      * Decrypt the document.
120      *
121      * @param doc The document to decrypt.
122      * @param decryptionMaterial The data used to decrypt the document.
123      *
124      * @throws CryptographyException If there is an error during decryption.
125      * @throws IOException If there is an error accessing data.
126      */

127     public void decryptDocument(PDDocument doc, DecryptionMaterial decryptionMaterial)
128         throws CryptographyException, IOException JavaDoc
129     {
130         this.document = doc;
131         
132         PDEncryptionDictionary dictionary = doc.getEncryptionDictionary();
133         
134         if(dictionary.getLength() != 0)
135         {
136             this.keyLength = dictionary.getLength();
137         }
138         
139         if(!(decryptionMaterial instanceof PublicKeyDecryptionMaterial))
140         {
141             throw new CryptographyException(
142                 "Provided decryption material is not compatible with the document");
143         }
144         
145         PublicKeyDecryptionMaterial material = (PublicKeyDecryptionMaterial)decryptionMaterial;
146         
147         try
148         {
149             boolean foundRecipient = false;
150         
151             // the decrypted content of the enveloped data that match
152
// the certificate in the decryption material provided
153
byte[] envelopedData = null;
154             
155             // the bytes of each recipient in the recipients array
156
byte[][] recipientFieldsBytes = new byte[dictionary.getRecipientsLength()][];
157             
158             int recipientFieldsLength = 0;
159             
160             for(int i=0; i<dictionary.getRecipientsLength(); i++)
161             {
162                 COSString recipientFieldString = dictionary.getRecipientStringAt(i);
163                 byte[] recipientBytes = recipientFieldString.getBytes();
164                 CMSEnvelopedData data = new CMSEnvelopedData(recipientBytes);
165                 Iterator JavaDoc recipCertificatesIt = data.getRecipientInfos().getRecipients().iterator();
166                 while(recipCertificatesIt.hasNext())
167                 {
168                     RecipientInformation ri =
169                         (RecipientInformation)recipCertificatesIt.next();
170                     // Impl: if a matching certificate was previously found it is an error,
171
// here we just don't care about it
172
if(ri.getRID().match(material.getCertificate()) && !foundRecipient)
173                     {
174                         foundRecipient = true;
175                         envelopedData = ri.getContent(material.getPrivateKey(), "BC");
176                     }
177                 }
178                 recipientFieldsBytes[i] = recipientBytes;
179                 recipientFieldsLength += recipientBytes.length;
180             }
181             if(!foundRecipient || envelopedData == null)
182             {
183                 throw new CryptographyException("The certificate matches no recipient entry");
184             }
185             if(envelopedData.length != 24)
186             {
187                 throw new CryptographyException("The enveloped data does not contain 24 bytes");
188             }
189             // now envelopedData contains:
190
// - the 20 bytes seed
191
// - the 4 bytes of permission for the current user
192

193             byte[] accessBytes = new byte[4];
194             System.arraycopy(envelopedData, 20, accessBytes, 0, 4);
195             
196             currentAccessPermission = new AccessPermission(accessBytes);
197             currentAccessPermission.setReadOnly();
198             
199              // what we will put in the SHA1 = the seed + each byte contained in the recipients array
200
byte[] sha1Input = new byte[recipientFieldsLength + 20];
201             
202             // put the seed in the sha1 input
203
System.arraycopy(envelopedData, 0, sha1Input, 0, 20);
204             
205             // put each bytes of the recipients array in the sha1 input
206
int sha1InputOffset = 20;
207             for(int i=0; i<recipientFieldsBytes.length; i++)
208             {
209                 System.arraycopy(
210                     recipientFieldsBytes[i], 0,
211                     sha1Input, sha1InputOffset, recipientFieldsBytes[i].length);
212                 sha1InputOffset += recipientFieldsBytes[i].length;
213             }
214             
215             MessageDigest JavaDoc md = MessageDigest.getInstance("SHA-1");
216             byte[] mdResult = md.digest(sha1Input);
217             
218             // we have the encryption key ...
219
encryptionKey = new byte[this.keyLength/8];
220             System.arraycopy(mdResult, 0, encryptionKey, 0, this.keyLength/8);
221             
222             proceedDecryption();
223             
224             
225         }
226         catch(CMSException e)
227         {
228             throw new CryptographyException(e);
229         }
230         catch(KeyStoreException JavaDoc e)
231         {
232             throw new CryptographyException(e);
233         }
234         catch(NoSuchProviderException JavaDoc e)
235         {
236             throw new CryptographyException(e);
237         }
238         catch(NoSuchAlgorithmException JavaDoc e)
239         {
240             throw new CryptographyException(e);
241         }
242         
243     }
244   
245     /**
246      * Prepare the document for encryption.
247      *
248      * @param doc The document that will be encrypted.
249      *
250      * @throws CryptographyException If there is an error while encrypting.
251      */

252     public void prepareDocumentForEncryption(PDDocument doc) throws CryptographyException
253     {
254         
255         try
256         {
257             Security.addProvider(new BouncyCastleProvider());
258             
259             PDEncryptionDictionary dictionary = doc.getEncryptionDictionary();
260             
261             dictionary.setFilter(FILTER);
262             dictionary.setLength(this.keyLength);
263             dictionary.setVersion(2);
264             dictionary.setSubFilter(SUBFILTER);
265             
266             byte[][] recipientsField = new byte[policy.getRecipientsNumber()][];
267                         
268             // create the 20 bytes seed
269

270             byte[] seed = new byte[20];
271
272             KeyGenerator key = KeyGenerator.getInstance("AES");
273             key.init(192, new SecureRandom JavaDoc());
274             SecretKey sk = key.generateKey();
275             System.arraycopy(sk.getEncoded(), 0, seed, 0, 20); // create the 20 bytes seed
276

277             
278             Iterator JavaDoc it = policy.getRecipientsIterator();
279             int i = 0;
280             
281             
282             while(it.hasNext())
283             {
284                 PublicKeyRecipient recipient = (PublicKeyRecipient)it.next();
285                 X509Certificate JavaDoc certificate = recipient.getX509();
286                 int permission = recipient.getPermission().getPermissionBytesForPublicKey();
287             
288                 byte[] pkcs7input = new byte[24];
289                 byte one = (byte)(permission);
290                 byte two = (byte)(permission >>> 8);
291                 byte three = (byte)(permission >>> 16);
292                 byte four = (byte)(permission >>> 24);
293                             
294                 System.arraycopy(seed, 0, pkcs7input, 0, 20); // put this seed in the pkcs7 input
295

296                 pkcs7input[20] = four;
297                 pkcs7input[21] = three;
298                 pkcs7input[22] = two;
299                 pkcs7input[23] = one;
300                     
301                 DERObject obj = createDERForRecipient(pkcs7input, certificate);
302                     
303                 ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
304                     
305                 DEROutputStream k = new DEROutputStream(baos);
306                     
307                 k.writeObject(obj);
308                     
309                 recipientsField[i] = baos.toByteArray();
310                     
311                 i++;
312             }
313
314             dictionary.setRecipients(recipientsField);
315             
316             int sha1InputLength = seed.length;
317             
318             for(int j=0; j<dictionary.getRecipientsLength(); j++)
319             {
320                 COSString string = dictionary.getRecipientStringAt(j);
321                 sha1InputLength += string.getBytes().length;
322             }
323                         
324                         
325             byte[] sha1Input = new byte[sha1InputLength];
326             
327             System.arraycopy(seed, 0, sha1Input, 0, 20);
328             
329             int sha1InputOffset = 20;
330
331             
332             for(int j=0; j<dictionary.getRecipientsLength(); j++)
333             {
334                 COSString string = dictionary.getRecipientStringAt(j);
335                 System.arraycopy(
336                     string.getBytes(), 0,
337                     sha1Input, sha1InputOffset, string.getBytes().length);
338                 sha1InputOffset += string.getBytes().length;
339             }
340             
341             MessageDigest JavaDoc md = MessageDigest.getInstance("SHA-1");
342             
343             byte[] mdResult = md.digest(sha1Input);
344             
345             this.encryptionKey = new byte[this.keyLength/8];
346             System.arraycopy(mdResult, 0, this.encryptionKey, 0, this.keyLength/8);
347             
348             doc.setEncryptionDictionary(dictionary);
349             doc.getDocument().setEncryptionDictionary(dictionary.encryptionDictionary);
350             
351         }
352         catch(NoSuchAlgorithmException JavaDoc ex)
353         {
354             throw new CryptographyException(ex);
355         }
356         catch(NoSuchProviderException JavaDoc ex)
357         {
358             throw new CryptographyException(ex);
359         }
360         catch(Exception JavaDoc e)
361         {
362             e.printStackTrace();
363             throw new CryptographyException(e);
364         }
365         
366     }
367     
368     private DERObject createDERForRecipient(byte[] in, X509Certificate JavaDoc cert)
369         throws IOException JavaDoc,
370                GeneralSecurityException JavaDoc
371     {
372         
373         String JavaDoc s = "1.2.840.113549.3.2";
374         
375         AlgorithmParameterGenerator JavaDoc algorithmparametergenerator = AlgorithmParameterGenerator.getInstance(s);
376         AlgorithmParameters JavaDoc algorithmparameters = algorithmparametergenerator.generateParameters();
377         ByteArrayInputStream JavaDoc bytearrayinputstream = new ByteArrayInputStream JavaDoc(algorithmparameters.getEncoded("ASN.1"));
378         ASN1InputStream asn1inputstream = new ASN1InputStream(bytearrayinputstream);
379         DERObject derobject = asn1inputstream.readObject();
380         KeyGenerator keygenerator = KeyGenerator.getInstance(s);
381         keygenerator.init(128);
382         SecretKey secretkey = keygenerator.generateKey();
383         Cipher cipher = Cipher.getInstance(s);
384         cipher.init(1, secretkey, algorithmparameters);
385         byte[] abyte1 = cipher.doFinal(in);
386         DEROctetString deroctetstring = new DEROctetString(abyte1);
387         KeyTransRecipientInfo keytransrecipientinfo = computeRecipientInfo(cert, secretkey.getEncoded());
388         DERSet derset = new DERSet(new RecipientInfo(keytransrecipientinfo));
389         AlgorithmIdentifier algorithmidentifier = new AlgorithmIdentifier(new DERObjectIdentifier(s), derobject);
390         EncryptedContentInfo encryptedcontentinfo =
391             new EncryptedContentInfo(PKCSObjectIdentifiers.data, algorithmidentifier, deroctetstring);
392         EnvelopedData env = new EnvelopedData(null, derset, encryptedcontentinfo, null);
393         ContentInfo contentinfo =
394             new ContentInfo(PKCSObjectIdentifiers.envelopedData, env);
395         return contentinfo.getDERObject();
396     }
397     
398     private KeyTransRecipientInfo computeRecipientInfo(X509Certificate JavaDoc x509certificate, byte[] abyte0)
399         throws GeneralSecurityException JavaDoc, IOException JavaDoc
400     {
401         ASN1InputStream asn1inputstream =
402             new ASN1InputStream(new ByteArrayInputStream JavaDoc(x509certificate.getTBSCertificate()));
403         TBSCertificateStructure tbscertificatestructure =
404             TBSCertificateStructure.getInstance(asn1inputstream.readObject());
405         AlgorithmIdentifier algorithmidentifier = tbscertificatestructure.getSubjectPublicKeyInfo().getAlgorithmId();
406         IssuerAndSerialNumber issuerandserialnumber =
407             new IssuerAndSerialNumber(
408                 tbscertificatestructure.getIssuer(),
409                 tbscertificatestructure.getSerialNumber().getValue());
410         Cipher cipher = Cipher.getInstance(algorithmidentifier.getObjectId().getId());
411         cipher.init(1, x509certificate.getPublicKey());
412         DEROctetString deroctetstring = new DEROctetString(cipher.doFinal(abyte0));
413         RecipientIdentifier recipId = new RecipientIdentifier(issuerandserialnumber);
414         return new KeyTransRecipientInfo( recipId, algorithmidentifier, deroctetstring);
415     }
416         
417 }
418
Popular Tags