{
private SignedDataAsn _signedData;
private bool _hasData;
+ private SubjectIdentifierType _signerIdentifierType;
// A defensive copy of the relevant portions of the data to Decode
private Memory<byte> _heldData;
if (contentInfo.Content == null)
throw new ArgumentNullException("contentInfo.Content");
- // signerIdentifierType is ignored.
- // In .NET Framework it is used for the signer type of a prompt-for-certificate signer.
- // In .NET Core we don't support prompting.
- //
- // .NET Framework turned any unknown value into IssuerAndSerialNumber, so no exceptions
- // are required, either.
+ // Normalize the subject identifier type the same way as .NET Framework.
+ // This value is only used in the zero-argument ComputeSignature overload,
+ // where it controls whether it succeeds (NoSignature) or throws (anything else),
+ // but in case it ever applies to anything else, make sure we're storing it
+ // faithfully.
+ switch (signerIdentifierType)
+ {
+ case SubjectIdentifierType.NoSignature:
+ case SubjectIdentifierType.IssuerAndSerialNumber:
+ case SubjectIdentifierType.SubjectKeyIdentifier:
+ _signerIdentifierType = signerIdentifierType;
+ break;
+ default:
+ _signerIdentifierType = SubjectIdentifierType.IssuerAndSerialNumber;
+ break;
+ }
ContentInfo = contentInfo;
Detached = detached;
return wrappedContent;
}
- public void ComputeSignature()
- {
- throw new PlatformNotSupportedException(SR.Cryptography_Cms_NoSignerCert);
- }
+ public void ComputeSignature() => ComputeSignature(new CmsSigner(_signerIdentifierType), true);
public void ComputeSignature(CmsSigner signer) => ComputeSignature(signer, true);
throw new CryptographicException(SR.Cryptography_Cms_Sign_Empty_Content);
}
+ if (_hasData && signer.SignerIdentifierType == SubjectIdentifierType.NoSignature)
+ {
+ // Even if all signers have been removed, throw if doing a NoSignature signature
+ // on a loaded (from file, or from first signature) document.
+ //
+ // This matches the NetFX behavior.
+ throw new CryptographicException(SR.Cryptography_Cms_Sign_No_Signature_First_Signer);
+ }
+
+ if (signer.Certificate == null && signer.SignerIdentifierType != SubjectIdentifierType.NoSignature)
+ {
+ if (silent)
+ {
+ // NetFX compatibility, silent disallows prompting, so throws InvalidOperationException
+ // in this state.
+ //
+ // The message is different than on NetFX, because the resource string was for
+ // enveloped CMS recipients (and the other site which would use that resource
+ // is unreachable code due to CmsRecipient's ctor guarding against null certificates)
+ throw new InvalidOperationException(SR.Cryptography_Cms_NoSignerCertSilent);
+ }
+
+ // Otherwise, PNSE. .NET Core doesn't support launching the cert picker UI.
+ throw new PlatformNotSupportedException(SR.Cryptography_Cms_NoSignerCert);
+ }
+
// If we had content already, use that now.
// (The second signer doesn't inherit edits to signedCms.ContentInfo.Content)
ReadOnlyMemory<byte> content = _heldContent ?? ContentInfo.Content;
}
[Theory]
+ [InlineData(SubjectIdentifierType.Unknown)]
+ [InlineData(SubjectIdentifierType.IssuerAndSerialNumber)]
+ [InlineData(SubjectIdentifierType.SubjectKeyIdentifier)]
+ [InlineData((SubjectIdentifierType)76)]
+ public static void ZeroArgComputeSignature(SubjectIdentifierType identifierType)
+ {
+ ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
+ SignedCms cms = new SignedCms(identifierType, contentInfo);
+
+ Assert.Throws<InvalidOperationException>(() => cms.ComputeSignature());
+
+ cms = new SignedCms(identifierType, contentInfo, detached: true);
+ Assert.Throws<InvalidOperationException>(() => cms.ComputeSignature());
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public static void ZeroArgComputeSignature_NoSignature(bool detached)
+ {
+ ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
+ SignedCms cms = new SignedCms(SubjectIdentifierType.NoSignature, contentInfo, detached);
+
+ if (PlatformDetection.IsFullFramework)
+ {
+ Assert.Throws<NullReferenceException>(() => cms.ComputeSignature());
+ }
+ else
+ {
+ cms.ComputeSignature();
+
+ SignerInfoCollection signers = cms.SignerInfos;
+ Assert.Equal(1, signers.Count);
+ Assert.Equal(SubjectIdentifierType.NoSignature, signers[0].SignerIdentifier.Type);
+ cms.CheckHash();
+ Assert.Throws<CryptographicException>(() => cms.CheckSignature(true));
+ }
+ }
+
+ [Theory]
+ [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, false)]
+ [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, true)]
+ [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, false)]
+ [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, true)]
+ // NoSignature is a different test, because it succeeds (CoreFX) or fails differently (NetFX)
+ public static void SignSilentWithNoCertificate(SubjectIdentifierType identifierType, bool detached)
+ {
+ ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
+ SignedCms cms = new SignedCms(contentInfo, detached);
+
+ Assert.Throws<InvalidOperationException>(
+ () => cms.ComputeSignature(new CmsSigner(identifierType), silent: true));
+ }
+
+ [Theory]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, false)]
+ [InlineData(SubjectIdentifierType.IssuerAndSerialNumber, true)]
+ [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, false)]
+ [InlineData(SubjectIdentifierType.SubjectKeyIdentifier, true)]
+ // NoSignature is a different test, because it succeeds (CoreFX) or fails differently (NetFX)
+ public static void SignNoisyWithNoCertificate_NotSupported(
+ SubjectIdentifierType identifierType,
+ bool detached)
+ {
+ ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
+ SignedCms cms = new SignedCms(contentInfo, detached);
+
+ Assert.Throws<PlatformNotSupportedException>(
+ () => cms.ComputeSignature(new CmsSigner(identifierType), silent: false));
+ }
+
+ [Theory]
[InlineData(SubjectIdentifierType.IssuerAndSerialNumber, false)]
[InlineData(SubjectIdentifierType.IssuerAndSerialNumber, true)]
[InlineData(SubjectIdentifierType.SubjectKeyIdentifier, false)]
[Theory]
[InlineData(false, false)]
[InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(true, true)]
+ public static void AddFirstSigner_NoSignature(bool detached, bool includeExtraCert)
+ {
+ ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
+ SignedCms cms = new SignedCms(contentInfo, detached);
+ X509Certificate2Collection certs;
+
+ // A certificate shouldn't really be required here, but on .NET Framework
+ // it will encounter throw a NullReferenceException.
+ using (X509Certificate2 cert = Certificates.RSAKeyTransferCapi1.GetCertificate())
+ using (X509Certificate2 cert2 = Certificates.DHKeyAgree1.GetCertificate())
+ {
+ CmsSigner cmsSigner = new CmsSigner(SubjectIdentifierType.NoSignature, cert);
+
+ if (includeExtraCert)
+ {
+ cmsSigner.Certificates.Add(cert2);
+ }
+
+ cms.ComputeSignature(cmsSigner);
+
+ certs = cms.Certificates;
+
+ if (includeExtraCert)
+ {
+ Assert.Equal(1, certs.Count);
+ Assert.Equal(cert2.RawData, certs[0].RawData);
+ }
+ else
+ {
+ Assert.Equal(0, certs.Count);
+ }
+ }
+
+ Assert.ThrowsAny<CryptographicException>(() => cms.CheckSignature(true));
+ cms.CheckHash();
+
+ byte[] encoded = cms.Encode();
+
+ if (detached)
+ {
+ cms = new SignedCms(contentInfo, detached);
+ }
+ else
+ {
+ cms = new SignedCms();
+ }
+
+ cms.Decode(encoded);
+ Assert.ThrowsAny<CryptographicException>(() => cms.CheckSignature(true));
+ cms.CheckHash();
+
+ SignerInfoCollection signerInfos = cms.SignerInfos;
+ Assert.Equal(1, signerInfos.Count);
+
+ SignerInfo firstSigner = signerInfos[0];
+ Assert.ThrowsAny<CryptographicException>(() => firstSigner.CheckSignature(true));
+ firstSigner.CheckHash();
+
+ certs = cms.Certificates;
+
+ if (includeExtraCert)
+ {
+ Assert.Equal(1, certs.Count);
+ Assert.Equal("CN=DfHelleKeyAgreement1", certs[0].SubjectName.Name);
+ }
+ else
+ {
+ Assert.Equal(0, certs.Count);
+ }
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public static void AddFirstSigner_NoSignature_NoCert(bool detached)
+ {
+ ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
+ SignedCms cms = new SignedCms(contentInfo, detached);
+
+ Action sign = () =>
+ cms.ComputeSignature(
+ new CmsSigner(SubjectIdentifierType.NoSignature)
+ {
+ IncludeOption = X509IncludeOption.None,
+ });
+
+ if (PlatformDetection.IsFullFramework)
+ {
+ Assert.Throws<NullReferenceException>(sign);
+ }
+ else
+ {
+ sign();
+ Assert.ThrowsAny<CryptographicException>(() => cms.CheckSignature(true));
+ cms.CheckHash();
+ Assert.Equal(1, cms.SignerInfos.Count);
+ }
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public static void AddSecondSigner_NoSignature(bool detached)
+ {
+ ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
+ SignedCms cms = new SignedCms(contentInfo, detached);
+
+ using (X509Certificate2 cert = Certificates.RSAKeyTransferCapi1.TryGetCertificateWithPrivateKey())
+ {
+ cms.ComputeSignature(
+ new CmsSigner(cert)
+ {
+ IncludeOption = X509IncludeOption.None,
+ });
+
+ Assert.Throws<CryptographicException>(
+ () =>
+ cms.ComputeSignature(
+ new CmsSigner(SubjectIdentifierType.NoSignature)
+ {
+ IncludeOption = X509IncludeOption.None,
+ }));
+ }
+
+ Assert.Equal(1, cms.SignerInfos.Count);
+ Assert.ThrowsAny<CryptographicException>(() => cms.CheckSignature(true));
+ cms.CheckHash();
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public static void AddSecondSigner_NoSignature_AfterRemove(bool detached)
+ {
+ ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
+ SignedCms cms = new SignedCms(contentInfo, detached);
+
+ using (X509Certificate2 cert = Certificates.RSAKeyTransferCapi1.TryGetCertificateWithPrivateKey())
+ {
+ cms.ComputeSignature(
+ new CmsSigner(cert)
+ {
+ IncludeOption = X509IncludeOption.None,
+ });
+
+ Assert.Throws<CryptographicException>(
+ () =>
+ cms.ComputeSignature(
+ new CmsSigner(SubjectIdentifierType.NoSignature)
+ {
+ IncludeOption = X509IncludeOption.None,
+ }));
+
+ cms.RemoveSignature(0);
+
+ // Because the document was already initialized (when initially signed),
+ // the "NoSignature must be the first signer" exception is thrown, even
+ // though there are no signers.
+ Assert.Throws<CryptographicException>(
+ () =>
+ cms.ComputeSignature(
+ new CmsSigner(SubjectIdentifierType.NoSignature)
+ {
+ IncludeOption = X509IncludeOption.None,
+ }));
+ }
+
+ Assert.Equal(0, cms.SignerInfos.Count);
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public static void AddSecondSigner_NoSignature_LoadUnsigned(bool detached)
+ {
+ ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
+ SignedCms cms = new SignedCms(contentInfo, detached);
+
+ using (X509Certificate2 cert = Certificates.RSAKeyTransferCapi1.TryGetCertificateWithPrivateKey())
+ {
+ cms.ComputeSignature(
+ new CmsSigner(cert)
+ {
+ IncludeOption = X509IncludeOption.None,
+ });
+
+ Assert.Throws<CryptographicException>(
+ () =>
+ cms.ComputeSignature(
+ new CmsSigner(SubjectIdentifierType.NoSignature)
+ {
+ IncludeOption = X509IncludeOption.None,
+ }));
+
+ cms.RemoveSignature(0);
+
+ // Reload the document now that it has no signatures.
+ byte[] encoded = cms.Encode();
+
+ if (detached)
+ {
+ cms = new SignedCms(contentInfo, detached);
+ }
+ else
+ {
+ cms = new SignedCms();
+ }
+
+ cms.Decode(encoded);
+
+ // Because the document was already initialized (when loaded),
+ // the "NoSignature must be the first signer" exception is thrown, even
+ // though there are no signers.
+ Assert.Throws<CryptographicException>(
+ () =>
+ cms.ComputeSignature(
+ new CmsSigner(SubjectIdentifierType.NoSignature)
+ {
+ IncludeOption = X509IncludeOption.None,
+ }));
+ }
+
+ Assert.Equal(0, cms.SignerInfos.Count);
+ }
+
+ [Theory]
+ [InlineData(false, false)]
+ [InlineData(false, true)]
[InlineData(true, true)]
[InlineData(true, false)]
public static void AddSigner_DuplicateCert_RSA(bool skidFirst, bool detached)
Assert.Equal("42", envelopedCms.ContentInfo.Content.ByteArrayToHex());
}
+
+ [Fact]
+ public static void CheckNoSignature_FromNetFx()
+ {
+ byte[] encoded = (
+ "30819F06092A864886F70D010702A0819130818E020101310F300D0609608648" +
+ "0165030402010500301406092A864886F70D010701A007040509080706053162" +
+ "3060020101301C3017311530130603550403130C44756D6D79205369676E6572" +
+ "020100300D06096086480165030402010500300C06082B060105050706020500" +
+ "0420AF5F6F5C5967C377E49193ECA1EE0B98300A171CD3165C9A2410E8FB7C02" +
+ "8674"
+ ).HexToByteArray();
+
+ CheckNoSignature(encoded);
+ }
+
+ [Fact]
+ public static void CheckNoSignature_FromNetFx_TamperSignatureOid()
+ {
+ // CheckNoSignature_FromNetFx with the algorithm identifier changed from
+ // 1.3.6.1.5.5.7.6.2 to 10.3.6.1.5.5.7.6.10
+ byte[] encoded = (
+ "30819F06092A864886F70D010702A0819130818E020101310F300D0609608648" +
+ "0165030402010500301406092A864886F70D010701A007040509080706053162" +
+ "3060020101301C3017311530130603550403130C44756D6D79205369676E6572" +
+ "020100300D06096086480165030402010500300C06082B0601050507060A0500" +
+ "0420AF5F6F5C5967C377E49193ECA1EE0B98300A171CD3165C9A2410E8FB7C02" +
+ "8674"
+ ).HexToByteArray();
+
+ CheckNoSignature(encoded, badOid: true);
+ }
+
+ [Fact]
+ public static void CheckNoSignature_FromCoreFx()
+ {
+ byte[] encoded = (
+ "30819906092A864886F70D010702A0818B308188020101310D300B0609608648" +
+ "016503040201301406092A864886F70D010701A00704050908070605315E305C" +
+ "020101301C3017311530130603550403130C44756D6D79205369676E65720201" +
+ "00300B0609608648016503040201300A06082B060105050706020420AF5F6F5C" +
+ "5967C377E49193ECA1EE0B98300A171CD3165C9A2410E8FB7C028674"
+ ).HexToByteArray();
+
+ CheckNoSignature(encoded);
+ }
+
+ [Fact]
+ public static void CheckNoSignature_FromCoreFx_TamperSignatureOid()
+ {
+ // CheckNoSignature_FromCoreFx with the algorithm identifier changed from
+ // 1.3.6.1.5.5.7.6.2 to 10.3.6.1.5.5.7.6.10
+ byte[] encoded = (
+ "30819906092A864886F70D010702A0818B308188020101310D300B0609608648" +
+ "016503040201301406092A864886F70D010701A00704050908070605315E305C" +
+ "020101301C3017311530130603550403130C44756D6D79205369676E65720201" +
+ "00300B0609608648016503040201300A06082B0601050507060A0420AF5F6F5C" +
+ "5967C377E49193ECA1EE0B98300A171CD3165C9A2410E8FB7C028674"
+ ).HexToByteArray();
+
+ CheckNoSignature(encoded, badOid: true);
+ }
+
+ [Fact]
+ public static void CheckNoSignature_FromCoreFx_TamperIssuerName()
+ {
+ // CheckNoSignature_FromCoreFx with the issuer name changed from "Dummy Cert"
+ // to "Dumny Cert" (m => n / 0x6D => 0x6E)
+ byte[] encoded = (
+ "30819906092A864886F70D010702A0818B308188020101310D300B0609608648" +
+ "016503040201301406092A864886F70D010701A00704050908070605315E305C" +
+ "020101301C3017311530130603550403130C44756D6E79205369676E65720201" +
+ "00300B0609608648016503040201300A06082B060105050706020420AF5F6F5C" +
+ "5967C377E49193ECA1EE0B98300A171CD3165C9A2410E8FB7C028674"
+ ).HexToByteArray();
+
+ SignedCms cms = new SignedCms();
+ cms.Decode(encoded);
+ SignerInfoCollection signers = cms.SignerInfos;
+ Assert.Equal(1, signers.Count);
+ Assert.Equal(SubjectIdentifierType.IssuerAndSerialNumber, signers[0].SignerIdentifier.Type);
+ Assert.ThrowsAny<CryptographicException>(() => cms.CheckSignature(true));
+ Assert.ThrowsAny<CryptographicException>(() => signers[0].CheckSignature(true));
+
+ // Assert.NoThrow
+ cms.CheckHash();
+ signers[0].CheckHash();
+ }
+
+ private static void CheckNoSignature(byte[] encoded, bool badOid=false)
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(encoded);
+ SignerInfoCollection signers = cms.SignerInfos;
+ Assert.Equal(1, signers.Count);
+ Assert.Equal(SubjectIdentifierType.NoSignature, signers[0].SignerIdentifier.Type);
+ Assert.Throws<CryptographicException>(() => cms.CheckSignature(true));
+
+ if (badOid)
+ {
+ Assert.ThrowsAny<CryptographicException>(() => cms.CheckHash());
+ }
+ else
+ {
+ // Assert.NoThrow
+ cms.CheckHash();
+ }
+ }
}
}
}
[Fact]
+ public static void AddFirstCounterSigner_NoSignature_NoPrivateKey()
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber);
+
+ SignerInfo firstSigner = cms.SignerInfos[0];
+
+ using (X509Certificate2 cert = Certificates.RSAKeyTransferCapi1.GetCertificate())
+ {
+ Action sign = () =>
+ firstSigner.ComputeCounterSignature(
+ new CmsSigner(
+ SubjectIdentifierType.NoSignature,
+ cert)
+ {
+ IncludeOption = X509IncludeOption.None,
+ });
+
+ if (PlatformDetection.IsFullFramework)
+ {
+ Assert.ThrowsAny<CryptographicException>(sign);
+ }
+ else
+ {
+ sign();
+ cms.CheckHash();
+ Assert.ThrowsAny<CryptographicException>(() => cms.CheckSignature(true));
+ firstSigner.CheckSignature(true);
+ }
+ }
+ }
+
+ [Fact]
+ public static void AddFirstCounterSigner_NoSignature()
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber);
+
+ SignerInfo firstSigner = cms.SignerInfos[0];
+
+ // A certificate shouldn't really be required here, but on .NET Framework
+ // it will prompt for the counter-signer's certificate if it's null,
+ // even if the signature type is NoSignature.
+ using (X509Certificate2 cert = Certificates.RSAKeyTransferCapi1.TryGetCertificateWithPrivateKey())
+ {
+ firstSigner.ComputeCounterSignature(
+ new CmsSigner(
+ SubjectIdentifierType.NoSignature,
+ cert)
+ {
+ IncludeOption = X509IncludeOption.None,
+ });
+ }
+
+ Assert.ThrowsAny<CryptographicException>(() => cms.CheckSignature(true));
+ cms.CheckHash();
+
+ byte[] encoded = cms.Encode();
+ cms = new SignedCms();
+ cms.Decode(encoded);
+ Assert.ThrowsAny<CryptographicException>(() => cms.CheckSignature(true));
+ cms.CheckHash();
+
+ firstSigner = cms.SignerInfos[0];
+ firstSigner.CheckSignature(verifySignatureOnly: true);
+ Assert.ThrowsAny<CryptographicException>(() => firstSigner.CheckHash());
+
+ SignerInfo firstCounterSigner = firstSigner.CounterSignerInfos[0];
+ Assert.ThrowsAny<CryptographicException>(() => firstCounterSigner.CheckSignature(true));
+
+ if (PlatformDetection.IsFullFramework)
+ {
+ // NetFX's CheckHash only looks at top-level SignerInfos to find the
+ // crypt32 CMS signer ID, so it fails on any check from a countersigner.
+ Assert.ThrowsAny<CryptographicException>(() => firstCounterSigner.CheckHash());
+ }
+ else
+ {
+ firstCounterSigner.CheckHash();
+ }
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public static void AddSecondCounterSignature_NoSignature_WithCert(bool addExtraCert)
+ {
+ AddSecondCounterSignature_NoSignature(withCertificate: true, addExtraCert);
+ }
+
+ [Theory]
+ // On .NET Framework it will prompt for the counter-signer's certificate if it's null,
+ // even if the signature type is NoSignature, so don't run the test there.
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ [InlineData(false)]
+ [InlineData(true)]
+ public static void AddSecondCounterSignature_NoSignature_WithoutCert(bool addExtraCert)
+ {
+ AddSecondCounterSignature_NoSignature(withCertificate: false, addExtraCert);
+ }
+
+ private static void AddSecondCounterSignature_NoSignature(bool withCertificate, bool addExtraCert)
+ {
+ X509Certificate2Collection certs;
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber);
+
+ SignerInfo firstSigner = cms.SignerInfos[0];
+
+ using (X509Certificate2 cert = Certificates.RSAKeyTransferCapi1.TryGetCertificateWithPrivateKey())
+ using (X509Certificate2 cert2 = Certificates.DHKeyAgree1.GetCertificate())
+ {
+ firstSigner.ComputeCounterSignature(
+ new CmsSigner(cert)
+ {
+ IncludeOption = X509IncludeOption.None,
+ });
+
+ CmsSigner counterSigner;
+
+ if (withCertificate)
+ {
+ counterSigner = new CmsSigner(SubjectIdentifierType.NoSignature, cert);
+ }
+ else
+ {
+ counterSigner = new CmsSigner(SubjectIdentifierType.NoSignature);
+ }
+
+ if (addExtraCert)
+ {
+ counterSigner.Certificates.Add(cert2);
+ }
+
+ firstSigner.ComputeCounterSignature(counterSigner);
+
+ certs = cms.Certificates;
+
+ if (addExtraCert)
+ {
+ Assert.Equal(2, certs.Count);
+ Assert.NotEqual(cert2.RawData, certs[0].RawData);
+ Assert.Equal(cert2.RawData, certs[1].RawData);
+ }
+ else
+ {
+ Assert.Equal(1, certs.Count);
+ Assert.NotEqual(cert2.RawData, certs[0].RawData);
+ }
+ }
+
+ Assert.ThrowsAny<CryptographicException>(() => cms.CheckSignature(true));
+ cms.CheckHash();
+
+ byte[] encoded = cms.Encode();
+ cms = new SignedCms();
+ cms.Decode(encoded);
+ Assert.ThrowsAny<CryptographicException>(() => cms.CheckSignature(true));
+ cms.CheckHash();
+
+ firstSigner = cms.SignerInfos[0];
+ firstSigner.CheckSignature(verifySignatureOnly: true);
+ Assert.ThrowsAny<CryptographicException>(() => firstSigner.CheckHash());
+
+ // The NoSignature CounterSigner sorts first.
+ SignerInfo firstCounterSigner = firstSigner.CounterSignerInfos[0];
+ Assert.Equal(SubjectIdentifierType.NoSignature, firstCounterSigner.SignerIdentifier.Type);
+ Assert.ThrowsAny<CryptographicException>(() => firstCounterSigner.CheckSignature(true));
+
+ if (PlatformDetection.IsFullFramework)
+ {
+ // NetFX's CheckHash only looks at top-level SignerInfos to find the
+ // crypt32 CMS signer ID, so it fails on any check from a countersigner.
+ Assert.ThrowsAny<CryptographicException>(() => firstCounterSigner.CheckHash());
+ }
+ else
+ {
+ firstCounterSigner.CheckHash();
+ }
+
+ certs = cms.Certificates;
+
+ if (addExtraCert)
+ {
+ Assert.Equal(2, certs.Count);
+ Assert.Equal("CN=DfHelleKeyAgreement1", certs[1].SubjectName.Name);
+ }
+ else
+ {
+ Assert.Equal(1, certs.Count);
+ }
+
+ Assert.Equal("CN=RSAKeyTransferCapi1", certs[0].SubjectName.Name);
+ }
+
+ [Fact]
[ActiveIssue(31977, TargetFrameworkMonikers.Uap)]
public static void EnsureExtraCertsAdded()
{