diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj b/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj
index abdc464da8efa..122d141eba6f7 100644
--- a/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj
+++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj
@@ -6,7 +6,8 @@
enable
$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.1-windows;netstandard2.1;netstandard2.0-windows;netstandard2.0;net461-windows
true
- 1
+ true
+ 2
Provides support for PKCS and CMS algorithms.
Commonly Used Types:
diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs
index bc38187f6edc2..07f7f8df07d2e 100644
--- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs
+++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs
@@ -677,7 +677,7 @@ public void AddCertificate(X509Certificate2 certificate)
{
foreach (CertificateChoiceAsn cert in _signedData.CertificateSet!)
{
- if (cert.Certificate!.Value.Span.SequenceEqual(rawData))
+ if (cert.Certificate is not null && cert.Certificate.Value.Span.SequenceEqual(rawData))
{
throw new CryptographicException(SR.Cryptography_Cms_CertificateAlreadyInCollection);
}
@@ -712,7 +712,7 @@ public void RemoveCertificate(X509Certificate2 certificate)
foreach (CertificateChoiceAsn cert in _signedData.CertificateSet!)
{
- if (cert.Certificate!.Value.Span.SequenceEqual(rawData))
+ if (cert.Certificate is not null && cert.Certificate.Value.Span.SequenceEqual(rawData))
{
PkcsHelpers.RemoveAt(ref _signedData.CertificateSet, idx);
Reencode();
diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.netcoreapp.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.netcoreapp.cs
index cfa17458204e5..4fbce727908ae 100644
--- a/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.netcoreapp.cs
+++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.netcoreapp.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
+using System.Formats.Asn1;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Test.Cryptography;
@@ -479,6 +480,92 @@ public static void CreateSignature_DigestAlgorithmWithSignatureOid_Prohibited()
}
}
+ [Fact]
+ public static void AddCertificate_CollectionContainsAttributeCertificate()
+ {
+ SignedCms signedCms = new SignedCms();
+ signedCms.Decode(SignedDocuments.TstWithAttributeCertificate);
+ signedCms.CheckSignature(true);
+
+ int countBefore = CountCertificateChoices(SignedDocuments.TstWithAttributeCertificate);
+
+ using (X509Certificate2 cert = Certificates.RSA2048SignatureOnly.GetCertificate())
+ {
+ signedCms.AddCertificate(cert);
+ byte[] reEncoded = signedCms.Encode();
+ int countAfter = CountCertificateChoices(reEncoded);
+ Assert.Equal(countBefore + 1, countAfter);
+
+ signedCms = new SignedCms();
+ signedCms.Decode(reEncoded);
+ signedCms.CheckSignature(true);
+ }
+ }
+
+ [Fact]
+ public static void RemoveCertificate_Existing_CollectionContainsAttributeCertificate()
+ {
+ SignedCms signedCms = new SignedCms();
+ signedCms.Decode(SignedDocuments.TstWithAttributeCertificate);
+ int countBefore = CountCertificateChoices(SignedDocuments.TstWithAttributeCertificate);
+
+ signedCms.RemoveCertificate(signedCms.Certificates[0]);
+ byte[] reEncoded = signedCms.Encode();
+ int countAfter = CountCertificateChoices(reEncoded);
+ Assert.Equal(countBefore - 1, countAfter);
+ }
+
+ [Fact]
+ public static void RemoveCertificate_NonExisting_CollectionContainsAttributeCertificate()
+ {
+ SignedCms signedCms = new SignedCms();
+ signedCms.Decode(SignedDocuments.TstWithAttributeCertificate);
+
+ using (X509Certificate2 cert = Certificates.RSA2048SignatureOnly.GetCertificate())
+ {
+ // Remove a non-existing certificate so that we are forced to enumerate the entire 'certificates[0]'
+ // collection (including attribute certificates) looking for it.
+ Assert.Throws(() => signedCms.RemoveCertificate(cert));
+ }
+ }
+
+ [Fact]
+ public static void ComputeCounterSignature_PreservesAttributeCertificate()
+ {
+ SignedCms signedCms = new SignedCms();
+ signedCms.Decode(SignedDocuments.TstWithAttributeCertificate);
+ int countBefore = CountCertificateChoices(SignedDocuments.TstWithAttributeCertificate);
+
+ using (X509Certificate2 cert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey())
+ {
+ CmsSigner signer = new CmsSigner(cert);
+ SignerInfo info = signedCms.SignerInfos[0];
+ info.ComputeCounterSignature(signer);
+ }
+
+ byte[] encoded = signedCms.Encode();
+ int countAfter = CountCertificateChoices(encoded);
+ Assert.Equal(countBefore + 1, countAfter);
+ }
+
+ [Fact]
+ public static void ComputeSignature_PreservesAttributeCertificate()
+ {
+ SignedCms signedCms = new SignedCms();
+ signedCms.Decode(SignedDocuments.TstWithAttributeCertificate);
+ int countBefore = CountCertificateChoices(SignedDocuments.TstWithAttributeCertificate);
+
+ using (X509Certificate2 cert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey())
+ {
+ CmsSigner signer = new CmsSigner(cert);
+ signedCms.ComputeSignature(signer);
+ }
+
+ byte[] encoded = signedCms.Encode();
+ int countAfter = CountCertificateChoices(encoded);
+ Assert.Equal(countBefore + 1, countAfter);
+ }
+
private static void VerifyWithExplicitPrivateKey(X509Certificate2 cert, AsymmetricAlgorithm key)
{
using (var pubCert = new X509Certificate2(cert.RawData))
@@ -539,5 +626,36 @@ private static void VerifyCounterSignatureWithExplicitPrivateKey(X509Certificate
Assert.Equal(counterSignerPubCert, cms.SignerInfos[0].CounterSignerInfos[0].Certificate);
}
}
+
+ private static int CountCertificateChoices(byte[] encoded)
+ {
+ AsnReader reader = new AsnReader(encoded, AsnEncodingRules.BER);
+ reader = reader.ReadSequence();
+ reader.ReadObjectIdentifier();
+ reader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0));
+ reader = reader.ReadSequence();
+
+ reader.ReadInteger(); // version
+ reader.ReadSetOf(); // digestAlgorithms
+ reader.ReadSequence(); // encapsulatedContentInfo
+
+ Asn1Tag expectedTag = new Asn1Tag(TagClass.ContextSpecific, 0, true); // certificates[0]
+
+ if (reader.PeekTag() == expectedTag)
+ {
+ AsnReader certs = reader.ReadSetOf(expectedTag);
+ int count = 0;
+
+ while (certs.HasData)
+ {
+ certs.ReadEncodedValue();
+ count++;
+ }
+
+ return count;
+ }
+
+ return 0;
+ }
}
}