Skip to content

Commit cbdc014

Browse files
authored
Fix SignedCms certificate collection modification with attribute certificates
When adding or removing certificates from the certificateSet collection, we assumed that the collection would only contain X.509 certificates. This changes the implementation so that when looking for certificates, we skip over choices that are not an X.509 certificate.
1 parent 2f27364 commit cbdc014

File tree

2 files changed

+83
-2
lines changed

2 files changed

+83
-2
lines changed

src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ public void AddCertificate(X509Certificate2 certificate)
686686
{
687687
foreach (CertificateChoiceAsn cert in _signedData.CertificateSet!)
688688
{
689-
if (cert.Certificate!.Value.Span.SequenceEqual(rawData))
689+
if (cert.Certificate is not null && cert.Certificate.Value.Span.SequenceEqual(rawData))
690690
{
691691
throw new CryptographicException(SR.Cryptography_Cms_CertificateAlreadyInCollection);
692692
}
@@ -721,7 +721,7 @@ public void RemoveCertificate(X509Certificate2 certificate)
721721

722722
foreach (CertificateChoiceAsn cert in _signedData.CertificateSet!)
723723
{
724-
if (cert.Certificate!.Value.Span.SequenceEqual(rawData))
724+
if (cert.Certificate is not null && cert.Certificate.Value.Span.SequenceEqual(rawData))
725725
{
726726
PkcsHelpers.RemoveAt(ref _signedData.CertificateSet, idx);
727727
Reencode();

src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.netcoreapp.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Generic;
5+
using System.Formats.Asn1;
56
using System.Linq;
67
using System.Security.Cryptography.X509Certificates;
78
using Test.Cryptography;
@@ -604,6 +605,55 @@ public static void CreateSignature_Ecdsa_ThrowsWithRsaSignaturePadding()
604605
}
605606
}
606607

608+
[Fact]
609+
public static void AddCertificate_CollectionContainsAttributeCertificate()
610+
{
611+
SignedCms signedCms = new SignedCms();
612+
signedCms.Decode(SignedDocuments.TstWithAttributeCertificate);
613+
signedCms.CheckSignature(true);
614+
615+
int countBefore = CountCertificateChoices(SignedDocuments.TstWithAttributeCertificate);
616+
617+
using (X509Certificate2 cert = Certificates.RSA2048SignatureOnly.GetCertificate())
618+
{
619+
signedCms.AddCertificate(cert);
620+
byte[] reEncoded = signedCms.Encode();
621+
int countAfter = CountCertificateChoices(reEncoded);
622+
Assert.Equal(countBefore + 1, countAfter);
623+
624+
signedCms = new SignedCms();
625+
signedCms.Decode(reEncoded);
626+
signedCms.CheckSignature(true);
627+
}
628+
}
629+
630+
[Fact]
631+
public static void RemoveCertificate_Existing_CollectionContainsAttributeCertificate()
632+
{
633+
SignedCms signedCms = new SignedCms();
634+
signedCms.Decode(SignedDocuments.TstWithAttributeCertificate);
635+
int countBefore = CountCertificateChoices(SignedDocuments.TstWithAttributeCertificate);
636+
637+
signedCms.RemoveCertificate(signedCms.Certificates[0]);
638+
byte[] reEncoded = signedCms.Encode();
639+
int countAfter = CountCertificateChoices(reEncoded);
640+
Assert.Equal(countBefore - 1, countAfter);
641+
}
642+
643+
[Fact]
644+
public static void RemoveCertificate_NonExisting_CollectionContainsAttributeCertificate()
645+
{
646+
SignedCms signedCms = new SignedCms();
647+
signedCms.Decode(SignedDocuments.TstWithAttributeCertificate);
648+
649+
using (X509Certificate2 cert = Certificates.RSA2048SignatureOnly.GetCertificate())
650+
{
651+
// Remove a non-existing certificate so that we are forced to enumerate the entire 'certificates[0]'
652+
// collection (including attribute certificates) looking for it.
653+
Assert.Throws<CryptographicException>(() => signedCms.RemoveCertificate(cert));
654+
}
655+
}
656+
607657
private static void VerifyWithExplicitPrivateKey(X509Certificate2 cert, AsymmetricAlgorithm key)
608658
{
609659
using (var pubCert = new X509Certificate2(cert.RawData))
@@ -664,5 +714,36 @@ private static void VerifyCounterSignatureWithExplicitPrivateKey(X509Certificate
664714
Assert.Equal(counterSignerPubCert, cms.SignerInfos[0].CounterSignerInfos[0].Certificate);
665715
}
666716
}
717+
718+
private static int CountCertificateChoices(byte[] encoded)
719+
{
720+
AsnReader reader = new AsnReader(encoded, AsnEncodingRules.BER);
721+
reader = reader.ReadSequence();
722+
reader.ReadObjectIdentifier();
723+
reader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
724+
reader = reader.ReadSequence();
725+
726+
reader.ReadInteger(); // version
727+
reader.ReadSetOf(); // digestAlgorithms
728+
reader.ReadSequence(); // encapsulatedContentInfo
729+
730+
Asn1Tag expectedTag = new Asn1Tag(TagClass.ContextSpecific, 0, true); // certificates[0]
731+
732+
if (reader.PeekTag() == expectedTag)
733+
{
734+
AsnReader certs = reader.ReadSetOf(expectedTag);
735+
int count = 0;
736+
737+
while (certs.HasData)
738+
{
739+
certs.ReadEncodedValue();
740+
count++;
741+
}
742+
743+
return count;
744+
}
745+
746+
return 0;
747+
}
667748
}
668749
}

0 commit comments

Comments
 (0)