View Javadoc

1   /*
2    * $Id: DecryptStreamTransformer.java 22865 2011-09-05 17:23:45Z 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  import java.util.concurrent.atomic.AtomicLong;
20  
21  import org.apache.commons.lang.Validate;
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 DecryptStreamTransformer implements StreamTransformer
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 DecryptStreamTransformer(InputStream toBeDecrypted,
50                                       PGPPublicKey publicKey,
51                                       PGPSecretKey secretKey,
52                                       String password) throws IOException
53      {
54          Validate.notNull(toBeDecrypted, "The toBeDecrypted should not be null");
55          Validate.notNull(publicKey, "The publicKey should not be null");
56          Validate.notNull(secretKey, "The secretKey should not be null");
57          Validate.notNull(password, "The password should not be null");
58  
59          this.toBeDecrypted = toBeDecrypted;
60          this.publicKey = publicKey;
61          this.secretKey = secretKey;
62          this.password = password;
63          this.bytesWrote = 0;
64      }
65  
66      /**
67       * {@inheritDoc}
68       */
69      @Override
70      public void initialize(OutputStream out) throws Exception
71      {
72          InputStream decodedInputStream = PGPUtil.getDecoderStream(this.toBeDecrypted);
73          PGPObjectFactory pgpF = new PGPObjectFactory(decodedInputStream);
74          Object o = pgpF.nextObject();
75  
76          if (o == null)
77          {
78              throw new IllegalArgumentException("Invalid PGP message");
79          }
80  
81          // the first object might be a PGP marker packet.
82          PGPEncryptedDataList enc;
83          if (o instanceof PGPEncryptedDataList)
84          {
85              enc = (PGPEncryptedDataList) o;
86  
87          }
88          else
89          {
90              enc = (PGPEncryptedDataList) pgpF.nextObject();
91          }
92  
93          // This loop looks like it is ready for multiple encrypted
94          // objects, but really only one is expected.
95          Iterator<?> it = enc.getEncryptedDataObjects();
96          PGPPublicKeyEncryptedData pbe = null;
97          PGPPrivateKey privateKey = null;
98          while (privateKey == null && it.hasNext())
99          {
100             pbe = (PGPPublicKeyEncryptedData) it.next();
101             privateKey = getPrivateKey(pbe.getKeyID(), this.password);
102             if (privateKey == null)
103             {
104                 throw new IllegalArgumentException("Failed to find private key with ID " + pbe.getKeyID());
105             }
106         }
107 
108         clearStream = pbe.getDataStream(privateKey, "BC");
109         PGPObjectFactory plainFact = new PGPObjectFactory(clearStream);
110 
111         o = plainFact.nextObject();
112         PGPOnePassSignature signature = null;
113         if (o instanceof PGPOnePassSignatureList)
114         {
115             PGPOnePassSignatureList list = (PGPOnePassSignatureList) o;
116             signature = list.get(0);
117             signature.initVerify(this.publicKey, "BC");
118             // TODO verify signature
119             // signature.verify(null);
120             o = plainFact.nextObject();
121         }
122 
123         compressedStream = null;
124         if (o instanceof PGPCompressedData)
125         {
126             PGPCompressedData cData = (PGPCompressedData) o;
127             compressedStream = new BufferedInputStream(cData.getDataStream());
128             PGPObjectFactory pgpFact = new PGPObjectFactory(compressedStream);
129             Object streamData = pgpFact.nextObject();
130             o = streamData;
131         }
132 
133         if (o instanceof PGPLiteralData)
134         {
135             PGPLiteralData ld = (PGPLiteralData) o;
136             uncStream = ld.getInputStream();
137         }
138         else
139         {
140             throw new PGPException("input is not PGPLiteralData - type unknown.");
141         }
142     }
143 
144     /**
145      * {@inheritDoc}
146      */
147     public boolean write(OutputStream out, AtomicLong bytesRequested) throws Exception
148     {
149         int len = 0;
150         byte[] buf = new byte[1 << 16];
151         boolean wroteSomething = false;
152 
153         while (bytesRequested.get() + offset > bytesWrote && (len = uncStream.read(buf)) > 0)
154         {
155             out.write(buf, 0, len);
156             bytesWrote = bytesWrote + len;
157             wroteSomething = true;
158         }
159 
160         if (wroteSomething && len <= 0)
161         {
162             uncStream.close();
163             if (compressedStream != null)
164             {
165                 compressedStream.close();
166             }
167             clearStream.close();
168             return true;
169         }
170 
171         return false;
172     }
173 
174     private PGPPrivateKey getPrivateKey(long keyID, String pass) throws PGPException, NoSuchProviderException
175     {
176         PGPSecretKey pgpSecKey = this.secretKey;
177         if (pgpSecKey == null)
178         {
179             return null;
180         }
181         else
182         {
183             return pgpSecKey.extractPrivateKey(pass.toCharArray(), "BC");
184         }
185     }
186 }