public void CheckSignature(bool verifySignatureOnly) => throw null;
public void CheckSignature(System.Security.Cryptography.X509Certificates.X509Certificate2Collection extraStore, bool verifySignatureOnly) => throw null;
public void CheckHash() => throw null;
+ public void AddCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) => throw null;
+ public void RemoveCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) => throw null;
}
public sealed partial class SignerInfo
{
public Oid DigestAlgorithm => throw null;
public CryptographicAttributeObjectCollection SignedAttributes => throw null;
public CryptographicAttributeObjectCollection UnsignedAttributes => throw null;
+ public void AddUnsignedAttribute(System.Security.Cryptography.AsnEncodedData asnEncodedData) => throw null;
+ public void RemoveUnsignedAttribute(System.Security.Cryptography.AsnEncodedData asnEncodedData) => throw null;
public SignerInfoCollection CounterSignerInfos => throw null;
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public void ComputeCounterSignature() => throw null;
<data name="Cryptography_WriteEncodedValue_OneValueAtATime" xml:space="preserve">
<value>The input to WriteEncodedValue must represent a single encoded value with no trailing data.</value>
</data>
-</root>
+ <data name="Cryptography_Cms_NoAttributeFound" xml:space="preserve">
+ <value>Attribute not found.</value>
+ </data>
+ <data name="Cryptography_Cms_NoCertificateFound" xml:space="preserve">
+ <value>Certificate not found.</value>
+ </data>
+ <data name="Cryptography_Cms_CertificateAlreadyInCollection" xml:space="preserve">
+ <value>Certificate already present in the collection.</value>
+ </data>
+</root>
\ No newline at end of file
{
return ref _signedData;
}
+
+ public void AddCertificate(X509Certificate2 certificate)
+ {
+ int existingLength = _signedData.CertificateSet?.Length ?? 0;
+
+ byte[] rawData = certificate.RawData;
+
+ if (existingLength > 0)
+ {
+ foreach (CertificateChoiceAsn cert in _signedData.CertificateSet)
+ {
+ if (cert.Certificate.Value.Span.SequenceEqual(rawData))
+ {
+ throw new CryptographicException(SR.Cryptography_Cms_CertificateAlreadyInCollection);
+ }
+ }
+ }
+
+ if (_signedData.CertificateSet == null)
+ {
+ _signedData.CertificateSet = new CertificateChoiceAsn[1];
+ }
+ else
+ {
+ Array.Resize(ref _signedData.CertificateSet, existingLength + 1);
+ }
+
+ _signedData.CertificateSet[existingLength] = new CertificateChoiceAsn
+ {
+ Certificate = rawData
+ };
+
+ Reencode();
+ }
+
+ public void RemoveCertificate(X509Certificate2 certificate)
+ {
+ int existingLength = _signedData.CertificateSet?.Length ?? 0;
+
+ if (existingLength != 0)
+ {
+ int idx = 0;
+ byte[] rawData = certificate.RawData;
+
+ foreach (CertificateChoiceAsn cert in _signedData.CertificateSet)
+ {
+ if (cert.Certificate.Value.Span.SequenceEqual(rawData))
+ {
+ Helpers.RemoveAt(ref _signedData.CertificateSet, idx);
+ Reencode();
+ return;
+ }
+
+ idx++;
+ }
+ }
+
+ throw new CryptographicException(SR.Cryptography_Cms_NoCertificateFound);
+ }
}
}
public Oid SignatureAlgorithm => new Oid(_signatureAlgorithm);
+ public void AddUnsignedAttribute(AsnEncodedData unsignedAttribute)
+ {
+ int myIdx = _document.SignerInfos.FindIndexForSigner(this);
+
+ if (myIdx < 0)
+ {
+ throw new CryptographicException(SR.Cryptography_Cms_SignerNotFound);
+ }
+
+ ref SignedDataAsn signedData = ref _document.GetRawData();
+ ref SignerInfoAsn mySigner = ref signedData.SignerInfos[myIdx];
+
+ int existingAttribute = mySigner.UnsignedAttributes == null ? -1 : FindAttributeIndexByOid(mySigner.UnsignedAttributes, unsignedAttribute.Oid);
+
+ if (existingAttribute == -1)
+ {
+ // create a new attribute
+ AttributeAsn newUnsignedAttr;
+ using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER))
+ {
+ writer.PushSetOf();
+ writer.WriteEncodedValue(unsignedAttribute.RawData);
+ writer.PopSetOf();
+
+ newUnsignedAttr = new AttributeAsn
+ {
+ AttrType = new Oid(unsignedAttribute.Oid),
+ AttrValues = writer.Encode(),
+ };
+ }
+
+ int newAttributeIdx;
+
+ if (mySigner.UnsignedAttributes == null)
+ {
+ newAttributeIdx = 0;
+ mySigner.UnsignedAttributes = new AttributeAsn[1];
+ }
+ else
+ {
+ newAttributeIdx = mySigner.UnsignedAttributes.Length;
+ Array.Resize(ref mySigner.UnsignedAttributes, newAttributeIdx + 1);
+ }
+
+ mySigner.UnsignedAttributes[newAttributeIdx] = newUnsignedAttr;
+ }
+ else
+ {
+ // merge with existing attribute
+ ref AttributeAsn modifiedAttr = ref mySigner.UnsignedAttributes[existingAttribute];
+
+ using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER))
+ {
+ writer.PushSetOf();
+
+ AsnReader reader = new AsnReader(modifiedAttr.AttrValues, AsnEncodingRules.BER);
+ AsnReader collReader = reader.ReadSetOf();
+
+ if (reader.HasData)
+ {
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+
+ // re-add old values
+ while (collReader.HasData)
+ {
+ writer.WriteEncodedValue(collReader.GetEncodedValue());
+ }
+
+ writer.WriteEncodedValue(unsignedAttribute.RawData);
+
+ writer.PopSetOf();
+ modifiedAttr.AttrValues = writer.Encode();
+ }
+ }
+
+ // Re-normalize the document
+ _document.Reencode();
+ }
+
+ public void RemoveUnsignedAttribute(AsnEncodedData unsignedAttribute)
+ {
+ int myIdx = _document.SignerInfos.FindIndexForSigner(this);
+
+ if (myIdx < 0)
+ {
+ throw new CryptographicException(SR.Cryptography_Cms_SignerNotFound);
+ }
+
+ ref SignedDataAsn signedData = ref _document.GetRawData();
+ ref SignerInfoAsn mySigner = ref signedData.SignerInfos[myIdx];
+
+ (int outerIndex, int innerIndex) = FindAttributeLocation(mySigner.UnsignedAttributes, unsignedAttribute, out bool isOnlyValue);
+
+ if (outerIndex == -1 || innerIndex == -1)
+ {
+ throw new CryptographicException(SR.Cryptography_Cms_NoAttributeFound);
+ }
+
+ if (isOnlyValue)
+ {
+ Helpers.RemoveAt(ref mySigner.UnsignedAttributes, outerIndex);
+ }
+ else
+ {
+ RemoveAttributeValueWithoutIndexChecking(ref mySigner.UnsignedAttributes[outerIndex], innerIndex);
+ }
+
+ // Re-normalize the document
+ _document.Reencode();
+ }
+
private SignerInfoCollection GetCounterSigners(AttributeAsn[] unsignedAttrs)
{
// Since each "attribute" can have multiple "attribute values" there's no real
}
else
{
- ref AttributeAsn modifiedAttr = ref unsignedAttrs[removeAttrIdx];
-
- using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER))
- {
- writer.PushSetOf();
-
- AsnReader reader = new AsnReader(modifiedAttr.AttrValues, writer.RuleSet);
-
- int i = 0;
-
- while (reader.HasData)
- {
- ReadOnlyMemory<byte> encodedValue = reader.GetEncodedValue();
-
- if (i != removeValueIndex)
- {
- writer.WriteEncodedValue(encodedValue);
- }
-
- i++;
- }
-
- writer.PopSetOf();
- modifiedAttr.AttrValues = writer.Encode();
- }
+ RemoveAttributeValueWithoutIndexChecking(ref unsignedAttrs[removeAttrIdx], removeValueIndex);
}
}
return new CryptographicAttributeObject(type, valueColl);
}
+
+ private static int FindAttributeIndexByOid(AttributeAsn[] attributes, Oid oid, int startIndex = 0)
+ {
+ for (int i = startIndex; i < attributes.Length; i++)
+ {
+ if (attributes[i].AttrType.Value == oid.Value)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ private static int FindAttributeValueIndexByEncodedData(ReadOnlyMemory<byte> attributeValues, ReadOnlySpan<byte> asnEncodedData, out bool isOnlyValue)
+ {
+ AsnReader reader = new AsnReader(attributeValues, AsnEncodingRules.BER);
+ AsnReader collReader = reader.ReadSetOf();
+
+ if (reader.HasData)
+ {
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+
+ for (int i = 0; collReader.HasData; i++)
+ {
+ ReadOnlySpan<byte> data = collReader.GetEncodedValue().Span;
+ if (data.SequenceEqual(asnEncodedData))
+ {
+ isOnlyValue = i == 0 && !collReader.HasData;
+ return i;
+ }
+ }
+
+ isOnlyValue = false;
+ return -1;
+ }
+
+ private static (int, int) FindAttributeLocation(AttributeAsn[] attributes, AsnEncodedData attribute, out bool isOnlyValue)
+ {
+ for (int outerIndex = 0; ; outerIndex++)
+ {
+ outerIndex = FindAttributeIndexByOid(attributes, attribute.Oid, outerIndex);
+
+ if (outerIndex == -1)
+ {
+ break;
+ }
+
+ int innerIndex = FindAttributeValueIndexByEncodedData(attributes[outerIndex].AttrValues, attribute.RawData, out isOnlyValue);
+ if (innerIndex != -1)
+ {
+ return (outerIndex, innerIndex);
+ }
+ }
+
+ isOnlyValue = false;
+ return (-1, -1);
+ }
+
+ private static void RemoveAttributeValueWithoutIndexChecking(ref AttributeAsn modifiedAttr, int removeValueIndex)
+ {
+ // Using BER rules to avoid resorting
+ using (AsnWriter writer = new AsnWriter(AsnEncodingRules.BER))
+ {
+ writer.PushSetOf();
+
+ AsnReader reader = new AsnReader(modifiedAttr.AttrValues, writer.RuleSet);
+ AsnReader collReader = reader.ReadSetOf();
+
+ if (reader.HasData)
+ {
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+
+ int i = 0;
+
+ while (collReader.HasData)
+ {
+ ReadOnlyMemory<byte> encodedValue = collReader.GetEncodedValue();
+
+ if (i != removeValueIndex)
+ {
+ writer.WriteEncodedValue(encodedValue);
+ }
+
+ i++;
+ }
+
+ writer.PopSetOf();
+ modifiedAttr.AttrValues = writer.Encode();
+ }
+ }
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Test.Cryptography;
Assert.Throws<CryptographicException>(() => cms.ComputeSignature(signer));
}
}
-
+
+ [Fact]
+ public static void AddCertificate()
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.CounterSignedRsaPkcs1OneSigner);
+
+ int numOfCerts = cms.Certificates.Count;
+
+ using (X509Certificate2 newCert = Certificates.RSAKeyTransfer1.GetCertificate())
+ {
+ cms.AddCertificate(newCert);
+
+ Assert.Equal(numOfCerts + 1, cms.Certificates.Count);
+ Assert.True(cms.Certificates.Contains(newCert));
+
+ cms.CheckSignature(true);
+ }
+ }
+
+ [Fact]
+ public static void AddCertificateWithPrivateKey()
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.CounterSignedRsaPkcs1OneSigner);
+
+ int numOfCerts = cms.Certificates.Count;
+
+ using (X509Certificate2 newCert = Certificates.RSAKeyTransfer1.TryGetCertificateWithPrivateKey())
+ {
+ Assert.True(newCert.HasPrivateKey);
+ cms.AddCertificate(newCert);
+
+ Assert.Equal(numOfCerts + 1, cms.Certificates.Count);
+
+ X509Certificate2 addedCert = cms.Certificates.OfType<X509Certificate2>().Where((cert) => cert.Equals(newCert)).Single();
+ Assert.False(addedCert.HasPrivateKey);
+
+ Assert.Equal(newCert, addedCert);
+
+ cms.CheckSignature(true);
+ }
+ }
+
+ [Fact]
+ public static void RemoveCertificate()
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.CounterSignedRsaPkcs1OneSigner);
+
+ var expectedCerts = new HashSet<X509Certificate2>(cms.Certificates.OfType<X509Certificate2>());
+
+ using (X509Certificate2 cert1 = Certificates.RSAKeyTransfer1.GetCertificate())
+ using (X509Certificate2 cert2 = Certificates.RSAKeyTransfer2.GetCertificate())
+ {
+ Assert.NotEqual(cert1, cert2);
+
+ cms.AddCertificate(cert1);
+ cms.AddCertificate(cert2);
+
+ expectedCerts.Add(cert2);
+
+ cms.RemoveCertificate(cert1);
+
+ Assert.Equal(expectedCerts.Count, cms.Certificates.Count);
+
+ foreach (X509Certificate2 documentCert in cms.Certificates)
+ {
+ Assert.True(expectedCerts.Contains(documentCert));
+ }
+ }
+ }
+
+ [Fact]
+ public static void RemoveNonExistingCertificate()
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.CounterSignedRsaPkcs1OneSigner);
+
+ using (X509Certificate2 certToRemove = Certificates.RSAKeyTransfer1.GetCertificate())
+ {
+ Assert.Throws<CryptographicException>(() => cms.RemoveCertificate(certToRemove));
+ }
+ }
+
+ [Fact]
+ public static void RemoveAllCertsAddBackSignerCert()
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.CounterSignedRsaPkcs1OneSigner);
+
+ SignerInfo signerInfoBeforeRemoval = cms.SignerInfos[0];
+ X509Certificate2 signerCert = signerInfoBeforeRemoval.Certificate;
+
+ while (cms.Certificates.Count > 0)
+ {
+ cms.RemoveCertificate(cms.Certificates[0]);
+ }
+
+ // Signer info should be gone
+ Assert.Throws<CryptographicException>(() => cms.CheckSignature(true));
+
+ Assert.Null(cms.SignerInfos[0].Certificate);
+ Assert.NotNull(signerInfoBeforeRemoval.Certificate);
+
+ cms.AddCertificate(signerCert);
+ cms.CheckSignature(true);
+
+ Assert.Equal(1, cms.Certificates.Count);
+ }
+
+ [Fact]
+ public static void AddExistingCertificate()
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.CounterSignedRsaPkcs1OneSigner);
+
+ using (X509Certificate2 newCert = Certificates.RSAKeyTransfer1.GetCertificate())
+ {
+ cms.AddCertificate(newCert);
+ Assert.Throws<CryptographicException>(() => cms.AddCertificate(newCert));
+ }
+ }
+
private static void VerifyWithExplicitPrivateKey(X509Certificate2 cert, AsymmetricAlgorithm key)
{
using (var pubCert = new X509Certificate2(cert.RawData))
"0906052B0E03021A0500300906072A8648CE380403042F302D021476DCB780CE" +
"D5B308A3630726A85DB97FBC50DFD1021500CDF2649B50500BB7428B9DCA6BEF" +
"2C7E7EF1B79C").HexToByteArray();
+
+ public static byte[] RsaPkcs1TwoCounterSignaturesInSingleAttribute = (
+ "30820BBA06092A864886F70D010702A0820BAB30820BA7020101310D300B0609" +
+ "608648016503040201301406092A864886F70D010701A00704050102030405A0" +
+ "82081D308201583081FFA003020102021035428F3B3C5107AD49E776D6E74C4D" +
+ "C8300A06082A8648CE3D04030230153113301106035504030C0A454344534120" +
+ "54657374301E170D3135303530313030333730335A170D313630353031303035" +
+ "3730335A30153113301106035504030C0A454344534120546573743059301306" +
+ "072A8648CE3D020106082A8648CE3D030107034200047590F69CA114E92927E0" +
+ "34C997B7C882A8C992AC00CEFB4EB831901536F291E1B515263BCD20E1EA3249" +
+ "6FDAC84E2D8D1B703266A9088F6EAF652549D9BB63D5A331302F300E0603551D" +
+ "0F0101FF040403020388301D0603551D0E0416041411218A92C5EB12273B3C5C" +
+ "CFB8220CCCFDF387DB300A06082A8648CE3D040302034800304502201AFE595E" +
+ "19F1AE4B6A4B231E8851926438C55B5DDE632E6ADF13C1023A65898E022100CB" +
+ "DF434FDD197D8B594E8026E44263BADE773C2BEBD060CC4109484A498E7C7E30" +
+ "82032C30820214A003020102020900E0D8AB6819D7306E300D06092A864886F7" +
+ "0D01010B05003038313630340603550403132D54776F2074686F7573616E6420" +
+ "666F7274792065696768742062697473206F662052534120676F6F646E657373" +
+ "301E170D3137313130333233353131355A170D3138313130333233353131355A" +
+ "3038313630340603550403132D54776F2074686F7573616E6420666F72747920" +
+ "65696768742062697473206F662052534120676F6F646E65737330820122300D" +
+ "06092A864886F70D01010105000382010F003082010A028201010096C114A589" +
+ "8D09133EF859F89C1D848BA8CB5258793E05B92D499C55EEFACE274BBBC26803" +
+ "FB813B9C11C6898153CC1745DED2C4D2672F807F0B2D957BC4B65EBC9DDE26E2" +
+ "EA7B2A6FE9A7C4D8BD1EF6032B8F0BB6AA33C8B57248B3D5E3901D8A38A283D7" +
+ "E25FF8E6F522381EE5484234CFF7B30C174635418FA89E14C468AD89DCFCBBB5" +
+ "35E5AF53510F9EA7F9DA8C1B53375B6DAB95A291439A5648726EE1012E41388E" +
+ "100691642CF6917F5569D8351F2782F435A579014E8448EEA0C4AECAFF2F4767" +
+ "99D88457E2C8BCB56E5E128782B4FE26AFF0720D91D52CCAFE344255808F5271" +
+ "D09F784F787E8323182080915BE0AE15A71D66476D0F264DD084F30203010001" +
+ "A3393037301D0603551D0E04160414745B5F12EF962E84B897E246D399A2BADE" +
+ "A9C5AC30090603551D1304023000300B0603551D0F040403020780300D06092A" +
+ "864886F70D01010B0500038201010087A15DF37FBD6E9DED7A8FFF25E60B731F" +
+ "635469BA01DD14BC03B2A24D99EFD8B894E9493D63EC88C496CB04B33DF25222" +
+ "544F23D43F4023612C4D97B719C1F9431E4DB7A580CDF66A3E5F0DAF89A267DD" +
+ "187ABFFB08361B1F79232376AA5FC5AD384CC2F98FE36C1CEA0B943E1E396119" +
+ "0648889C8ABE8397A5A338843CBFB1D8B212BE46685ACE7B80475CC7C97FC037" +
+ "7936ABD5F664E9C09C463897726650711A1110FA9866BC1C278D95E5636AB96F" +
+ "AE95CCD67FD572A8C727E2C03E7B242457318BEC1BE52CA5BD9454A0A41140AE" +
+ "96ED1C56D220D1FD5DD3B1B4FB2AA0E04FC94F7E3C7D476F298962245563953A" +
+ "D7225EDCEAC8B8509E49292E62D8BF3082038D3082034AA003020102020900AB" +
+ "740A714AA83C92300B060960864801650304030230818D310B30090603550406" +
+ "13025553311330110603550408130A57617368696E67746F6E3110300E060355" +
+ "040713075265646D6F6E64311E301C060355040A13154D6963726F736F667420" +
+ "436F72706F726174696F6E3120301E060355040B13172E4E4554204672616D65" +
+ "776F726B2028436F7265465829311530130603550403130C313032342D626974" +
+ "20445341301E170D3135313132353134343030335A170D313531323235313434" +
+ "3030335A30818D310B3009060355040613025553311330110603550408130A57" +
+ "617368696E67746F6E3110300E060355040713075265646D6F6E64311E301C06" +
+ "0355040A13154D6963726F736F667420436F72706F726174696F6E3120301E06" +
+ "0355040B13172E4E4554204672616D65776F726B2028436F7265465829311530" +
+ "130603550403130C313032342D62697420445341308201B73082012C06072A86" +
+ "48CE3804013082011F02818100AEE3309FC7C9DB750D4C3797D333B3B9B234B4" +
+ "62868DB6FFBDED790B7FC8DDD574C2BD6F5E749622507AB2C09DF5EAAD84859F" +
+ "C0706A70BB8C9C8BE22B4890EF2325280E3A7F9A3CE341DBABEF6058D063EA67" +
+ "83478FF8B3B7A45E0CA3F7BAC9995DCFDDD56DF168E91349130F719A4E717351" +
+ "FAAD1A77EAC043611DC5CC5A7F021500D23428A76743EA3B49C62EF0AA17314A" +
+ "85415F0902818100853F830BDAA738465300CFEE02418E6B07965658EAFDA7E3" +
+ "38A2EB1531C0E0CA5EF1A12D9DDC7B550A5A205D1FF87F69500A4E4AF5759F3F" +
+ "6E7F0C48C55396B738164D9E35FB506BD50E090F6A497C70E7E868C61BD4477C" +
+ "1D62922B3DBB40B688DE7C175447E2E826901A109FAD624F1481B276BF63A665" +
+ "D99C87CEE9FD06330381840002818025B8E7078E149BAC352667623620029F5E" +
+ "4A5D4126E336D56F1189F9FF71EA671B844EBD351514F27B69685DDF716B32F1" +
+ "02D60EA520D56F544D19B2F08F5D9BDDA3CBA3A73287E21E559E6A07586194AF" +
+ "AC4F6E721EDCE49DE0029627626D7BD30EEB337311DB4FF62D7608997B6CC32E" +
+ "9C42859820CA7EF399590D5A388C48A330302E302C0603551D11042530238704" +
+ "7F00000187100000000000000000000000000000000182096C6F63616C686F73" +
+ "74300B0609608648016503040302033000302D021500B9316CC7E05C9F79197E" +
+ "0B41F6FD4E3FCEB72A8A0214075505CCAECB18B7EF4C00F9C069FA3BC78014DE" +
+ "3182035A3082035602010130453038313630340603550403132D54776F207468" +
+ "6F7573616E6420666F7274792065696768742062697473206F66205253412067" +
+ "6F6F646E657373020900E0D8AB6819D7306E300B060960864801650304020130" +
+ "0B06092A864886F70D01010104820100457E2996B3A1AE5C7DC2F4EF4D9010F4" +
+ "8B62B72DFB43F2EDC503FD32408A1058EE7BBCF4750CB4B4242B11A599C40792" +
+ "70D32D15A57FF791FF59836A027E634B9B97E1764173597A9A6155D5ED5365F6" +
+ "5DF14FDD15928ABD63E1409DBF2D1A713D20D80E09EE76BC63775F3FA8638A26" +
+ "ED3816FF87C7CDC8A9299485055BFC38AE158BB6577812AA98436FB54844544A" +
+ "C92CD449690B8107447044580FAE590D8A7326A8D139886C8A4AC8CEEACB0458" +
+ "1666D8447D267F1A9E9CAB20F155E05D5EC055AC863C047B5E1E3A98528EA766" +
+ "7C19B33AD98B2D33ABBD7E607C1DA18BCDB87C626554C277E069CE9EC489BC87" +
+ "2E7DEAED4C642DE5AB10BD2D558EAFB3A18201EA308201E606092A864886F70D" +
+ "010906318201D73082010D02010130819B30818D310B30090603550406130255" +
+ "53311330110603550408130A57617368696E67746F6E3110300E060355040713" +
+ "075265646D6F6E64311E301C060355040A13154D6963726F736F667420436F72" +
+ "706F726174696F6E3120301E060355040B13172E4E4554204672616D65776F72" +
+ "6B2028436F7265465829311530130603550403130C313032342D626974204453" +
+ "41020900AB740A714AA83C92300706052B0E03021AA025302306092A864886F7" +
+ "0D0109043116041409200943E2EDD3DD3B186C5839BDC9B1051903FF30090607" +
+ "2A8648CE380403042F302D0215009FDBE95176B1EC0697155ADDF335E5126A9F" +
+ "59D60214736F650C74E73BEA577151BCFD226FEDC06832E53081C30201013029" +
+ "30153113301106035504030C0A45434453412054657374021035428F3B3C5107" +
+ "AD49E776D6E74C4DC8300B0609608648016503040201A031302F06092A864886" +
+ "F70D01090431220420DF5D49DB775A8F94CAB3129038B200EDE9FCD2AE8F039D" +
+ "B1AB96D9B827D299D2300A06082A8648CE3D0403020447304502202327A60E1A" +
+ "5A798CD29B72C7C7991F968D29DB15C4865BEE83A7E2FD73326CA4022100899F" +
+ "000179F77BFE296783548EAE56BA7F53C0DB0563A27A36A149BAEC9C23AC").HexToByteArray();
}
}
namespace System.Security.Cryptography.Pkcs.Tests
{
- public static class SignerInfoTests
+ public static partial class SignerInfoTests
{
[Fact]
public static void SignerInfo_SignedAttributes_Cached_WhenEmpty()
() => signerInfo.RemoveCounterSignature(signerInfo));
}
+ [Theory]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "NetFx bug")]
+ [InlineData(0)]
+ [InlineData(1)]
+ public static void RemoveCounterSignature_EncodedInSingleAttribute(int indexToRemove)
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.RsaPkcs1TwoCounterSignaturesInSingleAttribute);
+ SignerInfo signerInfo = cms.SignerInfos[0];
+
+ Assert.Equal(2, signerInfo.CounterSignerInfos.Count);
+ signerInfo.RemoveCounterSignature(indexToRemove);
+ Assert.Equal(1, signerInfo.CounterSignerInfos.Count);
+
+ cms.CheckSignature(true);
+ }
+
[Fact]
public static void RemoveCounterSignature_Null()
{
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography.X509Certificates;
+using Test.Cryptography;
+using Xunit;
+
+namespace System.Security.Cryptography.Pkcs.Tests
+{
+ public static partial class SignerInfoTests
+ {
+ private const string TokenAttributeOid = "1.2.840.113549.1.9.16.2.14";
+
+ [Fact]
+ public static void SignerInfo_AddUnsignedAttribute_Adds()
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber);
+
+ Assert.Equal(0, cms.SignerInfos[0].UnsignedAttributes.Count);
+
+ AsnEncodedData attribute1 = CreateTimestampToken(1);
+ cms.SignerInfos[0].AddUnsignedAttribute(attribute1);
+
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes.Count);
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes[0].Values.Count);
+ VerifyAttributesAreEqual(cms.SignerInfos[0].UnsignedAttributes[0].Values[0], attribute1);
+
+ ReReadSignedCms(ref cms);
+
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes.Count);
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes[0].Values.Count);
+ VerifyAttributesAreEqual(cms.SignerInfos[0].UnsignedAttributes[0].Values[0], attribute1);
+
+ AsnEncodedData attribute2 = CreateTimestampToken(2);
+
+ cms.SignerInfos[0].AddUnsignedAttribute(attribute2);
+
+ var expectedAttributes = new List<AsnEncodedData>();
+ expectedAttributes.Add(attribute1);
+ expectedAttributes.Add(attribute2);
+
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes.Count);
+ Assert.Equal(2, cms.SignerInfos[0].UnsignedAttributes[0].Values.Count);
+ VerifyAttributesContainsAll(cms.SignerInfos[0].UnsignedAttributes, expectedAttributes);
+
+ ReReadSignedCms(ref cms);
+
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes.Count);
+ Assert.Equal(2, cms.SignerInfos[0].UnsignedAttributes[0].Values.Count);
+ VerifyAttributesContainsAll(cms.SignerInfos[0].UnsignedAttributes, expectedAttributes);
+ }
+
+ [Fact]
+ public static void SignerInfo_RemoveUnsignedAttribute_RemoveCounterSignature()
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners);
+
+ Assert.Equal(2, cms.SignerInfos[0].UnsignedAttributes.Count);
+ Assert.Equal(2, cms.SignerInfos[0].CounterSignerInfos.Count);
+ byte[] secondSignerCounterSignature = cms.SignerInfos[0].CounterSignerInfos[1].GetSignature();
+
+ cms.SignerInfos[0].RemoveUnsignedAttribute(cms.SignerInfos[0].UnsignedAttributes[0].Values[0]);
+
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes.Count);
+ Assert.Equal(1, cms.SignerInfos[0].CounterSignerInfos.Count);
+ Assert.Equal(secondSignerCounterSignature, cms.SignerInfos[0].CounterSignerInfos[0].GetSignature());
+
+ ReReadSignedCms(ref cms);
+
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes.Count);
+ Assert.Equal(1, cms.SignerInfos[0].CounterSignerInfos.Count);
+ Assert.Equal(secondSignerCounterSignature, cms.SignerInfos[0].CounterSignerInfos[0].GetSignature());
+ }
+
+ [Theory]
+ [MemberData(nameof(SignedDocumentsWithAttributesTestData))]
+ public static void SignerInfo_RemoveUnsignedAttributes_RemoveAllAttributesFromBeginning(byte[] document)
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(document);
+
+ List<AsnEncodedData> attributes = GetAllAsnEncodedDataFromAttributes(cms.SignerInfos[0].UnsignedAttributes);
+ Assert.True(attributes.Count > 0);
+
+ for (int i = 0; i < attributes.Count; i++)
+ {
+ AsnEncodedData attribute = attributes[i];
+ cms.SignerInfos[0].RemoveUnsignedAttribute(attribute);
+ attributes.RemoveAt(0);
+
+ VerifyAttributesContainsAll(cms.SignerInfos[0].UnsignedAttributes, attributes);
+
+ ReReadSignedCms(ref cms);
+ VerifyAttributesContainsAll(cms.SignerInfos[0].UnsignedAttributes, attributes);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(SignedDocumentsWithAttributesTestData))]
+ public static void SignerInfo_RemoveUnsignedAttributes_RemoveAllAttributesFromEnd(byte[] document)
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(document);
+
+ List<AsnEncodedData> attributes = GetAllAsnEncodedDataFromAttributes(cms.SignerInfos[0].UnsignedAttributes);
+ Assert.True(attributes.Count > 0);
+
+ for (int i = attributes.Count - 1; i >= 0; i--)
+ {
+ AsnEncodedData attribute = attributes[i];
+ cms.SignerInfos[0].RemoveUnsignedAttribute(attribute);
+ attributes.RemoveAt(i);
+
+ VerifyAttributesContainsAll(cms.SignerInfos[0].UnsignedAttributes, attributes);
+
+ ReReadSignedCms(ref cms);
+ VerifyAttributesContainsAll(cms.SignerInfos[0].UnsignedAttributes, attributes);
+ }
+ }
+
+ [Fact]
+ public static void SignerInfo_RemoveUnsignedAttributes_RemoveWithNonMatchingOid()
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners);
+
+ int numberOfAttributes = cms.SignerInfos[0].UnsignedAttributes.Count;
+ Assert.NotEqual(0, numberOfAttributes);
+
+ AsnEncodedData fakeAttribute = new AsnEncodedData(new Oid("1.2.3.4", "1.2.3.4"), cms.SignerInfos[0].UnsignedAttributes[0].Values[0].RawData);
+ Assert.Throws<CryptographicException>(() => cms.SignerInfos[0].RemoveUnsignedAttribute(fakeAttribute));
+
+ Assert.Equal(numberOfAttributes, cms.SignerInfos[0].UnsignedAttributes.Count);
+ }
+
+ [Fact]
+ public static void SignerInfo_RemoveUnsignedAttributes_RemoveWithNonMatchingData()
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.OneRsaSignerTwoRsaCounterSigners);
+
+ int numberOfAttributes = cms.SignerInfos[0].UnsignedAttributes.Count;
+ Assert.NotEqual(0, numberOfAttributes);
+
+ AsnEncodedData fakeAttribute = new AsnEncodedData(
+ cms.SignerInfos[0].UnsignedAttributes[0].Oid,
+ cms.SignerInfos[0].UnsignedAttributes[0].Values[0].RawData.Skip(1).ToArray());
+ Assert.Throws<CryptographicException>(() => cms.SignerInfos[0].RemoveUnsignedAttribute(fakeAttribute));
+
+ Assert.Equal(numberOfAttributes, cms.SignerInfos[0].UnsignedAttributes.Count);
+ }
+
+ [Fact]
+ public static void SignerInfo_RemoveUnsignedAttributes_MultipleAttributeValues()
+ {
+ SignedCms cms = new SignedCms();
+ cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber);
+
+ Assert.Equal(0, cms.SignerInfos[0].UnsignedAttributes.Count);
+
+ AsnEncodedData attribute1 = CreateTimestampToken(1);
+ AsnEncodedData attribute2 = CreateTimestampToken(2);
+ cms.SignerInfos[0].AddUnsignedAttribute(attribute1);
+ cms.SignerInfos[0].AddUnsignedAttribute(attribute2);
+
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes.Count);
+ Assert.Equal(2, cms.SignerInfos[0].UnsignedAttributes[0].Values.Count);
+
+ cms.SignerInfos[0].RemoveUnsignedAttribute(attribute1);
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes.Count);
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes[0].Values.Count);
+ Assert.True(AsnEncodedDataEqual(attribute2, cms.SignerInfos[0].UnsignedAttributes[0].Values[0]));
+
+ cms.SignerInfos[0].RemoveUnsignedAttribute(attribute2);
+ Assert.Equal(0, cms.SignerInfos[0].UnsignedAttributes.Count);
+ }
+
+ [Fact]
+ public static void SignerInfo_AddRemoveUnsignedAttributes_JoinCounterSignaturesAttributesIntoOne()
+ {
+ byte[] message = { 1, 2, 3, 4, 5 };
+ ContentInfo content = new ContentInfo(message);
+ SignedCms cms = new SignedCms(content);
+
+ using (X509Certificate2 signerCert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey())
+ {
+ CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, signerCert);
+ cms.ComputeSignature(signer);
+ }
+
+ using (X509Certificate2 counterSigner1cert = Certificates.Dsa1024.TryGetCertificateWithPrivateKey())
+ {
+ CmsSigner counterSigner = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, counterSigner1cert);
+ counterSigner.IncludeOption = X509IncludeOption.EndCertOnly;
+ counterSigner.DigestAlgorithm = new Oid(Oids.Sha1, Oids.Sha1);
+ cms.SignerInfos[0].ComputeCounterSignature(counterSigner);
+ }
+
+ using (X509Certificate2 counterSigner2cert = Certificates.ECDsaP256Win.TryGetCertificateWithPrivateKey())
+ {
+ CmsSigner counterSigner = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, counterSigner2cert);
+ cms.SignerInfos[0].ComputeCounterSignature(counterSigner);
+ }
+
+ Assert.Equal(2, cms.SignerInfos[0].UnsignedAttributes.Count);
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes[0].Values.Count);
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes[1].Values.Count);
+ cms.CheckSignature(true);
+
+ AsnEncodedData counterSignature = cms.SignerInfos[0].UnsignedAttributes[0].Values[0];
+ cms.SignerInfos[0].RemoveUnsignedAttribute(counterSignature);
+ cms.SignerInfos[0].AddUnsignedAttribute(counterSignature);
+
+ Assert.Equal(1, cms.SignerInfos[0].UnsignedAttributes.Count);
+ Assert.Equal(2, cms.SignerInfos[0].UnsignedAttributes[0].Values.Count);
+ cms.CheckSignature(true);
+ }
+
+ private static void VerifyAttributesContainsAll(CryptographicAttributeObjectCollection attributes, List<AsnEncodedData> expectedAttributes)
+ {
+ var indices = new HashSet<int>();
+ foreach (CryptographicAttributeObject attribute in attributes)
+ {
+ foreach (AsnEncodedData attributeValue in attribute.Values)
+ {
+ int idx = FindAsnEncodedData(expectedAttributes, attributeValue);
+ Assert.NotEqual(-1, idx);
+ indices.Add(idx);
+ }
+ }
+
+ Assert.Equal(expectedAttributes.Count, indices.Count);
+ }
+
+ private static int FindAsnEncodedData(List<AsnEncodedData> array, AsnEncodedData data)
+ {
+ for (int i = 0; i < array.Count; i++)
+ {
+ if (AsnEncodedDataEqual(array[i], data))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ private static List<AsnEncodedData> GetAllAsnEncodedDataFromAttributes(CryptographicAttributeObjectCollection attributes)
+ {
+ var ret = new List<AsnEncodedData>();
+ foreach (CryptographicAttributeObject attribute in attributes)
+ {
+ foreach (AsnEncodedData attributeValue in attribute.Values)
+ {
+ ret.Add(attributeValue);
+ }
+ }
+
+ return ret;
+ }
+
+ private static bool AsnEncodedDataEqual(AsnEncodedData a, AsnEncodedData b)
+ {
+ return a.Oid.Value == b.Oid.Value && a.RawData.SequenceEqual(b.RawData);
+ }
+
+ private static void ReReadSignedCms(ref SignedCms cms)
+ {
+ byte[] bytes = cms.Encode();
+
+ cms = new SignedCms();
+ cms.Decode(bytes);
+ }
+
+ private static AsnEncodedData CreateTimestampToken(byte serial)
+ {
+ Oid tokenOid = new Oid(TokenAttributeOid, TokenAttributeOid);
+
+ Oid policyId = new Oid("0.0", "0.0");
+ Oid hashAlgorithmId = new Oid(Oids.Sha256);
+
+ var tokenInfo = new Rfc3161TimestampTokenInfo(
+ policyId,
+ hashAlgorithmId,
+ new byte[256 / 8],
+ new byte[] { (byte)serial },
+ DateTimeOffset.UtcNow);
+
+ return new AsnEncodedData(tokenOid, tokenInfo.Encode());
+ }
+
+ private static void VerifyAttributesAreEqual(AsnEncodedData actual, AsnEncodedData expected)
+ {
+ Assert.NotSame(expected.Oid, actual.Oid);
+ Assert.Equal(expected.Oid.Value, actual.Oid.Value);
+ Assert.Equal(expected.Oid.FriendlyName, actual.Oid.FriendlyName);
+
+ // We need to decode bytes because DER and BER may encode the same information slightly differently
+ Rfc3161TimestampTokenInfo expectedToken;
+ Assert.True(Rfc3161TimestampTokenInfo.TryDecode(expected.RawData, out expectedToken, out _));
+
+ Rfc3161TimestampTokenInfo actualToken;
+ Assert.True(Rfc3161TimestampTokenInfo.TryDecode(actual.RawData, out actualToken, out _));
+
+ Assert.Equal(expectedToken.GetSerialNumber().ByteArrayToHex(), actualToken.GetSerialNumber().ByteArrayToHex());
+ Assert.Equal(expectedToken.Timestamp, actualToken.Timestamp);
+ Assert.Equal(expectedToken.HashAlgorithmId.Value, Oids.Sha256);
+ Assert.Equal(expectedToken.HashAlgorithmId.Value, actualToken.HashAlgorithmId.Value);
+ }
+
+ private static IEnumerable<object[]> SignedDocumentsWithAttributesTestData()
+ {
+ yield return new object[] { SignedDocuments.CounterSignedRsaPkcs1OneSigner };
+ yield return new object[] { SignedDocuments.NoSignatureSignedWithAttributesAndCounterSignature };
+ yield return new object[] { SignedDocuments.OneRsaSignerTwoRsaCounterSigners };
+ yield return new object[] { SignedDocuments.RsaPkcs1CounterSignedWithNoSignature };
+ yield return new object[] { SignedDocuments.UnsortedSignerInfos};
+ }
+ }
+}
<Compile Include="Rfc3161\TimestampTokenTestData.cs" />
<Compile Include="Rfc3161\TimestampTokenTests.cs" />
<Compile Include="SignedCms\SignedCmsTests.netcoreapp.cs" />
+ <Compile Include="SignedCms\SignerInfoTests.netcoreapp.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsWindows)' == 'true' AND '$(TargetGroup)'=='netcoreapp'">
<Compile Include="EnvelopedCms\DecryptTests.KeyPersistence.cs" />