private static byte[] EncryptKeyData( byte[] rawKeyData, SymmetricKeyAlgorithmTag encAlgorithm, char[] passPhrase, SecureRandom random, out S2k s2k, out byte[] iv) { IBufferedCipher c; try { string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm); c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding"); } catch (Exception e) { throw new PgpException("Exception creating cipher", e); } byte[] s2kIV = new byte[8]; random.NextBytes(s2kIV); s2k = new S2k(HashAlgorithmTag.Sha1, s2kIV, 0x60); KeyParameter kp = PgpUtilities.MakeKeyFromPassPhrase(encAlgorithm, s2k, passPhrase); iv = new byte[c.GetBlockSize()]; random.NextBytes(iv); c.Init(true, new ParametersWithRandom(new ParametersWithIV(kp, iv), random)); return(c.DoFinal(rawKeyData)); }
/// <summary>Add a PBE encryption method to the encrypted object.</summary> public void AddMethod( char[] passPhrase, HashAlgorithmTag s2kDigest) { byte[] iv = new byte[8]; rand.NextBytes(iv); S2k s2k = new S2k(s2kDigest, iv, 0x60); methods.Add(new PbeMethod(defAlgorithm, s2k, PgpUtilities.MakeKeyFromPassPhrase(defAlgorithm, s2k, passPhrase))); }
/// <summary>Return the decrypted input stream, using the passed in passphrase.</summary> public Stream GetDataStream( char[] passPhrase) { try { SymmetricKeyAlgorithmTag keyAlgorithm = keyData.EncAlgorithm; KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase( keyAlgorithm, keyData.S2k, passPhrase); byte[] secKeyData = keyData.GetSecKeyData(); if (secKeyData != null && secKeyData.Length > 0) { IBufferedCipher keyCipher = CipherUtilities.GetCipher( PgpUtilities.GetSymmetricCipherName(keyAlgorithm) + "/CFB/NoPadding"); keyCipher.Init(false, new ParametersWithIV(key, new byte[keyCipher.GetBlockSize()])); byte[] keyBytes = keyCipher.DoFinal(secKeyData); keyAlgorithm = (SymmetricKeyAlgorithmTag)keyBytes[0]; key = ParameterUtilities.CreateKeyParameter( PgpUtilities.GetSymmetricCipherName(keyAlgorithm), keyBytes, 1, keyBytes.Length - 1); } IBufferedCipher c = CreateStreamCipher(keyAlgorithm); byte[] iv = new byte[c.GetBlockSize()]; c.Init(false, new ParametersWithIV(key, iv)); encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c, null)); if (encData is SymmetricEncIntegrityPacket) { truncStream = new TruncatedStream(encStream); string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1); IDigest digest = DigestUtilities.GetDigest(digestName); encStream = new DigestStream(truncStream, digest, null); } if (Streams.ReadFully(encStream, iv, 0, iv.Length) < iv.Length) { throw new EndOfStreamException("unexpected end of stream."); } int v1 = encStream.ReadByte(); int v2 = encStream.ReadByte(); if (v1 < 0 || v2 < 0) { throw new EndOfStreamException("unexpected end of stream."); } // Note: the oracle attack on the "quick check" bytes is not deemed // a security risk for PBE (see PgpPublicKeyEncryptedData) bool repeatCheckPassed = iv[iv.Length - 2] == (byte)v1 && iv[iv.Length - 1] == (byte)v2; // Note: some versions of PGP appear to produce 0 for the extra // bytes rather than repeating the two previous bytes bool zeroesCheckPassed = v1 == 0 && v2 == 0; if (!repeatCheckPassed && !zeroesCheckPassed) { throw new PgpDataValidationException("quick check failed."); } return(encStream); } catch (PgpException e) { throw e; } catch (Exception e) { throw new PgpException("Exception creating cipher", e); } }
private byte[] ExtractKeyData( char[] passPhrase) { SymmetricKeyAlgorithmTag alg = secret.EncAlgorithm; byte[] encData = secret.GetSecretKeyData(); if (alg == SymmetricKeyAlgorithmTag.Null) { return(encData); } byte[] data; IBufferedCipher c = null; try { string cName = PgpUtilities.GetSymmetricCipherName(alg); c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding"); } catch (Exception e) { throw new PgpException("Exception creating cipher", e); } // TODO Factor this block out as 'encryptData' try { KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(secret.EncAlgorithm, secret.S2k, passPhrase); byte[] iv = secret.GetIV(); if (secret.PublicKeyPacket.Version == 4) { c.Init(false, new ParametersWithIV(key, iv)); data = c.DoFinal(encData); bool useSha1 = secret.S2kUsage == SecretKeyPacket.UsageSha1; byte[] check = Checksum(useSha1, data, (useSha1) ? data.Length - 20 : data.Length - 2); for (int i = 0; i != check.Length; i++) { if (check[i] != data[data.Length - check.Length + i]) { throw new PgpException("Checksum mismatch at " + i + " of " + check.Length); } } } else // version 2 or 3, RSA only. { data = new byte[encData.Length]; // // read in the four numbers // int pos = 0; for (int i = 0; i != 4; i++) { c.Init(false, new ParametersWithIV(key, iv)); int encLen = (((encData[pos] << 8) | (encData[pos + 1] & 0xff)) + 7) / 8; data[pos] = encData[pos]; data[pos + 1] = encData[pos + 1]; pos += 2; c.DoFinal(encData, pos, encLen, data, pos); pos += encLen; if (i != 3) { Array.Copy(encData, pos - iv.Length, iv, 0, iv.Length); } } // // verify Checksum // int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff); int calcCs = 0; for (int j = 0; j < data.Length - 2; j++) { calcCs += data[j] & 0xff; } calcCs &= 0xffff; if (calcCs != cs) { throw new PgpException("Checksum mismatch: passphrase wrong, expected " + cs.ToString("X") + " found " + calcCs.ToString("X")); } } return(data); } catch (PgpException e) { throw e; } catch (Exception e) { throw new PgpException("Exception decrypting key", e); } }