~brian-thomason/+junk/bouncycastle

« back to all changes in this revision

Viewing changes to src/org/bouncycastle/openpgp/PGPPublicKeyEncryptedData.java

  • Committer: Brian Thomason
  • Date: 2011-12-20 17:20:32 UTC
  • Revision ID: brian.thomason@canonical.com-20111220172032-rdtm13jgdxtksacr
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package org.bouncycastle.openpgp;
 
2
 
 
3
import org.bouncycastle.bcpg.BCPGInputStream;
 
4
import org.bouncycastle.bcpg.HashAlgorithmTags;
 
5
import org.bouncycastle.bcpg.InputStreamPacket;
 
6
import org.bouncycastle.bcpg.PublicKeyEncSessionPacket;
 
7
import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket;
 
8
import org.bouncycastle.jce.interfaces.ElGamalKey;
 
9
 
 
10
import javax.crypto.Cipher;
 
11
import javax.crypto.CipherInputStream;
 
12
import javax.crypto.SecretKey;
 
13
import javax.crypto.spec.IvParameterSpec;
 
14
import javax.crypto.spec.SecretKeySpec;
 
15
import java.io.EOFException;
 
16
import java.io.InputStream;
 
17
import java.math.BigInteger;
 
18
import java.security.DigestInputStream;
 
19
import java.security.InvalidKeyException;
 
20
import java.security.MessageDigest;
 
21
import java.security.NoSuchProviderException;
 
22
import java.security.Provider;
 
23
 
 
24
/**
 
25
 * A public key encrypted data object.
 
26
 */
 
27
public class PGPPublicKeyEncryptedData
 
28
    extends PGPEncryptedData
 
29
{    
 
30
    PublicKeyEncSessionPacket        keyData;
 
31
    
 
32
    PGPPublicKeyEncryptedData(
 
33
        PublicKeyEncSessionPacket    keyData,
 
34
        InputStreamPacket            encData)
 
35
    {
 
36
        super(encData);
 
37
        
 
38
        this.keyData = keyData;
 
39
    }
 
40
    
 
41
    private static Cipher getKeyCipher(
 
42
        int       algorithm,
 
43
        Provider  provider)
 
44
        throws PGPException
 
45
    {
 
46
        try
 
47
        {
 
48
            switch (algorithm)
 
49
            {
 
50
            case PGPPublicKey.RSA_ENCRYPT:
 
51
            case PGPPublicKey.RSA_GENERAL:
 
52
                return Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
 
53
            case PGPPublicKey.ELGAMAL_ENCRYPT:
 
54
            case PGPPublicKey.ELGAMAL_GENERAL:
 
55
                return Cipher.getInstance("ElGamal/ECB/PKCS1Padding", provider);
 
56
            default:
 
57
                throw new PGPException("unknown asymmetric algorithm: " + algorithm);
 
58
            }
 
59
        }
 
60
        catch (PGPException e)
 
61
        {
 
62
            throw e;
 
63
        }
 
64
        catch (Exception e)
 
65
        {
 
66
            throw new PGPException("Exception creating cipher", e);
 
67
        }
 
68
    }
 
69
    
 
70
    private boolean confirmCheckSum(
 
71
        byte[]    sessionInfo)
 
72
    {
 
73
        int    check = 0;
 
74
        
 
75
        for (int i = 1; i != sessionInfo.length - 2; i++)
 
76
        {
 
77
            check += sessionInfo[i] & 0xff;
 
78
        }
 
79
        
 
80
        return (sessionInfo[sessionInfo.length - 2] == (byte)(check >> 8))
 
81
                    && (sessionInfo[sessionInfo.length - 1] == (byte)(check));
 
82
    }
 
83
    
 
84
    /**
 
85
     * Return the keyID for the key used to encrypt the data.
 
86
     * 
 
87
     * @return long
 
88
     */
 
89
    public long getKeyID()
 
90
    {
 
91
        return keyData.getKeyID();
 
92
    }
 
93
 
 
94
    /**
 
95
     * Return the algorithm code for the symmetric algorithm used to encrypt the data.
 
96
     *
 
97
     * @return integer algorithm code
 
98
     */
 
99
    public int getSymmetricAlgorithm(
 
100
        PGPPrivateKey  privKey,
 
101
        String         provider)
 
102
        throws PGPException, NoSuchProviderException
 
103
    {
 
104
        return getSymmetricAlgorithm(privKey, PGPUtil.getProvider(provider));
 
105
    }
 
106
 
 
107
    public int getSymmetricAlgorithm(
 
108
        PGPPrivateKey  privKey,
 
109
        Provider       provider)
 
110
        throws PGPException, NoSuchProviderException
 
111
    {
 
112
        byte[] plain = fetchSymmetricKeyData(privKey, provider);
 
113
 
 
114
        return plain[0];
 
115
    }
 
116
 
 
117
    /**
 
118
     * Return the decrypted data stream for the packet.
 
119
     *
 
120
     * @param privKey private key to use
 
121
     * @param provider provider to use for private key and symmetric key decryption.
 
122
     * @return InputStream
 
123
     * @throws PGPException
 
124
     * @throws NoSuchProviderException
 
125
     */
 
126
    public InputStream getDataStream(
 
127
        PGPPrivateKey  privKey,
 
128
        String         provider)
 
129
        throws PGPException, NoSuchProviderException
 
130
    {
 
131
        return getDataStream(privKey, provider, provider);
 
132
    }
 
133
 
 
134
    public InputStream getDataStream(
 
135
        PGPPrivateKey  privKey,
 
136
        Provider       provider)
 
137
        throws PGPException
 
138
    {
 
139
        return getDataStream(privKey, provider, provider);
 
140
    }
 
141
 
 
142
    /**
 
143
     * Return the decrypted data stream for the packet.
 
144
     * 
 
145
     * @param privKey private key to use.
 
146
     * @param asymProvider asymetric provider to use with private key.
 
147
     * @param provider provider to use for symmetric algorithm.
 
148
     * @return InputStream
 
149
     * @throws PGPException
 
150
     * @throws NoSuchProviderException
 
151
     */
 
152
    public InputStream getDataStream(
 
153
        PGPPrivateKey  privKey,
 
154
        String         asymProvider,
 
155
        String         provider)
 
156
        throws PGPException, NoSuchProviderException
 
157
    {
 
158
        return getDataStream(privKey, PGPUtil.getProvider(asymProvider), PGPUtil.getProvider(provider));
 
159
    }
 
160
 
 
161
    public InputStream getDataStream(
 
162
        PGPPrivateKey  privKey,
 
163
        Provider       asymProvider,
 
164
        Provider       provider)
 
165
        throws PGPException
 
166
    {
 
167
        byte[] plain = fetchSymmetricKeyData(privKey, asymProvider);
 
168
        
 
169
        Cipher         c2;
 
170
        
 
171
        try
 
172
        {
 
173
            if (encData instanceof SymmetricEncIntegrityPacket)
 
174
            {
 
175
                c2 =
 
176
                    Cipher.getInstance(
 
177
                        PGPUtil.getSymmetricCipherName(plain[0]) + "/CFB/NoPadding",
 
178
                            provider);
 
179
            }
 
180
            else
 
181
            {
 
182
                c2 =
 
183
                    Cipher.getInstance(
 
184
                        PGPUtil.getSymmetricCipherName(plain[0]) + "/OpenPGPCFB/NoPadding",
 
185
                        provider);
 
186
            }
 
187
        }
 
188
        catch (PGPException e)
 
189
        {
 
190
            throw e;
 
191
        }
 
192
        catch (Exception e)
 
193
        {
 
194
            throw new PGPException("exception creating cipher", e);
 
195
        }
 
196
        
 
197
        if (c2 != null)
 
198
        {
 
199
            try
 
200
            {
 
201
                SecretKey    key = new SecretKeySpec(plain, 1, plain.length - 3, PGPUtil.getSymmetricCipherName(plain[0]));
 
202
                
 
203
                byte[]       iv = new byte[c2.getBlockSize()];
 
204
                
 
205
                c2.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
 
206
 
 
207
                encStream = new BCPGInputStream(new CipherInputStream(encData.getInputStream(), c2));
 
208
                
 
209
                if (encData instanceof SymmetricEncIntegrityPacket)
 
210
                {
 
211
                    truncStream = new TruncatedStream(encStream);
 
212
                    encStream = new DigestInputStream(truncStream, MessageDigest.getInstance(PGPUtil.getDigestName(HashAlgorithmTags.SHA1), provider));
 
213
                }
 
214
                
 
215
                for (int i = 0; i != iv.length; i++)
 
216
                {
 
217
                    int    ch = encStream.read();
 
218
                    
 
219
                    if (ch < 0)
 
220
                    {
 
221
                        throw new EOFException("unexpected end of stream.");
 
222
                    }
 
223
                    
 
224
                    iv[i] = (byte)ch;
 
225
                }
 
226
                
 
227
                int    v1 = encStream.read();
 
228
                int    v2 = encStream.read();
 
229
                
 
230
                if (v1 < 0 || v2 < 0)
 
231
                {
 
232
                    throw new EOFException("unexpected end of stream.");
 
233
                }
 
234
                
 
235
                //
 
236
                // some versions of PGP appear to produce 0 for the extra
 
237
                // bytes rather than repeating the two previous bytes
 
238
                //
 
239
                /*
 
240
                 * Commented out in the light of the oracle attack.
 
241
                if (iv[iv.length - 2] != (byte)v1 && v1 != 0)
 
242
                {
 
243
                    throw new PGPDataValidationException("data check failed.");
 
244
                }
 
245
                
 
246
                if (iv[iv.length - 1] != (byte)v2 && v2 != 0)
 
247
                {
 
248
                    throw new PGPDataValidationException("data check failed.");
 
249
                }
 
250
                */
 
251
                
 
252
                return encStream;
 
253
            }
 
254
            catch (PGPException e)
 
255
            {
 
256
                throw e;
 
257
            }
 
258
            catch (Exception e)
 
259
            {
 
260
                throw new PGPException("Exception starting decryption", e);
 
261
            }
 
262
        }
 
263
        else
 
264
        {
 
265
            return encData.getInputStream();
 
266
        }
 
267
    }
 
268
 
 
269
    private byte[] fetchSymmetricKeyData(PGPPrivateKey privKey, Provider asymProvider)
 
270
        throws PGPException
 
271
    {
 
272
        Cipher c1 = getKeyCipher(keyData.getAlgorithm(), asymProvider);
 
273
 
 
274
        try
 
275
        {
 
276
            c1.init(Cipher.DECRYPT_MODE, privKey.getKey());
 
277
        }
 
278
        catch (InvalidKeyException e)
 
279
        {
 
280
            throw new PGPException("error setting asymmetric cipher", e);
 
281
        }
 
282
 
 
283
        BigInteger[]    keyD = keyData.getEncSessionKey();
 
284
 
 
285
        if (keyData.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT
 
286
            || keyData.getAlgorithm() == PGPPublicKey.RSA_GENERAL)
 
287
        {
 
288
            byte[]    bi = keyD[0].toByteArray();
 
289
 
 
290
            if (bi[0] == 0)
 
291
            {
 
292
                c1.update(bi, 1, bi.length - 1);
 
293
            }
 
294
            else
 
295
            {
 
296
                c1.update(bi);
 
297
            }
 
298
        }
 
299
        else
 
300
        {
 
301
            ElGamalKey k = (ElGamalKey)privKey.getKey();
 
302
            int           size = (k.getParameters().getP().bitLength() + 7) / 8;
 
303
            byte[]        tmp = new byte[size];
 
304
 
 
305
            byte[]        bi = keyD[0].toByteArray();
 
306
            if (bi.length > size)
 
307
            {
 
308
                c1.update(bi, 1, bi.length - 1);
 
309
            }
 
310
            else
 
311
            {
 
312
                System.arraycopy(bi, 0, tmp, tmp.length - bi.length, bi.length);
 
313
                c1.update(tmp);
 
314
            }
 
315
 
 
316
            bi = keyD[1].toByteArray();
 
317
            for (int i = 0; i != tmp.length; i++)
 
318
            {
 
319
                tmp[i] = 0;
 
320
            }
 
321
 
 
322
            if (bi.length > size)
 
323
            {
 
324
                c1.update(bi, 1, bi.length - 1);
 
325
            }
 
326
            else
 
327
            {
 
328
                System.arraycopy(bi, 0, tmp, tmp.length - bi.length, bi.length);
 
329
                c1.update(tmp);
 
330
            }
 
331
        }
 
332
 
 
333
        byte[] plain;
 
334
        try
 
335
        {
 
336
            plain = c1.doFinal();
 
337
        }
 
338
        catch (Exception e)
 
339
        {
 
340
            throw new PGPException("exception decrypting secret key", e);
 
341
        }
 
342
 
 
343
        if (!confirmCheckSum(plain))
 
344
        {
 
345
            throw new PGPKeyValidationException("key checksum failed");
 
346
        }
 
347
 
 
348
        return plain;
 
349
    }
 
350
}