mattosaurus / PgpCore

.NET Core class library for using PGP
MIT License
245 stars 98 forks source link

DecryptFileAsync not working without password #128

Closed tharwania closed 3 years ago

tharwania commented 3 years ago

GenerateKeys without a password, and Encrypt without a password but on DecryptFileAsync, it won't work without a password.

mattosaurus commented 3 years ago

Yes, both a private key and password are required for decryption.

tharwania commented 3 years ago

I have tested on BouncyCastle. Empty password works there, not a null but an empty string. It's quite confusing when you can generate keys and encrypt without a password but for decryption password is required.

mattosaurus commented 3 years ago

If you're using an empty string is it actually setting this as the password rather than there being no password set? If so then this is probably getting caught by a IsNullOrWhitespace check somehere in PgpCore which I could probably modify to just check if the password is null.

If the key actually does have no password then there's no functionality at present to deal with this scenario as I assumed there would always be a password required as this is best practise. If you'd like to add this functionality in then feel free to submit a PR.

tharwania commented 3 years ago

@mattosaurus I found the issue, While constructing the EncryptionKeys Object, the password is only param separating private key and public key constructor, i.e

EncryptionKeys encryptionKeys = new EncryptionKeys(testFactory.PublicKeyFileInfo); EncryptionKeys decryptionKeys = new EncryptionKeys(testFactory.PrivateKeyFileInfo)

these two objects will always initialize a private key because a constructor without a password will always identify as a public key.

public async Task GenerateKeys_Encrypt_Decrypt_Without_Key_Password()
        {
            // Arrange
            TestFactory testFactory = new TestFactory();
            await testFactory.ArrangeAsync(KeyType.Known, FileType.Known);
            PGP pgp = new PGP();

            // Act
            await pgp.GenerateKeyAsync(testFactory.PublicKeyFilePath, testFactory.PrivateKeyFilePath);

            EncryptionKeys encryptionKeys = new EncryptionKeys(testFactory.PublicKeyFileInfo);
            EncryptionKeys decryptionKeys = new EncryptionKeys(testFactory.PrivateKeyFileInfo);
            PGP pgpEncrypt = new PGP(encryptionKeys);
            PGP pgpDecrypt = new PGP(decryptionKeys);

            await pgpEncrypt.EncryptFileAsync(testFactory.ContentFileInfo, testFactory.EncryptedContentFileInfo);
            await pgpDecrypt.DecryptFileAsync(testFactory.EncryptedContentFileInfo, testFactory.DecryptedContentFileInfo);

            // Assert
            Assert.True(testFactory.EncryptedContentFileInfo.Exists);
            Assert.True(testFactory.DecryptedContentFileInfo.Exists);
            Assert.Equal(testFactory.Content, testFactory.DecryptedContent.Trim());

            // Cleanup
            testFactory.Teardown();
        }

I have written a failing test for more clarity. We need an enum or some identifier of what kind of key it is, but that will be a breaking change. What would you suggest in the scenario?

mattosaurus commented 3 years ago

Hi, sorry for the delay in getting back to you on this. A private key without a password can be represented by using an empty string.

EncryptionKeys decryptionKeys = new EncryptionKeys(testFactory.PrivateKeyFileInfo, "");

Whether using an empty string for this is a good idea or not is another matter but this should work for your issue.