/// <summary> /// Reads XML conforming to https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-SignatureMethod /// </summary> /// <param name="reader">a <see cref="XmlReader"/>positioned on a <SignatureMethod> 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>A string with the signature method.</returns> public virtual string ReadSignatureMethod(XmlReader reader) { XmlUtil.CheckReaderOnEntry(reader, XmlSignatureConstants.Elements.SignatureMethod, XmlSignatureConstants.Namespace); try { bool isEmptyElement = reader.IsEmptyElement; var signatureMethod = reader.GetAttribute(XmlSignatureConstants.Attributes.Algorithm, null); if (signatureMethod == null) { throw XmlUtil.OnRequiredAttributeMissing(XmlSignatureConstants.Elements.SignatureMethod, XmlSignatureConstants.Attributes.Algorithm); } reader.Read(); reader.MoveToContent(); if (!isEmptyElement) { reader.MoveToContent(); reader.ReadEndElement(); } return(signatureMethod); } catch (Exception ex) { if (ex is XmlReadException) { throw; } throw XmlUtil.LogReadException(LogMessages.IDX30016, ex, XmlSignatureConstants.Elements.Transform); } }
/// <summary> /// Reads the "X509DataElement" element conforming to https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-X509Data. /// </summary> /// <param name="reader">A <see cref="XmlReader"/> positioned on a <see cref="XmlSignatureConstants.Elements.X509Data"/> element.</param> private X509Data ReadX509Data(XmlReader reader) { var data = new X509Data(); if (reader.IsEmptyElement) { throw XmlUtil.LogReadException(LogMessages.IDX30108); } reader.ReadStartElement(XmlSignatureConstants.Elements.X509Data, XmlSignatureConstants.Namespace); while (reader.IsStartElement()) { if (reader.IsStartElement(XmlSignatureConstants.Elements.X509Certificate, XmlSignatureConstants.Namespace)) { data.Certificates.Add(reader.ReadElementContentAsString()); } else if (reader.IsStartElement(XmlSignatureConstants.Elements.X509IssuerSerial, XmlSignatureConstants.Namespace)) { if (data.IssuerSerial != null) { throw XmlUtil.LogReadException(LogMessages.IDX30015, XmlSignatureConstants.Elements.X509IssuerSerial); } data.IssuerSerial = ReadIssuerSerial(reader); } else if (reader.IsStartElement(XmlSignatureConstants.Elements.X509SKI, XmlSignatureConstants.Namespace)) { if (data.SKI != null) { throw XmlUtil.LogReadException(LogMessages.IDX30015, XmlSignatureConstants.Elements.X509SKI); } data.SKI = reader.ReadElementContentAsString(); } else if (reader.IsStartElement(XmlSignatureConstants.Elements.X509SubjectName, XmlSignatureConstants.Namespace)) { if (data.SubjectName != null) { throw XmlUtil.LogReadException(LogMessages.IDX30015, XmlSignatureConstants.Elements.X509SubjectName); } data.SubjectName = reader.ReadElementContentAsString(); } else if (reader.IsStartElement(XmlSignatureConstants.Elements.X509CRL, XmlSignatureConstants.Namespace)) { if (data.CRL != null) { throw XmlUtil.LogReadException(LogMessages.IDX30015, XmlSignatureConstants.Elements.X509CRL); } data.CRL = reader.ReadElementContentAsString(); } else { // Skip the element since it is not one of <X509Certificate>, <X509IssuerSerial>, <X509SKI>, <X509SubjectName>, <X509CRL> LogHelper.LogWarning(LogMessages.IDX30300, reader.ReadOuterXml()); } } // </X509Data> reader.ReadEndElement(); return(data); }
/// <summary> /// Reads XML conforming to https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-Signature /// </summary> /// <param name="reader">a <see cref="XmlReader"/>positioned on a <Signature> 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="Signature"/></returns> public virtual Signature ReadSignature(XmlReader reader) { try { // <Signature> XmlUtil.CheckReaderOnEntry(reader, XmlSignatureConstants.Elements.Signature, XmlSignatureConstants.Namespace); var prefix = reader.Prefix; var id = reader.GetAttribute(XmlSignatureConstants.Attributes.Id, null); reader.Read(); var signedInfo = ReadSignedInfo(reader); reader.MoveToContent(); var signatureValue = reader.ReadElementContentAsString().Trim(); KeyInfo keyInfo = null; if (reader.IsStartElement(XmlSignatureConstants.Elements.KeyInfo, XmlSignatureConstants.Namespace)) { keyInfo = ReadKeyInfo(reader); } // </Signature> reader.MoveToContent(); // throw if we are not on EndElement, something unexpected if (reader.NodeType != XmlNodeType.EndElement) { throw XmlUtil.LogReadException(LogMessages.IDX30025, XmlSignatureConstants.Elements.Signature, reader.NodeType, reader.LocalName); } reader.ReadEndElement(); return(new Signature { Id = id, KeyInfo = keyInfo, Prefix = prefix, SignedInfo = signedInfo, SignatureValue = signatureValue }); } catch (Exception ex) { if (ex is XmlReadException) { throw; } throw XmlUtil.LogReadException(LogMessages.IDX30016, ex, XmlSignatureConstants.Elements.Signature); } }
/// <summary> /// Reads XML conforming to https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-CanonicalizationMethod /// </summary> /// <param name="reader">a <see cref="XmlReader"/>positioned on a <CanonicalizationMethod> 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>A string with the canonicalization method.</returns> public virtual string ReadCanonicalizationMethod(XmlReader reader) { // <CanonicalizationMethod> XmlUtil.CheckReaderOnEntry(reader, XmlSignatureConstants.Elements.CanonicalizationMethod, XmlSignatureConstants.Namespace); try { bool isEmptyElement = reader.IsEmptyElement; var algorithm = reader.GetAttribute(XmlSignatureConstants.Attributes.Algorithm, null); if (string.IsNullOrEmpty(algorithm)) { throw XmlUtil.LogReadException(LogMessages.IDX30013, XmlSignatureConstants.Elements.Signature, XmlSignatureConstants.Attributes.Algorithm); } if (algorithm != SecurityAlgorithms.ExclusiveC14nWithComments && algorithm != SecurityAlgorithms.ExclusiveC14n) { throw XmlUtil.LogReadException(LogMessages.IDX30100, XmlSignatureConstants.Elements.Transform, algorithm, SecurityAlgorithms.ExclusiveC14n, SecurityAlgorithms.ExclusiveC14nWithComments); } reader.Read(); reader.MoveToContent(); if (!isEmptyElement) { if (reader.IsStartElement(XmlSignatureConstants.ExclusiveC14nInclusiveNamespaces)) { throw XmlUtil.LogReadException(LogMessages.IDX30107); } reader.MoveToContent(); reader.ReadEndElement(); } return(algorithm); } catch (Exception ex) { if (ex is XmlReadException) { throw; } throw XmlUtil.LogReadException(LogMessages.IDX30016, ex, XmlSignatureConstants.Elements.Transform); } }
/// <summary> /// Reads the "RSAKeyValue" element conforming to https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-RSAKeyValue. /// </summary> /// <param name="reader">A <see cref="XmlReader"/> positioned on a <see cref="XmlSignatureConstants.Elements.RSAKeyValue"/> element.</param> private RSAKeyValue ReadRSAKeyValue(XmlReader reader) { reader.ReadStartElement(XmlSignatureConstants.Elements.RSAKeyValue, XmlSignatureConstants.Namespace); if (!reader.IsStartElement(XmlSignatureConstants.Elements.Modulus, XmlSignatureConstants.Namespace)) { throw XmlUtil.LogReadException(LogMessages.IDX30011, XmlSignatureConstants.Namespace, XmlSignatureConstants.Elements.Modulus, reader.NamespaceURI, reader.LocalName); } string modulus = reader.ReadElementContentAsString(XmlSignatureConstants.Elements.Modulus, XmlSignatureConstants.Namespace); if (!reader.IsStartElement(XmlSignatureConstants.Elements.Exponent, XmlSignatureConstants.Namespace)) { throw XmlUtil.LogReadException(LogMessages.IDX30011, XmlSignatureConstants.Namespace, XmlSignatureConstants.Elements.Exponent, reader.NamespaceURI, reader.LocalName); } string exponent = reader.ReadElementContentAsString(XmlSignatureConstants.Elements.Exponent, XmlSignatureConstants.Namespace); reader.ReadEndElement(); return(new RSAKeyValue(modulus, exponent)); }
/// <summary> /// Reads the "X509IssuerSerial" element conforming to https://www.w3.org/TR/2001/PR-xmldsig-core-20010820/#sec-X509Data. /// </summary> /// <param name="reader">A <see cref="XmlReader"/> positioned on a <see cref="XmlSignatureConstants.Elements.X509IssuerSerial"/> element.</param> private IssuerSerial ReadIssuerSerial(XmlReader reader) { reader.ReadStartElement(XmlSignatureConstants.Elements.X509IssuerSerial, XmlSignatureConstants.Namespace); if (!reader.IsStartElement(XmlSignatureConstants.Elements.X509IssuerName, XmlSignatureConstants.Namespace)) { throw XmlUtil.LogReadException(LogMessages.IDX30011, XmlSignatureConstants.Namespace, XmlSignatureConstants.Elements.X509IssuerName, reader.NamespaceURI, reader.LocalName); } var issuerName = reader.ReadElementContentAsString(XmlSignatureConstants.Elements.X509IssuerName, XmlSignatureConstants.Namespace); if (!reader.IsStartElement(XmlSignatureConstants.Elements.X509SerialNumber, XmlSignatureConstants.Namespace)) { throw XmlUtil.LogReadException(LogMessages.IDX30011, XmlSignatureConstants.Namespace, XmlSignatureConstants.Elements.X509SerialNumber, reader.NamespaceURI, reader.LocalName); } var serialNumber = reader.ReadElementContentAsString(XmlSignatureConstants.Elements.X509SerialNumber, XmlSignatureConstants.Namespace); reader.ReadEndElement(); return(new IssuerSerial(issuerName, serialNumber)); }
/// <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> /// 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); } }