1
package org.bouncycastle.openpgp;
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;
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;
25
* A public key encrypted data object.
27
public class PGPPublicKeyEncryptedData
28
extends PGPEncryptedData
30
PublicKeyEncSessionPacket keyData;
32
PGPPublicKeyEncryptedData(
33
PublicKeyEncSessionPacket keyData,
34
InputStreamPacket encData)
38
this.keyData = keyData;
41
private static Cipher getKeyCipher(
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);
57
throw new PGPException("unknown asymmetric algorithm: " + algorithm);
60
catch (PGPException e)
66
throw new PGPException("Exception creating cipher", e);
70
private boolean confirmCheckSum(
75
for (int i = 1; i != sessionInfo.length - 2; i++)
77
check += sessionInfo[i] & 0xff;
80
return (sessionInfo[sessionInfo.length - 2] == (byte)(check >> 8))
81
&& (sessionInfo[sessionInfo.length - 1] == (byte)(check));
85
* Return the keyID for the key used to encrypt the data.
89
public long getKeyID()
91
return keyData.getKeyID();
95
* Return the algorithm code for the symmetric algorithm used to encrypt the data.
97
* @return integer algorithm code
99
public int getSymmetricAlgorithm(
100
PGPPrivateKey privKey,
102
throws PGPException, NoSuchProviderException
104
return getSymmetricAlgorithm(privKey, PGPUtil.getProvider(provider));
107
public int getSymmetricAlgorithm(
108
PGPPrivateKey privKey,
110
throws PGPException, NoSuchProviderException
112
byte[] plain = fetchSymmetricKeyData(privKey, provider);
118
* Return the decrypted data stream for the packet.
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
126
public InputStream getDataStream(
127
PGPPrivateKey privKey,
129
throws PGPException, NoSuchProviderException
131
return getDataStream(privKey, provider, provider);
134
public InputStream getDataStream(
135
PGPPrivateKey privKey,
139
return getDataStream(privKey, provider, provider);
143
* Return the decrypted data stream for the packet.
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
152
public InputStream getDataStream(
153
PGPPrivateKey privKey,
156
throws PGPException, NoSuchProviderException
158
return getDataStream(privKey, PGPUtil.getProvider(asymProvider), PGPUtil.getProvider(provider));
161
public InputStream getDataStream(
162
PGPPrivateKey privKey,
163
Provider asymProvider,
167
byte[] plain = fetchSymmetricKeyData(privKey, asymProvider);
173
if (encData instanceof SymmetricEncIntegrityPacket)
177
PGPUtil.getSymmetricCipherName(plain[0]) + "/CFB/NoPadding",
184
PGPUtil.getSymmetricCipherName(plain[0]) + "/OpenPGPCFB/NoPadding",
188
catch (PGPException e)
194
throw new PGPException("exception creating cipher", e);
201
SecretKey key = new SecretKeySpec(plain, 1, plain.length - 3, PGPUtil.getSymmetricCipherName(plain[0]));
203
byte[] iv = new byte[c2.getBlockSize()];
205
c2.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
207
encStream = new BCPGInputStream(new CipherInputStream(encData.getInputStream(), c2));
209
if (encData instanceof SymmetricEncIntegrityPacket)
211
truncStream = new TruncatedStream(encStream);
212
encStream = new DigestInputStream(truncStream, MessageDigest.getInstance(PGPUtil.getDigestName(HashAlgorithmTags.SHA1), provider));
215
for (int i = 0; i != iv.length; i++)
217
int ch = encStream.read();
221
throw new EOFException("unexpected end of stream.");
227
int v1 = encStream.read();
228
int v2 = encStream.read();
230
if (v1 < 0 || v2 < 0)
232
throw new EOFException("unexpected end of stream.");
236
// some versions of PGP appear to produce 0 for the extra
237
// bytes rather than repeating the two previous bytes
240
* Commented out in the light of the oracle attack.
241
if (iv[iv.length - 2] != (byte)v1 && v1 != 0)
243
throw new PGPDataValidationException("data check failed.");
246
if (iv[iv.length - 1] != (byte)v2 && v2 != 0)
248
throw new PGPDataValidationException("data check failed.");
254
catch (PGPException e)
260
throw new PGPException("Exception starting decryption", e);
265
return encData.getInputStream();
269
private byte[] fetchSymmetricKeyData(PGPPrivateKey privKey, Provider asymProvider)
272
Cipher c1 = getKeyCipher(keyData.getAlgorithm(), asymProvider);
276
c1.init(Cipher.DECRYPT_MODE, privKey.getKey());
278
catch (InvalidKeyException e)
280
throw new PGPException("error setting asymmetric cipher", e);
283
BigInteger[] keyD = keyData.getEncSessionKey();
285
if (keyData.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT
286
|| keyData.getAlgorithm() == PGPPublicKey.RSA_GENERAL)
288
byte[] bi = keyD[0].toByteArray();
292
c1.update(bi, 1, bi.length - 1);
301
ElGamalKey k = (ElGamalKey)privKey.getKey();
302
int size = (k.getParameters().getP().bitLength() + 7) / 8;
303
byte[] tmp = new byte[size];
305
byte[] bi = keyD[0].toByteArray();
306
if (bi.length > size)
308
c1.update(bi, 1, bi.length - 1);
312
System.arraycopy(bi, 0, tmp, tmp.length - bi.length, bi.length);
316
bi = keyD[1].toByteArray();
317
for (int i = 0; i != tmp.length; i++)
322
if (bi.length > size)
324
c1.update(bi, 1, bi.length - 1);
328
System.arraycopy(bi, 0, tmp, tmp.length - bi.length, bi.length);
336
plain = c1.doFinal();
340
throw new PGPException("exception decrypting secret key", e);
343
if (!confirmCheckSum(plain))
345
throw new PGPKeyValidationException("key checksum failed");