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.SymmetricEncIntegrityPacket;
007: import org.bouncycastle.bcpg.SymmetricKeyEncSessionPacket;
008:
009: import java.io.EOFException;
010: import java.io.InputStream;
011: import java.security.DigestInputStream;
012: import java.security.MessageDigest;
013: import java.security.NoSuchAlgorithmException;
014: import java.security.NoSuchProviderException;
015:
016: import javax.crypto.Cipher;
017: import javax.crypto.CipherInputStream;
018: import javax.crypto.NoSuchPaddingException;
019: import javax.crypto.SecretKey;
020: import javax.crypto.spec.IvParameterSpec;
021: import javax.crypto.spec.SecretKeySpec;
022:
023: /**
024: * A password based encryption object.
025: */
026: public class PGPPBEEncryptedData extends PGPEncryptedData {
027: SymmetricKeyEncSessionPacket keyData;
028:
029: PGPPBEEncryptedData(SymmetricKeyEncSessionPacket keyData,
030: InputStreamPacket encData) {
031: super (encData);
032:
033: this .keyData = keyData;
034: }
035:
036: /**
037: * Return the raw input stream for the data stream.
038: *
039: * @return InputStream
040: */
041: public InputStream getInputStream() {
042: return encData.getInputStream();
043: }
044:
045: /**
046: * Return the decrypted input stream, using the passed in passPhrase.
047: *
048: * @param passPhrase
049: * @param provider
050: * @return InputStream
051: * @throws PGPException
052: * @throws NoSuchProviderException
053: */
054: public InputStream getDataStream(char[] passPhrase, String provider)
055: throws PGPException, NoSuchProviderException {
056: try {
057: int keyAlgorithm = keyData.getEncAlgorithm();
058: SecretKey key = PGPUtil.makeKeyFromPassPhrase(keyAlgorithm,
059: keyData.getS2K(), passPhrase, provider);
060:
061: byte[] secKeyData = keyData.getSecKeyData();
062: if (secKeyData != null) {
063: Cipher keyCipher = Cipher.getInstance(PGPUtil
064: .getSymmetricCipherName(keyAlgorithm)
065: + "/CFB/NoPadding", provider);
066:
067: keyCipher.init(Cipher.DECRYPT_MODE, key,
068: new IvParameterSpec(new byte[keyCipher
069: .getBlockSize()]));
070:
071: byte[] keyBytes = keyCipher.doFinal(secKeyData);
072:
073: keyAlgorithm = keyBytes[0];
074: key = new SecretKeySpec(keyBytes, 1,
075: keyBytes.length - 1, PGPUtil
076: .getSymmetricCipherName(keyAlgorithm));
077: }
078:
079: Cipher c = createStreamCipher(keyAlgorithm, provider);
080:
081: byte[] iv = new byte[c.getBlockSize()];
082:
083: c.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
084:
085: encStream = new BCPGInputStream(new CipherInputStream(
086: encData.getInputStream(), c));
087:
088: if (encData instanceof SymmetricEncIntegrityPacket) {
089: truncStream = new TruncatedStream(encStream);
090:
091: String digestName = PGPUtil
092: .getDigestName(HashAlgorithmTags.SHA1);
093: MessageDigest digest = MessageDigest.getInstance(
094: digestName, provider);
095:
096: encStream = new DigestInputStream(truncStream, digest);
097: }
098:
099: for (int i = 0; i != iv.length; i++) {
100: int ch = encStream.read();
101:
102: if (ch < 0) {
103: throw new EOFException("unexpected end of stream.");
104: }
105:
106: iv[i] = (byte) ch;
107: }
108:
109: int v1 = encStream.read();
110: int v2 = encStream.read();
111:
112: if (v1 < 0 || v2 < 0) {
113: throw new EOFException("unexpected end of stream.");
114: }
115:
116: // Note: the oracle attack on "quick check" bytes is not deemed
117: // a security risk for PBE (see PGPPublicKeyEncryptedData)
118:
119: boolean repeatCheckPassed = iv[iv.length - 2] == (byte) v1
120: && iv[iv.length - 1] == (byte) v2;
121:
122: // Note: some versions of PGP appear to produce 0 for the extra
123: // bytes rather than repeating the two previous bytes
124: boolean zeroesCheckPassed = v1 == 0 && v2 == 0;
125:
126: if (!repeatCheckPassed && !zeroesCheckPassed) {
127: throw new PGPDataValidationException(
128: "data check failed.");
129: }
130:
131: return encStream;
132: } catch (PGPException e) {
133: throw e;
134: } catch (Exception e) {
135: throw new PGPException("Exception creating cipher", e);
136: }
137: }
138:
139: private Cipher createStreamCipher(int keyAlgorithm, String provider)
140: throws NoSuchAlgorithmException, NoSuchPaddingException,
141: NoSuchProviderException, PGPException {
142: String mode = (encData instanceof SymmetricEncIntegrityPacket) ? "CFB"
143: : "OpenPGPCFB";
144:
145: String cName = PGPUtil.getSymmetricCipherName(keyAlgorithm)
146: + "/" + mode + "/NoPadding";
147:
148: return Cipher.getInstance(cName, provider);
149: }
150: }
|