View Javadoc

1   /*
2    * $Id: DecryptOutputStreamWriter.java 20320 2010-11-24 15:03:31Z dfeist $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.module.pgp;
12  
13  import java.io.BufferedInputStream;
14  import java.io.IOException;
15  import java.io.InputStream;
16  import java.io.OutputStream;
17  import java.security.NoSuchProviderException;
18  import java.util.Iterator;
19  
20  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicLong;
21  
22  import org.bouncycastle.openpgp.PGPCompressedData;
23  import org.bouncycastle.openpgp.PGPEncryptedDataList;
24  import org.bouncycastle.openpgp.PGPException;
25  import org.bouncycastle.openpgp.PGPLiteralData;
26  import org.bouncycastle.openpgp.PGPObjectFactory;
27  import org.bouncycastle.openpgp.PGPOnePassSignature;
28  import org.bouncycastle.openpgp.PGPOnePassSignatureList;
29  import org.bouncycastle.openpgp.PGPPrivateKey;
30  import org.bouncycastle.openpgp.PGPPublicKey;
31  import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
32  import org.bouncycastle.openpgp.PGPSecretKey;
33  import org.bouncycastle.openpgp.PGPUtil;
34  
35  public class DecryptOutputStreamWriter implements OutputStreamWriter
36  {
37      private static final long offset = 1 << 24;
38      
39      private InputStream toBeDecrypted;
40      private PGPPublicKey publicKey;
41      private PGPSecretKey secretKey;
42      private String password;
43  
44      private InputStream uncStream;
45      private InputStream compressedStream;
46      private InputStream clearStream;
47      private long bytesWrote;
48  
49      public DecryptOutputStreamWriter(InputStream toBeDecrypted,
50                                       PGPPublicKey publicKey,
51                                       PGPSecretKey secretKey,
52                                       String password) throws IOException
53      {
54          this.toBeDecrypted = toBeDecrypted;
55          this.publicKey = publicKey;
56          this.secretKey = secretKey;
57          this.password = password;
58          this.bytesWrote = 0;
59      }
60  
61      /**
62       * {@inheritDoc}
63       */
64      public void initialize(OutputStream out) throws Exception
65      {
66          InputStream decodedInputStream = PGPUtil.getDecoderStream(this.toBeDecrypted);
67          PGPObjectFactory pgpF = new PGPObjectFactory(decodedInputStream);
68          Object o = pgpF.nextObject();
69  
70          if (o == null)
71          {
72              throw new IllegalArgumentException("Invalid PGP message");
73          }
74  
75          // the first object might be a PGP marker packet.
76          PGPEncryptedDataList enc;
77          if (o instanceof PGPEncryptedDataList)
78          {
79              enc = (PGPEncryptedDataList) o;
80  
81          }
82          else
83          {
84              enc = (PGPEncryptedDataList) pgpF.nextObject();
85          }
86  
87          // This loop looks like it is ready for multiple encrypted
88          // objects, but really only one is expected.
89          Iterator it = enc.getEncryptedDataObjects();
90          PGPPublicKeyEncryptedData pbe = null;
91          PGPPrivateKey privateKey = null;
92          while (privateKey == null && it.hasNext())
93          {
94              pbe = (PGPPublicKeyEncryptedData) it.next();
95              privateKey = getPrivateKey(pbe.getKeyID(), this.password);
96              if (privateKey == null)
97              {
98                  throw new IllegalArgumentException("Failed to find private key with ID " + pbe.getKeyID());
99              }
100         }
101 
102         clearStream = pbe.getDataStream(privateKey, "BC");
103         PGPObjectFactory plainFact = new PGPObjectFactory(clearStream);
104 
105         o = plainFact.nextObject();
106         PGPOnePassSignature signature = null;
107         if (o instanceof PGPOnePassSignatureList)
108         {
109             PGPOnePassSignatureList list = (PGPOnePassSignatureList) o;
110             signature = list.get(0);
111             signature.initVerify(this.publicKey, "BC");
112             // TODO verify signature
113             // signature.verify(null);
114             o = plainFact.nextObject();
115         }
116 
117         compressedStream = null;
118         if (o instanceof PGPCompressedData)
119         {
120             PGPCompressedData cData = (PGPCompressedData) o;
121             compressedStream = new BufferedInputStream(cData.getDataStream());
122             PGPObjectFactory pgpFact = new PGPObjectFactory(compressedStream);
123             Object streamData = pgpFact.nextObject();
124             o = streamData;
125         }
126 
127         if (o instanceof PGPLiteralData)
128         {
129             PGPLiteralData ld = (PGPLiteralData) o;
130             uncStream = ld.getInputStream();
131         }
132         else
133         {
134             throw new PGPException("input is not PGPLiteralData - type unknown.");
135         }
136     }
137 
138     /**
139      * {@inheritDoc}
140      */
141     public boolean write(OutputStream out, AtomicLong bytesRequested) throws Exception
142     {
143         int len = 0;
144         byte[] buf = new byte[1 << 16];
145         boolean wroteSomething = false;
146 
147         while (bytesRequested.get() + offset > bytesWrote && (len = uncStream.read(buf)) > 0)
148         {
149             out.write(buf, 0, len);
150             bytesWrote = bytesWrote + len;
151             wroteSomething = true;
152         }
153 
154         if (wroteSomething && len <= 0)
155         {
156             uncStream.close();
157             if (compressedStream != null)
158             {
159                 compressedStream.close();
160             }
161             clearStream.close();
162             return true;
163         }
164 
165         return false;
166     }
167 
168     private PGPPrivateKey getPrivateKey(long keyID, String pass) throws PGPException, NoSuchProviderException
169     {
170         PGPSecretKey pgpSecKey = this.secretKey;
171         if (pgpSecKey == null)
172         {
173             return null;
174         }
175         else
176         {
177             return pgpSecKey.extractPrivateKey(pass.toCharArray(), "BC");
178         }
179     }
180 }