Example #1
0
        /// <summary>
        /// Writes the contents of a <see cref="SignedInfo"/> as XML conforming to https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-SignedInfo.
        /// </summary>
        /// <param name="writer">the <see cref="XmlWriter"/> to use.</param>
        /// <param name="signedInfo">the <see cref="SignedInfo"/>to write.</param>
        /// <remarks>Assumes the &lt;Reference> digest has been calculated, no canonicalization or digest calculation is performed.</remarks>
        /// <exception cref="ArgumentNullException">if <paramref name="writer"/> is null.</exception>
        /// <exception cref="ArgumentNullException">if <paramref name="signedInfo"/> is null.</exception>
        /// <exception cref="XmlWriteException">if <see cref="SignedInfo.CanonicalizationMethod"/> is null or empty.</exception>
        /// <exception cref="XmlWriteException">if <see cref="SignedInfo.References"/> is null.</exception>
        /// <exception cref="NotSupportedException">if <see cref="SignedInfo.References" />.Count > 1.</exception>
        /// <exception cref="XmlWriteException">if <see cref="SignedInfo.SignatureMethod"/> is null or empty.</exception>
        public virtual void WriteSignedInfo(XmlWriter writer, SignedInfo signedInfo)
        {
            if (writer == null)
            {
                throw LogArgumentNullException(nameof(writer));
            }

            if (signedInfo == null)
            {
                throw LogArgumentNullException(nameof(signedInfo));
            }

            if (string.IsNullOrEmpty(signedInfo.CanonicalizationMethod))
            {
                throw XmlUtil.LogWriteException(LogMessages.IDX30401, XmlSignatureConstants.Elements.SignedInfo, XmlSignatureConstants.Elements.CanonicalizationMethod);
            }

            if (string.IsNullOrEmpty(signedInfo.SignatureMethod))
            {
                throw XmlUtil.LogWriteException(LogMessages.IDX30401, XmlSignatureConstants.Elements.SignedInfo, XmlSignatureConstants.Elements.SignatureMethod);
            }

            if (signedInfo.References == null)
            {
                throw XmlUtil.LogWriteException(LogMessages.IDX30405);
            }

            // <SignedInfo>
            writer.WriteStartElement(Prefix, XmlSignatureConstants.Elements.SignedInfo, XmlSignatureConstants.Namespace);

            // @Id
            if (signedInfo.Id != null)
            {
                writer.WriteAttributeString(XmlSignatureConstants.Attributes.Id, null, signedInfo.Id);
            }

            // <CanonicalizationMethod>
            writer.WriteStartElement(Prefix, XmlSignatureConstants.Elements.CanonicalizationMethod, XmlSignatureConstants.Namespace);

            //@Algorithm
            writer.WriteAttributeString(XmlSignatureConstants.Attributes.Algorithm, null, signedInfo.CanonicalizationMethod);
            writer.WriteEndElement();

            // <SignatureMethod>
            writer.WriteStartElement(Prefix, XmlSignatureConstants.Elements.SignatureMethod, XmlSignatureConstants.Namespace);

            // @Algorithm
            writer.WriteAttributeString(XmlSignatureConstants.Attributes.Algorithm, null, signedInfo.SignatureMethod);

            // </SignatureMethod>
            writer.WriteEndElement();

            // <Reference>
            foreach (var reference in signedInfo.References)
            {
                WriteReference(writer, reference);
            }

            // </SignedInfo>
            writer.WriteEndElement();
        }
Example #2
0
        /// <summary>
        /// Reads XML conforming to https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-KeyInfo
        /// </summary>
        /// <param name="reader"><see cref="XmlReader"/> pointing positioned on a &lt;KeyInfo> element.</param>
        /// <exception cref="ArgumentNullException">if <paramref name="reader"/> is null.</exception>
        /// <exception cref="XmlReadException">if there is a problem reading the XML.</exception>
        /// <remarks>Only handles IssuerSerial, Ski, SubjectName, Certificate. Unsupported types are skipped. Only a X509 data element is supported.</remarks>
        public virtual KeyInfo ReadKeyInfo(XmlReader reader)
        {
            XmlUtil.CheckReaderOnEntry(reader, XmlSignatureConstants.Elements.KeyInfo, XmlSignatureConstants.Namespace);

            var keyInfo = new KeyInfo
            {
                Prefix = reader.Prefix
            };

            try
            {
                bool isEmptyElement = reader.IsEmptyElement;

                // <KeyInfo>
                reader.ReadStartElement();
                while (reader.IsStartElement())
                {
                    // <X509Data>
                    if (reader.IsStartElement(XmlSignatureConstants.Elements.X509Data, XmlSignatureConstants.Namespace))
                    {
                        keyInfo.X509Data.Add(ReadX509Data(reader));
                    }
                    // <RetrievalMethod>
                    else if (reader.IsStartElement(XmlSignatureConstants.Elements.RetrievalMethod, XmlSignatureConstants.Namespace))
                    {
                        keyInfo.RetrievalMethodUri = reader.GetAttribute(XmlSignatureConstants.Attributes.URI);
                        reader.ReadOuterXml();
                    }
                    // <KeyName>
                    else if (reader.IsStartElement(XmlSignatureConstants.Elements.KeyName, XmlSignatureConstants.Namespace))
                    {
                        keyInfo.KeyName = reader.ReadElementContentAsString(XmlSignatureConstants.Elements.KeyName, XmlSignatureConstants.Namespace);
                    }
                    // <KeyValue>
                    else if (reader.IsStartElement(XmlSignatureConstants.Elements.KeyValue, XmlSignatureConstants.Namespace))
                    {
                        reader.ReadStartElement(XmlSignatureConstants.Elements.KeyValue, XmlSignatureConstants.Namespace);
                        if (reader.IsStartElement(XmlSignatureConstants.Elements.RSAKeyValue, XmlSignatureConstants.Namespace))
                        {
                            // Multiple RSAKeyValues were found
                            if (keyInfo.RSAKeyValue != null)
                            {
                                throw XmlUtil.LogReadException(LogMessages.IDX30015, XmlSignatureConstants.Elements.RSAKeyValue);
                            }

                            keyInfo.RSAKeyValue = ReadRSAKeyValue(reader);
                        }
                        else
                        {
                            // Skip the element since it is not an <RSAKeyValue>
                            LogHelper.LogWarning(LogMessages.IDX30300, reader.ReadOuterXml());
                        }

                        // </KeyValue>
                        reader.ReadEndElement();
                    }
                    else
                    {
                        // Skip the element since it is not one of  <RetrievalMethod>, <X509Data>, <KeyValue>
                        LogHelper.LogWarning(LogMessages.IDX30300, reader.ReadOuterXml());
                    }
                }

                // </KeyInfo>
                if (!isEmptyElement)
                {
                    reader.ReadEndElement();
                }
            }
            catch (Exception ex)
            {
                if (ex is XmlReadException)
                {
                    throw;
                }

                throw XmlUtil.LogReadException(LogMessages.IDX30017, ex, XmlSignatureConstants.Elements.KeyInfo, ex);
            }

            return(keyInfo);
        }
Example #3
0
        /// <summary>
        /// Writes the contents of a <see cref="Reference"/> as XML conforming to https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-Reference.
        /// </summary>
        /// <param name="writer">the <see cref="XmlWriter"/> to use.</param>
        /// <param name="reference">the <see cref="Reference"/>to write.</param>
        /// <remarks>Assumes the &lt;DigestValue> has been calculated, no canonicalization or digest calculation is performed.</remarks>
        /// <exception cref="ArgumentNullException">if <paramref name="writer"/> is null.</exception>
        /// <exception cref="ArgumentNullException">if <paramref name="reference"/> is null.</exception>
        /// <exception cref="XmlWriteException">if <see cref="Reference.DigestMethod"/> is null or empty.</exception>
        /// <exception cref="XmlWriteException">if <see cref="Reference.DigestValue"/> is null or empty.</exception>
        /// <exception cref="XmlWriteException">if one of the values in <see cref="Reference.Transforms"/> is null or empty.</exception>
        public virtual void WriteReference(XmlWriter writer, Reference reference)
        {
            if (writer == null)
            {
                throw LogArgumentNullException(nameof(writer));
            }

            if (reference == null)
            {
                throw LogArgumentNullException(nameof(reference));
            }

            if (string.IsNullOrEmpty(reference.DigestMethod))
            {
                throw XmlUtil.LogWriteException(LogMessages.IDX30401, XmlSignatureConstants.Elements.Reference, XmlSignatureConstants.Elements.DigestMethod);
            }

            if (string.IsNullOrEmpty(reference.DigestValue))
            {
                throw XmlUtil.LogWriteException(LogMessages.IDX30401, XmlSignatureConstants.Elements.Reference, XmlSignatureConstants.Elements.DigestValue);
            }

            // <Reference>
            writer.WriteStartElement(Prefix, XmlSignatureConstants.Elements.Reference, XmlSignatureConstants.Namespace);

            // @Id
            if (reference.Id != null)
            {
                writer.WriteAttributeString(XmlSignatureConstants.Attributes.Id, null, reference.Id);
            }

            // @Uri
            if (reference.Uri != null)
            {
                writer.WriteAttributeString(XmlSignatureConstants.Attributes.URI, null, reference.Uri);
            }
            else if (reference.Id != null)
            {
                if (reference.Id.StartsWith("#", StringComparison.Ordinal))
                {
                    writer.WriteAttributeString(XmlSignatureConstants.Attributes.URI, null, reference.Id);
                }
                else
                {
                    writer.WriteAttributeString(XmlSignatureConstants.Attributes.URI, null, "#" + reference.Id);
                }
            }

            // @Type
            if (reference.Type != null)
            {
                writer.WriteAttributeString(XmlSignatureConstants.Attributes.Type, null, reference.Type);
            }

            // <Transforms>
            writer.WriteStartElement(Prefix, XmlSignatureConstants.Elements.Transforms, XmlSignatureConstants.Namespace);

            // <Transform>
            foreach (var transform in reference.Transforms)
            {
                if (transform == null)
                {
                    throw XmlUtil.LogWriteException(LogMessages.IDX30403);
                }

                // <Transform>
                writer.WriteStartElement(Prefix, XmlSignatureConstants.Elements.Transform, XmlSignatureConstants.Namespace);

                // @Algorithm
                writer.WriteAttributeString(XmlSignatureConstants.Attributes.Algorithm, transform.Algorithm);

                // </Transform>
                writer.WriteEndElement();
            }

            // Write Canonicalizing transform
            if (reference.CanonicalizingTransfrom != null)
            {
                // <Transform>
                writer.WriteStartElement(Prefix, XmlSignatureConstants.Elements.Transform, XmlSignatureConstants.Namespace);

                // @Algorithm
                writer.WriteAttributeString(XmlSignatureConstants.Attributes.Algorithm, reference.CanonicalizingTransfrom.Algorithm);

                // <InclusiveNamespaces>
                if (!string.IsNullOrEmpty(reference.CanonicalizingTransfrom.InclusiveNamespacesPrefixList))
                {
                    // @PrefixList
                    writer.WriteStartElement(XmlSignatureConstants.ExclusiveC14nPrefix, XmlSignatureConstants.Elements.InclusiveNamespaces, XmlSignatureConstants.ExclusiveC14nNamespace);
                    writer.WriteAttributeString(XmlSignatureConstants.Attributes.PrefixList, reference.CanonicalizingTransfrom.InclusiveNamespacesPrefixList);
                    writer.WriteEndElement();
                }

                // </Transform>
                writer.WriteEndElement();
            }

            // </Transforms>
            writer.WriteEndElement();

            // <DigestMethod>
            writer.WriteStartElement(Prefix, XmlSignatureConstants.Elements.DigestMethod, XmlSignatureConstants.Namespace);

            // @Algorithm
            writer.WriteAttributeString(XmlSignatureConstants.Attributes.Algorithm, null, reference.DigestMethod);

            // </DigestMethod>
            writer.WriteEndElement();

            // <DigestValue />
            writer.WriteElementString(Prefix, XmlSignatureConstants.Elements.DigestValue, XmlSignatureConstants.Namespace, reference.DigestValue);

            // </Reference>
            writer.WriteEndElement();
        }
Example #4
0
        /// <summary>
        /// Reads XML conforming to https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-Transforms
        /// </summary>
        /// <param name="reader">a <see cref="XmlReader"/>positioned on a &lt;Transforms> element.</param>
        /// <param name="reference">a <see cref="Reference"/> to attach transforms.</param>
        /// <exception cref="ArgumentNullException">if <paramref name="reader"/> is null.</exception>
        /// <exception cref="ArgumentNullException">if <paramref name="reference"/> is null.</exception>
        /// <exception cref="XmlReadException">if there is a problem reading the XML.</exception>
        public virtual void ReadTransforms(XmlReader reader, Reference reference)
        {
            if (reader == null)
            {
                throw LogArgumentNullException(nameof(reader));
            }

            if (reference == null)
            {
                throw LogArgumentNullException(nameof(reference));
            }

            try
            {
                // <Transforms> - optional
                if (!reader.IsStartElement(XmlSignatureConstants.Elements.Transforms, XmlSignatureConstants.Namespace))
                {
                    return;
                }

                if (reader.IsEmptyElement)
                {
                    reader.Read();
                    return;
                }

                reader.Read();
                // <Transform> - unbounded

                while (reader.IsStartElement(XmlSignatureConstants.Elements.Transform, XmlSignatureConstants.Namespace))
                {
                    var isEmptyElement = reader.IsEmptyElement;
                    var algorithm      = reader.GetAttribute(XmlSignatureConstants.Attributes.Algorithm);
                    if (string.IsNullOrEmpty(algorithm))
                    {
                        throw XmlUtil.LogReadException(LogMessages.IDX30105);
                    }

                    if (TransformFactory.IsSupportedTransform(algorithm))
                    {
                        reference.Transforms.Add(TransformFactory.GetTransform(algorithm));
                        reader.Read();
                    }
                    else if (TransformFactory.IsSupportedCanonicalizingTransfrom(algorithm))
                    {
                        reference.CanonicalizingTransfrom = TransformFactory.GetCanonicalizingTransform(algorithm);
                        reader.Read();
                        // release 5.2.1 did not require 'ec' ns. So, we need to accept names with and without a prefix.
                        if (reader.IsStartElement(XmlSignatureConstants.Elements.InclusiveNamespaces, XmlSignatureConstants.ExclusiveC14nNamespace) || reader.IsStartElement(XmlSignatureConstants.Elements.InclusiveNamespaces))
                        {
                            bool isOnEmptyElement = reader.IsEmptyElement;
                            reference.CanonicalizingTransfrom.InclusiveNamespacesPrefixList = reader.GetAttribute(XmlSignatureConstants.Attributes.PrefixList);
                            reader.ReadStartElement();
                            if (!isOnEmptyElement)
                            {
                                reader.ReadEndElement();
                            }
                        }
                    }
                    else
                    {
                        throw XmlUtil.LogReadException(LogMessages.IDX30210, algorithm);
                    }

                    reader.MoveToContent();
                    if (!isEmptyElement)
                    {
                        reader.ReadEndElement();
                    }
                }

                // </ Transforms>
                reader.MoveToContent();
                reader.ReadEndElement();
            }
            catch (Exception ex)
            {
                if (ex is XmlReadException)
                {
                    throw;
                }

                throw XmlUtil.LogReadException(LogMessages.IDX30016, ex, XmlSignatureConstants.Elements.Transforms);
            }
        }
Example #5
0
        /// <summary>
        /// Reads XML conforming to https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-Reference
        /// </summary>
        /// <param name="reader">a <see cref="XmlReader"/>positioned on a &lt;Reference> element.</param>
        /// <exception cref="ArgumentNullException">if <paramref name="reader"/> is null.</exception>
        /// <exception cref="XmlReadException">if there is a problem reading the XML.</exception>
        /// <returns><see cref="Reference"/></returns>
        public virtual Reference ReadReference(XmlReader reader)
        {
            XmlUtil.CheckReaderOnEntry(reader, XmlSignatureConstants.Elements.Reference, XmlSignatureConstants.Namespace);

            try
            {
                var reference = new Reference
                {
                    Prefix = reader.Prefix,
                    Id     = reader.GetAttribute(XmlSignatureConstants.Attributes.Id, null),
                    Type   = reader.GetAttribute(XmlSignatureConstants.Attributes.Type, null),
                    Uri    = reader.GetAttribute(XmlSignatureConstants.Attributes.URI, null)
                };

                reader.Read();
                ReadTransforms(reader, reference);

                // <DigestMethod> - required
                XmlUtil.CheckReaderOnEntry(reader, XmlSignatureConstants.Elements.DigestMethod, XmlSignatureConstants.Namespace);
                bool isEmptyElement = reader.IsEmptyElement;
                var  digestMethod   = reader.GetAttribute(XmlSignatureConstants.Attributes.Algorithm, null);
                if (string.IsNullOrEmpty(digestMethod))
                {
                    throw XmlUtil.OnRequiredAttributeMissing(XmlSignatureConstants.Elements.DigestMethod, XmlSignatureConstants.Attributes.Algorithm);
                }

                reference.DigestMethod = digestMethod;

                reader.Read();
                reader.MoveToContent();
                if (!isEmptyElement)
                {
                    reader.ReadEndElement();
                }

                // <DigestValue>
                XmlUtil.CheckReaderOnEntry(reader, XmlSignatureConstants.Elements.DigestValue, XmlSignatureConstants.Namespace);
                var digestValue = reader.ReadElementContentAsString().Trim();
                if (string.IsNullOrEmpty(digestValue))
                {
                    throw XmlUtil.LogReadException(LogMessages.IDX30206, reference.Uri ?? reference.Id);
                }

                reference.DigestValue = digestValue;

                // </Reference>
                reader.MoveToContent();
                reader.ReadEndElement();

                return(reference);
            }
            catch (Exception ex)
            {
                if (ex is XmlReadException)
                {
                    throw;
                }

                throw XmlUtil.LogReadException(LogMessages.IDX30016, ex, XmlSignatureConstants.Elements.Reference);
            }
        }
Example #6
0
        /// <summary>
        /// Reads XML conforming to https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-SignedInfo
        /// </summary>
        /// <param name="reader">a <see cref="XmlReader"/>positioned on a &lt;SignedInfo> element.</param>
        /// <exception cref="ArgumentNullException">if <paramref name="reader"/> is null.</exception>
        /// <exception cref="XmlReadException">if there is a problem reading the XML.</exception>
        /// <returns><see cref="SignedInfo"/></returns>
        public virtual SignedInfo ReadSignedInfo(XmlReader reader)
        {
            XmlUtil.CheckReaderOnEntry(reader, XmlSignatureConstants.Elements.SignedInfo, XmlSignatureConstants.Namespace);

            try
            {
                var defaultNamespace = reader.LookupNamespace(string.Empty);
                var bufferedStream   = new MemoryStream();
                var settings         = new XmlWriterSettings
                {
                    Encoding        = Encoding.UTF8,
                    NewLineHandling = NewLineHandling.None
                };

                // need to read into buffer since the canonicalization reader needs a stream.
                using (XmlWriter bufferWriter = XmlDictionaryWriter.Create(bufferedStream, settings))
                {
                    bufferWriter.WriteNode(reader, true);
                    bufferWriter.Flush();
                }

                bufferedStream.Position = 0;

                //
                // We are creating a XmlDictionaryReader with a hard-coded Max XmlDictionaryReaderQuotas. This is a reader that we
                // are creating over an already buffered content. The content was initially read off user provided XmlDictionaryReader
                // with the correct quotas and hence we know the data is valid.
                //
                using (var canonicalizingReader = XmlDictionaryReader.CreateTextReader(bufferedStream, XmlDictionaryReaderQuotas.Max))
                {
                    var signedInfo = new SignedInfo();
                    signedInfo.CanonicalStream = new MemoryStream();

                    // TODO - should not always use 'false'
                    canonicalizingReader.StartCanonicalization(signedInfo.CanonicalStream, false, null);
                    canonicalizingReader.MoveToStartElement(XmlSignatureConstants.Elements.SignedInfo, XmlSignatureConstants.Namespace);
                    signedInfo.Prefix = canonicalizingReader.Prefix;
                    signedInfo.Id     = canonicalizingReader.GetAttribute(XmlSignatureConstants.Attributes.Id, null);
                    // read <SignedInfo ...> start element
                    canonicalizingReader.Read();
                    // TODO - if comments are not false, then we need to reset.
                    // this should be very rare.
                    signedInfo.CanonicalizationMethod = ReadCanonicalizationMethod(canonicalizingReader);
                    signedInfo.SignatureMethod        = ReadSignatureMethod(canonicalizingReader);
                    signedInfo.References.Add(ReadReference(canonicalizingReader));

                    if (canonicalizingReader.IsStartElement(XmlSignatureConstants.Elements.Reference, XmlSignatureConstants.Namespace))
                    {
                        throw XmlUtil.LogReadException(LogMessages.IDX30020);
                    }

                    canonicalizingReader.ReadEndElement();
                    canonicalizingReader.EndCanonicalization();
                    signedInfo.CanonicalStream.Flush();

                    return(signedInfo);
                }
            }
            catch (Exception ex)
            {
                if (ex is XmlReadException)
                {
                    throw;
                }

                throw XmlUtil.LogReadException(LogMessages.IDX30016, ex, XmlSignatureConstants.Elements.SignedInfo);
            }
        }