/// <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 <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(); }
/// <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 <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); }
/// <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 <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(); }
/// <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 <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); } }
/// <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 <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); } }
/// <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 <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); } }