bcgit / bc-csharp

BouncyCastle.NET Cryptography Library (Mirror)
https://www.bouncycastle.org/csharp
MIT License
1.63k stars 546 forks source link

`NullReferenceException` error when trying to get a recipient based on `SubjectKeyIdentifier` #541

Open rosco12345 opened 2 months ago

rosco12345 commented 2 months ago

We recently tried to update from version 2.3.1 to 2.4.0

This caused our encryption tests to break. I've produced a minimal example here. It's simply a test that encrypts a string then decrypts it, asserting that the original plaintext and the decrypted plaintext are the same.

The test in that example passes with version 2.3.1 and fails with a null reference error when using GetFirstRecipient().

I wonder if this could be related to changes made in a35474d

peterdettman commented 2 months ago

Yes, there was a fix around CMS recipients using SubjectKeyIdentifier that caused a breaking change. It arose out of this reported issue: https://github.com/bcgit/bc-csharp/issues/532 (not so much the initial report, but the followup discoveries).

Considering the example code you linked: the decryption code is technically wrong because RecipientID is an X509CertStoreSelector subclass, and the SubjectKeyIdentifier property should not be the raw SKI value (usually a 20 byte SHA-1 output), but rather a DER-encoding of that raw value (usually 22 bytes). The DER encoding corresponds to the expected format when used as an X.509 extension; we copied the behaviour apparently from java.security.cert.X509CertSelector.

The actual CMS messages (recipient infos) are supposed to store the raw SKI value (e.g. 20 byte OCTET STRING), but before 2.4.0 BC was using that value directly for the RecipientID.SubjectKeyIdentifier property and so would fail to match certificates. It was still possible to round-trip encrypt/decrypt (by fudging the encryption code) but then the messages would be invalid i.e. not interoperable with other implementations.

TLDR; Your example decryption code would have to use this to work correctly with 2.4.0:

var recipientID = new RecipientID {
    SubjectKeyIdentifier = new DerOctetString(SubjectKeyIdentifier).GetEncoded()
};