private SignerInfoCollection GetCounterSigners(AttributeAsn[] unsignedAttrs) { // Since each "attribute" can have multiple "attribute values" there's no real // correlation to a predictive size here. List <SignerInfo> signerInfos = new List <SignerInfo>(); foreach (AttributeAsn attributeAsn in unsignedAttrs) { if (attributeAsn.AttrType.Value == Oids.CounterSigner) { AsnReader reader = new AsnReader(attributeAsn.AttrValues, AsnEncodingRules.BER); AsnReader collReader = reader.ReadSetOf(); if (reader.HasData) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } while (collReader.HasData) { SignerInfoAsn parsedData = AsnSerializer.Deserialize <SignerInfoAsn>(collReader.GetEncodedValue(), AsnEncodingRules.BER); SignerInfo signerInfo = new SignerInfo(ref parsedData, _document) { _parentSignerInfo = this }; signerInfos.Add(signerInfo); } } } return(new SignerInfoCollection(signerInfos.ToArray())); }
internal SignerInfo(ref SignerInfoAsn parsedData, SignedCms ownerDocument) { Version = parsedData.Version; SignerIdentifier = new SubjectIdentifier(parsedData.Sid); _digestAlgorithm = parsedData.DigestAlgorithm.Algorithm; _signedAttributes = parsedData.SignedAttributes; _signatureAlgorithm = parsedData.SignatureAlgorithm.Algorithm; _signatureAlgorithmParameters = parsedData.SignatureAlgorithm.Parameters; _signature = parsedData.SignatureValue; _unsignedAttributes = parsedData.UnsignedAttributes; _document = ownerDocument; }
public void ComputeCounterSignature(CmsSigner signer) { if (_parentSignerInfo != null) { throw new CryptographicException(SR.Cryptography_Cms_NoCounterCounterSigner); } if (signer == null) { throw new ArgumentNullException(nameof(signer)); } signer.CheckCertificateValue(); int myIdx = _document.SignerInfos.FindIndexForSigner(this); if (myIdx < 0) { throw new CryptographicException(SR.Cryptography_Cms_SignerNotFound); } // Make sure that we're using the most up-to-date version of this that we can. SignerInfo effectiveThis = _document.SignerInfos[myIdx]; X509Certificate2Collection chain; SignerInfoAsn newSignerInfo = signer.Sign(effectiveThis._signature, null, false, out chain); AttributeAsn newUnsignedAttr; using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) { writer.PushSetOf(); AsnSerializer.Serialize(newSignerInfo, writer); writer.PopSetOf(); newUnsignedAttr = new AttributeAsn { AttrType = new Oid(Oids.CounterSigner, Oids.CounterSigner), AttrValues = writer.Encode(), }; } ref SignedDataAsn signedData = ref _document.GetRawData();
internal SignerInfo(ref SignerInfoAsn parsedData, SignedCms ownerDocument) { Version = parsedData.Version; SignerIdentifier = new SubjectIdentifier(parsedData.Sid); _digestAlgorithm = parsedData.DigestAlgorithm.Algorithm; _signedAttributesMemory = parsedData.SignedAttributes; _signatureAlgorithm = parsedData.SignatureAlgorithm.Algorithm; _signatureAlgorithmParameters = parsedData.SignatureAlgorithm.Parameters; _signature = parsedData.SignatureValue; _unsignedAttributes = parsedData.UnsignedAttributes; if (_signedAttributesMemory.HasValue) { SignedAttributesSet signedSet = SignedAttributesSet.Decode( _signedAttributesMemory.Value, AsnEncodingRules.BER); _signedAttributes = signedSet.SignedAttributes; Debug.Assert(_signedAttributes != null); } _document = ownerDocument; }
internal SignerInfoAsn Sign( ReadOnlyMemory <byte> data, string contentTypeOid, bool silent, out X509Certificate2Collection chainCerts) { HashAlgorithmName hashAlgorithmName = PkcsHelpers.GetDigestAlgorithm(DigestAlgorithm); IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithmName); hasher.AppendData(data.Span); byte[] dataHash = hasher.GetHashAndReset(); SignerInfoAsn newSignerInfo = new SignerInfoAsn(); newSignerInfo.DigestAlgorithm.Algorithm = DigestAlgorithm; // If the user specified attributes (not null, count > 0) we need attributes. // If the content type is null we're counter-signing, and need the message digest attr. // If the content type is otherwise not-data we need to record it as the content-type attr. if (SignedAttributes?.Count > 0 || contentTypeOid != Oids.Pkcs7Data) { List <AttributeAsn> signedAttrs = BuildAttributes(SignedAttributes); using (var writer = new AsnWriter(AsnEncodingRules.DER)) { writer.WriteOctetString(dataHash); signedAttrs.Add( new AttributeAsn { AttrType = new Oid(Oids.MessageDigest, Oids.MessageDigest), AttrValues = new[] { new ReadOnlyMemory <byte>(writer.Encode()) }, }); } if (contentTypeOid != null) { using (var writer = new AsnWriter(AsnEncodingRules.DER)) { writer.WriteObjectIdentifier(contentTypeOid); signedAttrs.Add( new AttributeAsn { AttrType = new Oid(Oids.ContentType, Oids.ContentType), AttrValues = new[] { new ReadOnlyMemory <byte>(writer.Encode()) }, }); } } // Use the serializer/deserializer to DER-normalize the attribute order. SignedAttributesSet signedAttrsSet = new SignedAttributesSet(); signedAttrsSet.SignedAttributes = PkcsHelpers.NormalizeAttributeSet( signedAttrs.ToArray(), normalized => hasher.AppendData(normalized)); // Since this contains user data in a context where BER is permitted, use BER. // There shouldn't be any observable difference here between BER and DER, though, // since the top level fields were written by NormalizeSet. using (AsnWriter attrsWriter = new AsnWriter(AsnEncodingRules.BER)) { signedAttrsSet.Encode(attrsWriter); newSignerInfo.SignedAttributes = attrsWriter.Encode(); } dataHash = hasher.GetHashAndReset(); } switch (SignerIdentifierType) { case SubjectIdentifierType.IssuerAndSerialNumber: byte[] serial = Certificate.GetSerialNumber(); Array.Reverse(serial); newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn { Issuer = Certificate.IssuerName.RawData, SerialNumber = serial, }; newSignerInfo.Version = 1; break; case SubjectIdentifierType.SubjectKeyIdentifier: newSignerInfo.Sid.SubjectKeyIdentifier = PkcsPal.Instance.GetSubjectKeyIdentifier(Certificate); newSignerInfo.Version = 3; break; case SubjectIdentifierType.NoSignature: newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn { Issuer = SubjectIdentifier.DummySignerEncodedValue, SerialNumber = new byte[1], }; newSignerInfo.Version = 1; break; default: Debug.Fail($"Unresolved SignerIdentifierType value: {SignerIdentifierType}"); throw new CryptographicException(); } if (UnsignedAttributes != null && UnsignedAttributes.Count > 0) { List <AttributeAsn> attrs = BuildAttributes(UnsignedAttributes); newSignerInfo.UnsignedAttributes = PkcsHelpers.NormalizeAttributeSet(attrs.ToArray()); } bool signed; Oid signatureAlgorithm; ReadOnlyMemory <byte> signatureValue; if (SignerIdentifierType == SubjectIdentifierType.NoSignature) { signatureAlgorithm = new Oid(Oids.NoSignature, null); signatureValue = dataHash; signed = true; } else { signed = CmsSignature.Sign( dataHash, hashAlgorithmName, Certificate, PrivateKey, silent, out signatureAlgorithm, out signatureValue); } if (!signed) { throw new CryptographicException(SR.Cryptography_Cms_CannotDetermineSignatureAlgorithm); } newSignerInfo.SignatureValue = signatureValue; newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm; X509Certificate2Collection certs = new X509Certificate2Collection(); certs.AddRange(Certificates); if (SignerIdentifierType != SubjectIdentifierType.NoSignature) { if (IncludeOption == X509IncludeOption.EndCertOnly) { certs.Add(Certificate); } else if (IncludeOption != X509IncludeOption.None) { X509Chain chain = new X509Chain(); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags; if (!chain.Build(Certificate)) { foreach (X509ChainStatus status in chain.ChainStatus) { if (status.Status == X509ChainStatusFlags.PartialChain) { throw new CryptographicException(SR.Cryptography_Cms_IncompleteCertChain); } } } X509ChainElementCollection elements = chain.ChainElements; int count = elements.Count; int last = count - 1; if (last == 0) { // If there's always one cert treat it as EE, not root. last = -1; } for (int i = 0; i < count; i++) { X509Certificate2 cert = elements[i].Certificate; if (i == last && IncludeOption == X509IncludeOption.ExcludeRoot && cert.SubjectName.RawData.AsSpan().SequenceEqual(cert.IssuerName.RawData)) { break; } certs.Add(cert); } } } chainCerts = certs; return(newSignerInfo); }
public void ComputeSignature(CmsSigner signer, bool silent) { if (signer == null) { throw new ArgumentNullException(nameof(signer)); } // While it shouldn't be possible to change the length of ContentInfo.Content // after it's built, use the property at this stage, then use the saved value // (if applicable) after this point. if (ContentInfo.Content.Length == 0) { 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 .NET Framework behavior. throw new CryptographicException(SR.Cryptography_Cms_Sign_No_Signature_First_Signer); } if (signer.Certificate == null && signer.SignerIdentifierType != SubjectIdentifierType.NoSignature) { if (silent) { // .NET Framework 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; string contentType = _contentType ?? ContentInfo.ContentType.Value ?? Oids.Pkcs7Data; X509Certificate2Collection chainCerts; SignerInfoAsn newSigner = signer.Sign(content, contentType, silent, out chainCerts); bool firstSigner = false; if (!_hasData) { firstSigner = true; _signedData = new SignedDataAsn { DigestAlgorithms = Array.Empty <AlgorithmIdentifierAsn>(), SignerInfos = Array.Empty <SignerInfoAsn>(), EncapContentInfo = new EncapsulatedContentInfoAsn { ContentType = contentType }, }; // Since we're going to call Decode before this method exits we don't need to save // the copy of _heldContent or _contentType here if we're attached. if (!Detached) { using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) { writer.WriteOctetString(content.Span); _signedData.EncapContentInfo.Content = writer.Encode(); } } _hasData = true; } int newIdx = _signedData.SignerInfos.Length; Array.Resize(ref _signedData.SignerInfos, newIdx + 1); _signedData.SignerInfos[newIdx] = newSigner; UpdateCertificatesFromAddition(chainCerts); ConsiderDigestAddition(newSigner.DigestAlgorithm); UpdateMetadata(); if (firstSigner) { Reencode(); Debug.Assert(_heldContent != null); Debug.Assert(_contentType == contentType); } }
public void ComputeSignature(CmsSigner signer, bool silent) { if (signer == null) { throw new ArgumentNullException(nameof(signer)); } // While it shouldn't be possible to change the length of ContentInfo.Content // after it's built, use the property at this stage, then use the saved value // (if applicable) after this point. if (ContentInfo.Content.Length == 0) { throw new CryptographicException(SR.Cryptography_Cms_Sign_Empty_Content); } // 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; string contentType = _contentType ?? ContentInfo.ContentType.Value ?? Oids.Pkcs7Data; X509Certificate2Collection chainCerts; SignerInfoAsn newSigner = signer.Sign(content, contentType, silent, out chainCerts); bool firstSigner = false; if (!_hasData) { firstSigner = true; _signedData = new SignedDataAsn { DigestAlgorithms = Array.Empty <AlgorithmIdentifierAsn>(), SignerInfos = Array.Empty <SignerInfoAsn>(), EncapContentInfo = new EncapsulatedContentInfoAsn { ContentType = contentType }, }; // Since we're going to call Decode before this method exits we don't need to save // the copy of _heldContent or _contentType here if we're attached. if (!Detached) { using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER)) { writer.WriteOctetString(content.Span); _signedData.EncapContentInfo.Content = writer.Encode(); } } _hasData = true; } int newIdx = _signedData.SignerInfos.Length; Array.Resize(ref _signedData.SignerInfos, newIdx + 1); _signedData.SignerInfos[newIdx] = newSigner; UpdateCertificatesFromAddition(chainCerts); ConsiderDigestAddition(newSigner.DigestAlgorithm); UpdateMetadata(); if (firstSigner) { Reencode(); Debug.Assert(_heldContent != null); Debug.Assert(_contentType == contentType); } }
internal SignerInfoAsn Sign( ReadOnlyMemory <byte> data, string contentTypeOid, bool silent, out X509Certificate2Collection chainCerts) { HashAlgorithmName hashAlgorithmName = Helpers.GetDigestAlgorithm(DigestAlgorithm); IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithmName); hasher.AppendData(data.Span); byte[] dataHash = hasher.GetHashAndReset(); SignerInfoAsn newSignerInfo = new SignerInfoAsn(); newSignerInfo.DigestAlgorithm.Algorithm = DigestAlgorithm; if ((SignedAttributes != null && SignedAttributes.Count > 0) || contentTypeOid == null) { List <AttributeAsn> signedAttrs = BuildAttributes(SignedAttributes); using (var writer = new AsnWriter(AsnEncodingRules.DER)) { writer.PushSetOf(); writer.WriteOctetString(dataHash); writer.PopSetOf(); signedAttrs.Add( new AttributeAsn { AttrType = new Oid(Oids.MessageDigest, Oids.MessageDigest), AttrValues = writer.Encode(), }); } if (contentTypeOid != null) { using (var writer = new AsnWriter(AsnEncodingRules.DER)) { writer.PushSetOf(); writer.WriteObjectIdentifier(contentTypeOid); writer.PopSetOf(); signedAttrs.Add( new AttributeAsn { AttrType = new Oid(Oids.ContentType, Oids.ContentType), AttrValues = writer.Encode(), }); } } // Use the serializer/deserializer to DER-normalize the attribute order. newSignerInfo.SignedAttributes = Helpers.NormalizeSet( signedAttrs.ToArray(), normalized => { AsnReader reader = new AsnReader(normalized, AsnEncodingRules.DER); hasher.AppendData(reader.PeekContentBytes().Span); }); dataHash = hasher.GetHashAndReset(); } switch (SignerIdentifierType) { case SubjectIdentifierType.IssuerAndSerialNumber: byte[] serial = Certificate.GetSerialNumber(); Array.Reverse(serial); newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn { Issuer = Certificate.IssuerName.RawData, SerialNumber = serial, }; newSignerInfo.Version = 1; break; case SubjectIdentifierType.SubjectKeyIdentifier: newSignerInfo.Sid.SubjectKeyIdentifier = Certificate.GetSubjectKeyIdentifier(); newSignerInfo.Version = 3; break; case SubjectIdentifierType.NoSignature: newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn { Issuer = SubjectIdentifier.DummySignerEncodedValue, SerialNumber = new byte[1], }; newSignerInfo.Version = 1; break; default: Debug.Fail($"Unresolved SignerIdentifierType value: {SignerIdentifierType}"); throw new CryptographicException(); } if (UnsignedAttributes != null && UnsignedAttributes.Count > 0) { List <AttributeAsn> attrs = BuildAttributes(UnsignedAttributes); newSignerInfo.UnsignedAttributes = Helpers.NormalizeSet(attrs.ToArray()); } bool signed = CmsSignature.Sign( dataHash, hashAlgorithmName, Certificate, PrivateKey, silent, out Oid signatureAlgorithm, out ReadOnlyMemory <byte> signatureValue); if (!signed) { throw new CryptographicException(SR.Cryptography_Cms_CannotDetermineSignatureAlgorithm); } newSignerInfo.SignatureValue = signatureValue; newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm; X509Certificate2Collection certs = new X509Certificate2Collection(); certs.AddRange(Certificates); if (SignerIdentifierType != SubjectIdentifierType.NoSignature) { if (IncludeOption == X509IncludeOption.EndCertOnly) { certs.Add(Certificate); } else if (IncludeOption != X509IncludeOption.None) { X509Chain chain = new X509Chain(); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags; if (!chain.Build(Certificate)) { foreach (X509ChainStatus status in chain.ChainStatus) { if (status.Status == X509ChainStatusFlags.PartialChain) { throw new CryptographicException(SR.Cryptography_Cms_IncompleteCertChain); } } } X509ChainElementCollection elements = chain.ChainElements; int count = elements.Count; int last = count - 1; if (last == 0) { // If there's always one cert treat it as EE, not root. last = -1; } for (int i = 0; i < count; i++) { X509Certificate2 cert = elements[i].Certificate; if (i == last && IncludeOption == X509IncludeOption.ExcludeRoot && cert.SubjectName.RawData.AsSpan().SequenceEqual(cert.IssuerName.RawData)) { break; } certs.Add(cert); } } } chainCerts = certs; return(newSignerInfo); }