001/*
002 * $Id: PdfPublicKeySecurityHandler.java 4784 2011-03-15 08:33:00Z blowagie $
003 *
004 * This file is part of the iText (R) project.
005 * Copyright (c) 1998-2011 1T3XT BVBA
006 * Authors: Bruno Lowagie, Paulo Soares, et al.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU Affero General Public License version 3
010 * as published by the Free Software Foundation with the addition of the
011 * following permission added to Section 15 as permitted in Section 7(a):
012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
014 *
015 * This program is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017 * or FITNESS FOR A PARTICULAR PURPOSE.
018 * See the GNU Affero General Public License for more details.
019 * You should have received a copy of the GNU Affero General Public License
020 * along with this program; if not, see http://www.gnu.org/licenses or write to
021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
022 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
023 * http://itextpdf.com/terms-of-use/
024 *
025 * The interactive user interfaces in modified source and object code versions
026 * of this program must display Appropriate Legal Notices, as required under
027 * Section 5 of the GNU Affero General Public License.
028 *
029 * In accordance with Section 7(b) of the GNU Affero General Public License,
030 * a covered work must retain the producer line in every PDF that is created
031 * or manipulated using iText.
032 *
033 * You can be released from the requirements of the license by purchasing
034 * a commercial license. Buying such a license is mandatory as soon as you
035 * develop commercial activities involving the iText software without
036 * disclosing the source code of your own applications.
037 * These activities include: offering paid services to customers as an ASP,
038 * serving PDFs on the fly in a web application, shipping iText with a closed
039 * source product.
040 *
041 * For more information, please contact iText Software Corp. at this
042 * address: sales@itextpdf.com
043 */
044
045/**
046 *     The below 2 methods are from pdfbox.
047 *
048 *     private DERObject createDERForRecipient(byte[] in, X509Certificate cert) ;
049 *     private KeyTransRecipientInfo computeRecipientInfo(X509Certificate x509certificate, byte[] abyte0);
050 *
051 *     2006-11-22 Aiken Sam.
052 */
053
054/**
055 * Copyright (c) 2003-2006, www.pdfbox.org
056 * All rights reserved.
057 *
058 * Redistribution and use in source and binary forms, with or without
059 * modification, are permitted provided that the following conditions are met:
060 *
061 * 1. Redistributions of source code must retain the above copyright notice,
062 *    this list of conditions and the following disclaimer.
063 * 2. Redistributions in binary form must reproduce the above copyright notice,
064 *    this list of conditions and the following disclaimer in the documentation
065 *    and/or other materials provided with the distribution.
066 * 3. Neither the name of pdfbox; nor the names of its
067 *    contributors may be used to endorse or promote products derived from this
068 *    software without specific prior written permission.
069 *
070 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
071 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
072 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
073 * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
074 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
075 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
076 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
077 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
078 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
079 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
080 *
081 * http://www.pdfbox.org
082 *
083 */
084
085package com.itextpdf.text.pdf;
086
087import java.io.ByteArrayInputStream;
088import java.io.ByteArrayOutputStream;
089import java.io.IOException;
090import java.security.AlgorithmParameterGenerator;
091import java.security.AlgorithmParameters;
092import java.security.GeneralSecurityException;
093import java.security.NoSuchAlgorithmException;
094import java.security.SecureRandom;
095import java.security.cert.Certificate;
096import java.security.cert.X509Certificate;
097import java.util.ArrayList;
098
099import javax.crypto.Cipher;
100import javax.crypto.KeyGenerator;
101import javax.crypto.SecretKey;
102
103import org.bouncycastle.asn1.ASN1InputStream;
104import org.bouncycastle.asn1.DERObject;
105import org.bouncycastle.asn1.DERObjectIdentifier;
106import org.bouncycastle.asn1.DEROctetString;
107import org.bouncycastle.asn1.DEROutputStream;
108import org.bouncycastle.asn1.DERSet;
109import org.bouncycastle.asn1.cms.ContentInfo;
110import org.bouncycastle.asn1.cms.EncryptedContentInfo;
111import org.bouncycastle.asn1.cms.EnvelopedData;
112import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
113import org.bouncycastle.asn1.cms.KeyTransRecipientInfo;
114import org.bouncycastle.asn1.cms.RecipientIdentifier;
115import org.bouncycastle.asn1.cms.RecipientInfo;
116import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
117import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
118import org.bouncycastle.asn1.x509.TBSCertificateStructure;
119
120/**
121 * @author Aiken Sam (aikensam@ieee.org)
122 */
123public class PdfPublicKeySecurityHandler {
124
125    static final int SEED_LENGTH = 20;
126
127    private ArrayList<PdfPublicKeyRecipient> recipients = null;
128
129    private byte[] seed = new byte[SEED_LENGTH];
130
131    public PdfPublicKeySecurityHandler() {
132        KeyGenerator key;
133        try {
134            key = KeyGenerator.getInstance("AES");
135            key.init(192, new SecureRandom());
136            SecretKey sk = key.generateKey();
137            System.arraycopy(sk.getEncoded(), 0, seed, 0, SEED_LENGTH); // create the 20 bytes seed
138        } catch (NoSuchAlgorithmException e) {
139            seed = SecureRandom.getSeed(SEED_LENGTH);
140        }
141
142        recipients = new ArrayList<PdfPublicKeyRecipient>();
143    }
144
145    public void addRecipient(PdfPublicKeyRecipient recipient) {
146        recipients.add(recipient);
147    }
148
149    protected byte[] getSeed() {
150        return seed.clone();
151    }
152    /*
153    public PdfPublicKeyRecipient[] getRecipients() {
154        recipients.toArray();
155        return (PdfPublicKeyRecipient[])recipients.toArray();
156    }*/
157
158    public int getRecipientsSize() {
159        return recipients.size();
160    }
161
162    public byte[] getEncodedRecipient(int index) throws IOException, GeneralSecurityException {
163        //Certificate certificate = recipient.getX509();
164        PdfPublicKeyRecipient recipient = recipients.get(index);
165        byte[] cms = recipient.getCms();
166
167        if (cms != null) return cms;
168
169        Certificate certificate  = recipient.getCertificate();
170        int permission =  recipient.getPermission();//PdfWriter.AllowCopy | PdfWriter.AllowPrinting | PdfWriter.AllowScreenReaders | PdfWriter.AllowAssembly;
171        int revision = 3;
172
173        permission |= revision==3 ? 0xfffff0c0 : 0xffffffc0;
174        permission &= 0xfffffffc;
175        permission += 1;
176
177        byte[] pkcs7input = new byte[24];
178
179        byte one = (byte)permission;
180        byte two = (byte)(permission >> 8);
181        byte three = (byte)(permission >> 16);
182        byte four = (byte)(permission >> 24);
183
184        System.arraycopy(seed, 0, pkcs7input, 0, 20); // put this seed in the pkcs7 input
185
186        pkcs7input[20] = four;
187        pkcs7input[21] = three;
188        pkcs7input[22] = two;
189        pkcs7input[23] = one;
190
191        DERObject obj = createDERForRecipient(pkcs7input, (X509Certificate)certificate);
192
193        ByteArrayOutputStream baos = new ByteArrayOutputStream();
194
195        DEROutputStream k = new DEROutputStream(baos);
196
197        k.writeObject(obj);
198
199        cms = baos.toByteArray();
200
201        recipient.setCms(cms);
202
203        return cms;
204    }
205
206    public PdfArray getEncodedRecipients() throws IOException,
207                                         GeneralSecurityException {
208        PdfArray EncodedRecipients = new PdfArray();
209        byte[] cms = null;
210        for (int i=0; i<recipients.size(); i++)
211        try {
212            cms = getEncodedRecipient(i);
213            EncodedRecipients.add(new PdfLiteral(PdfContentByte.escapeString(cms)));
214        } catch (GeneralSecurityException e) {
215            EncodedRecipients = null;
216        } catch (IOException e) {
217            EncodedRecipients = null;
218        }
219
220        return EncodedRecipients;
221    }
222
223    private DERObject createDERForRecipient(byte[] in, X509Certificate cert)
224        throws IOException,
225               GeneralSecurityException
226    {
227
228        String s = "1.2.840.113549.3.2";
229
230        AlgorithmParameterGenerator algorithmparametergenerator = AlgorithmParameterGenerator.getInstance(s);
231        AlgorithmParameters algorithmparameters = algorithmparametergenerator.generateParameters();
232        ByteArrayInputStream bytearrayinputstream = new ByteArrayInputStream(algorithmparameters.getEncoded("ASN.1"));
233        ASN1InputStream asn1inputstream = new ASN1InputStream(bytearrayinputstream);
234        DERObject derobject = asn1inputstream.readObject();
235        KeyGenerator keygenerator = KeyGenerator.getInstance(s);
236        keygenerator.init(128);
237        SecretKey secretkey = keygenerator.generateKey();
238        Cipher cipher = Cipher.getInstance(s);
239        cipher.init(1, secretkey, algorithmparameters);
240        byte[] abyte1 = cipher.doFinal(in);
241        DEROctetString deroctetstring = new DEROctetString(abyte1);
242        KeyTransRecipientInfo keytransrecipientinfo = computeRecipientInfo(cert, secretkey.getEncoded());
243        DERSet derset = new DERSet(new RecipientInfo(keytransrecipientinfo));
244        AlgorithmIdentifier algorithmidentifier = new AlgorithmIdentifier(new DERObjectIdentifier(s), derobject);
245        EncryptedContentInfo encryptedcontentinfo =
246            new EncryptedContentInfo(PKCSObjectIdentifiers.data, algorithmidentifier, deroctetstring);
247        EnvelopedData env = new EnvelopedData(null, derset, encryptedcontentinfo, null);
248        ContentInfo contentinfo =
249            new ContentInfo(PKCSObjectIdentifiers.envelopedData, env);
250        return contentinfo.getDERObject();
251    }
252
253    private KeyTransRecipientInfo computeRecipientInfo(X509Certificate x509certificate, byte[] abyte0)
254        throws GeneralSecurityException, IOException
255    {
256        ASN1InputStream asn1inputstream =
257            new ASN1InputStream(new ByteArrayInputStream(x509certificate.getTBSCertificate()));
258        TBSCertificateStructure tbscertificatestructure =
259            TBSCertificateStructure.getInstance(asn1inputstream.readObject());
260        AlgorithmIdentifier algorithmidentifier = tbscertificatestructure.getSubjectPublicKeyInfo().getAlgorithmId();
261        IssuerAndSerialNumber issuerandserialnumber =
262            new IssuerAndSerialNumber(
263                tbscertificatestructure.getIssuer(),
264                tbscertificatestructure.getSerialNumber().getValue());
265        Cipher cipher = Cipher.getInstance(algorithmidentifier.getObjectId().getId());
266        cipher.init(1, x509certificate);
267        DEROctetString deroctetstring = new DEROctetString(cipher.doFinal(abyte0));
268        RecipientIdentifier recipId = new RecipientIdentifier(issuerandserialnumber);
269        return new KeyTransRecipientInfo( recipId, algorithmidentifier, deroctetstring);
270    }
271}