001: package org.bouncycastle.openpgp;
002:
003: import org.bouncycastle.bcpg.BCPGInputStream;
004: import org.bouncycastle.bcpg.HashAlgorithmTags;
005: import org.bouncycastle.bcpg.InputStreamPacket;
006: import org.bouncycastle.bcpg.PublicKeyEncSessionPacket;
007: import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket;
008: import org.bouncycastle.jce.interfaces.ElGamalKey;
009:
010: import javax.crypto.Cipher;
011: import javax.crypto.CipherInputStream;
012: import javax.crypto.SecretKey;
013: import javax.crypto.spec.IvParameterSpec;
014: import javax.crypto.spec.SecretKeySpec;
015: import java.io.EOFException;
016: import java.io.InputStream;
017: import java.math.BigInteger;
018: import java.security.DigestInputStream;
019: import java.security.InvalidKeyException;
020: import java.security.MessageDigest;
021: import java.security.NoSuchProviderException;
022:
023: /**
024: * A public key encrypted data object.
025: */
026: public class PGPPublicKeyEncryptedData extends PGPEncryptedData {
027: PublicKeyEncSessionPacket keyData;
028:
029: PGPPublicKeyEncryptedData(PublicKeyEncSessionPacket keyData,
030: InputStreamPacket encData) {
031: super (encData);
032:
033: this .keyData = keyData;
034: }
035:
036: private static Cipher getKeyCipher(int algorithm, String provider)
037: throws NoSuchProviderException, PGPException {
038: try {
039: switch (algorithm) {
040: case PGPPublicKey.RSA_ENCRYPT:
041: case PGPPublicKey.RSA_GENERAL:
042: return Cipher.getInstance("RSA/ECB/PKCS1Padding",
043: provider);
044: case PGPPublicKey.ELGAMAL_ENCRYPT:
045: case PGPPublicKey.ELGAMAL_GENERAL:
046: return Cipher.getInstance("ElGamal/ECB/PKCS1Padding",
047: provider);
048: default:
049: throw new PGPException("unknown asymmetric algorithm: "
050: + algorithm);
051: }
052: } catch (NoSuchProviderException e) {
053: throw e;
054: } catch (PGPException e) {
055: throw e;
056: } catch (Exception e) {
057: throw new PGPException("Exception creating cipher", e);
058: }
059: }
060:
061: private boolean confirmCheckSum(byte[] sessionInfo) {
062: int check = 0;
063:
064: for (int i = 1; i != sessionInfo.length - 2; i++) {
065: check += sessionInfo[i] & 0xff;
066: }
067:
068: return (sessionInfo[sessionInfo.length - 2] == (byte) (check >> 8))
069: && (sessionInfo[sessionInfo.length - 1] == (byte) (check));
070: }
071:
072: /**
073: * Return the keyID for the key used to encrypt the data.
074: *
075: * @return long
076: */
077: public long getKeyID() {
078: return keyData.getKeyID();
079: }
080:
081: /**
082: * Return the decrypted data stream for the packet.
083: *
084: * @param privKey private key to use
085: * @param provider provider to use for private key and symmetric key decryption.
086: * @return InputStream
087: * @throws PGPException
088: * @throws NoSuchProviderException
089: */
090: public InputStream getDataStream(PGPPrivateKey privKey,
091: String provider) throws PGPException,
092: NoSuchProviderException {
093: return getDataStream(privKey, provider, provider);
094: }
095:
096: /**
097: * Return the decrypted data stream for the packet.
098: *
099: * @param privKey private key to use.
100: * @param asymProvider asymetric provider to use with private key.
101: * @param provider provider to use for symmetric algorithm.
102: * @return InputStream
103: * @throws PGPException
104: * @throws NoSuchProviderException
105: */
106: public InputStream getDataStream(PGPPrivateKey privKey,
107: String asymProvider, String provider) throws PGPException,
108: NoSuchProviderException {
109: Cipher c1 = getKeyCipher(keyData.getAlgorithm(), asymProvider);
110:
111: try {
112: c1.init(Cipher.DECRYPT_MODE, privKey.getKey());
113: } catch (InvalidKeyException e) {
114: throw new PGPException("error setting asymmetric cipher", e);
115: }
116:
117: BigInteger[] keyD = keyData.getEncSessionKey();
118:
119: if (keyData.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT
120: || keyData.getAlgorithm() == PGPPublicKey.RSA_GENERAL) {
121: byte[] bi = keyD[0].toByteArray();
122:
123: if (bi[0] == 0) {
124: c1.update(bi, 1, bi.length - 1);
125: } else {
126: c1.update(bi);
127: }
128: } else {
129: ElGamalKey k = (ElGamalKey) privKey.getKey();
130: int size = (k.getParameters().getP().bitLength() + 7) / 8;
131: byte[] tmp = new byte[size];
132:
133: byte[] bi = keyD[0].toByteArray();
134: if (bi.length > size) {
135: c1.update(bi, 1, bi.length - 1);
136: } else {
137: System.arraycopy(bi, 0, tmp, tmp.length - bi.length,
138: bi.length);
139: c1.update(tmp);
140: }
141:
142: bi = keyD[1].toByteArray();
143: for (int i = 0; i != tmp.length; i++) {
144: tmp[i] = 0;
145: }
146:
147: if (bi.length > size) {
148: c1.update(bi, 1, bi.length - 1);
149: } else {
150: System.arraycopy(bi, 0, tmp, tmp.length - bi.length,
151: bi.length);
152: c1.update(tmp);
153: }
154: }
155:
156: byte[] plain;
157: try {
158: plain = c1.doFinal();
159: } catch (Exception e) {
160: throw new PGPException("exception decrypting secret key", e);
161: }
162:
163: if (!confirmCheckSum(plain)) {
164: throw new PGPKeyValidationException("key checksum failed");
165: }
166:
167: Cipher c2;
168:
169: try {
170: if (encData instanceof SymmetricEncIntegrityPacket) {
171: c2 = Cipher.getInstance(PGPUtil
172: .getSymmetricCipherName(plain[0])
173: + "/CFB/NoPadding", provider);
174: } else {
175: c2 = Cipher.getInstance(PGPUtil
176: .getSymmetricCipherName(plain[0])
177: + "/OpenPGPCFB/NoPadding", provider);
178: }
179: } catch (NoSuchProviderException e) {
180: throw e;
181: } catch (PGPException e) {
182: throw e;
183: } catch (Exception e) {
184: throw new PGPException("exception creating cipher", e);
185: }
186:
187: if (c2 != null) {
188: try {
189: SecretKey key = new SecretKeySpec(plain, 1,
190: plain.length - 3, PGPUtil
191: .getSymmetricCipherName(plain[0]));
192:
193: byte[] iv = new byte[c2.getBlockSize()];
194:
195: c2.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(
196: iv));
197:
198: encStream = new BCPGInputStream(new CipherInputStream(
199: encData.getInputStream(), c2));
200:
201: if (encData instanceof SymmetricEncIntegrityPacket) {
202: truncStream = new TruncatedStream(encStream);
203: encStream = new DigestInputStream(
204: truncStream,
205: MessageDigest
206: .getInstance(
207: PGPUtil
208: .getDigestName(HashAlgorithmTags.SHA1),
209: provider));
210: }
211:
212: for (int i = 0; i != iv.length; i++) {
213: int ch = encStream.read();
214:
215: if (ch < 0) {
216: throw new EOFException(
217: "unexpected end of stream.");
218: }
219:
220: iv[i] = (byte) ch;
221: }
222:
223: int v1 = encStream.read();
224: int v2 = encStream.read();
225:
226: if (v1 < 0 || v2 < 0) {
227: throw new EOFException("unexpected end of stream.");
228: }
229:
230: //
231: // some versions of PGP appear to produce 0 for the extra
232: // bytes rather than repeating the two previous bytes
233: //
234: /*
235: * Commented out in the light of the oracle attack.
236: if (iv[iv.length - 2] != (byte)v1 && v1 != 0)
237: {
238: throw new PGPDataValidationException("data check failed.");
239: }
240:
241: if (iv[iv.length - 1] != (byte)v2 && v2 != 0)
242: {
243: throw new PGPDataValidationException("data check failed.");
244: }
245: */
246:
247: return encStream;
248: } catch (PGPException e) {
249: throw e;
250: } catch (Exception e) {
251: throw new PGPException("Exception starting decryption",
252: e);
253: }
254: } else {
255: return encData.getInputStream();
256: }
257: }
258: }
|