Search code examples
c#bouncycastleencryption-symmetricopenpgp

symmetric decryption throwing error


I'm working on adding the ability to decrypt a file encrypted using GPG & Symmetric Encryption.

However whenever it tries to get the private key data this exceptions keeps getting hit:

Unable to cast object of type 'Org.BouncyCastle.Bcpg.OpenPgp.PgpPbeEncryptedData' to type 'Org.BouncyCastle.Bcpg.OpenPgp.PgpPublicKeyEncryptedData'.

everywhere I look this is how you do it:

Stream inputStream = IoHelper.GetStream(inputData);
        PgpObjectFactory pgpFactory = new PgpObjectFactory(PgpUtilities.GetDecoderStream(inputStream));
        PgpObject pgp = null;
        if (pgpFactory != null)
        {
            pgp = pgpFactory.NextPgpObject();
        }

        PgpEncryptedDataList encryptedData = null;
        if (pgp is PgpEncryptedDataList)
        {
            encryptedData = (PgpEncryptedDataList)pgp;
        }
        else
        {
            encryptedData = (PgpEncryptedDataList)pgpFactory.NextPgpObject();
        }

        Stream privateKeyStream = File.OpenRead(PrivateKeyOnlyPath);

        // find secret key
        PgpSecretKeyRingBundle pgpKeyRing = new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(privateKeyStream));
        PgpPrivateKey privateKey = null;

        foreach (PgpPublicKeyEncryptedData pked in encryptedData.GetEncryptedDataObjects())
        {
            privateKey = FindSecretKey(pgpKeyRing, pked.KeyId, Password.ToCharArray());
            if (privateKey != null)
            {
                //pubKeyData = pked;
                break;
            }
        }

I'm referencing the code from here

I'm lost on why it's not working and not sure where to go next.


Solution

  • Hybrid Cryptosystems

    The "normal" way of encryption in OpenPGP (which GnuPG implements) is hybrid encryption: public/private key cryptography for key management and encrypting a session key, and subsequently symmetric encryption using this session key of the actual data.

    From what you write, it seems the public/private key cryptography step was omitted, and the session key instead generated using a passphrase, for example by using the command gpg --symmetric. You can determine how it was encrypted using pgpdump. Output for a symmetrically encrypted file looks like

    Old: Symmetric-Key Encrypted Session Key Packet(tag 3)(13 bytes)
        New version(4)
        Sym alg - CAST5(sym 3)
        Iterated and salted string-to-key(s2k 3):
            Hash alg - SHA512(hash 10)
            Salt - 3b be 1b 03 64 c3 bb 7e 
            Count - 102400(coded count 105)
    New: Symmetrically Encrypted Data Packet(tag 9)(26 bytes)
        Encrypted data [sym alg is specified in sym-key encrypted session key]
    

    Especially consider the first line with the symmetric-key encrypted session key packet.

    What to do

    I'm sorry I do not really know C#, and neither have worked with Bouncy Castle yet, so I cannot provide a ready-to-run solution. Moreover, the C# documentation seems to be more or less inexistent.

    I'd guess you'll have to make use of the PgpPbeEncryptedData class, which seems to take an input stream and a passphrase and provides the decrypted information as an output stream. There is an example for the Java Bouncy Castle package, which will probably be very similar and mostly boils down to

    byte[] decryptedAgainByteArray = ByteArrayHandler.decrypt(encryptedAgain, PASS.toCharArray());