// adapted from http://bugzilla.ximian.com/show_bug.cgi?id=52084 public void GetIdElement () { XmlDocument doc = new XmlDocument (); doc.LoadXml (signature); SignedXml v1 = new SignedXml (); v1.LoadXml (doc.DocumentElement); Assert.IsTrue (v1.CheckSignature (), "CheckSignature"); XmlElement xel = v1.GetIdElement (doc, "MyObjectId"); Assert.IsTrue (xel.InnerXml.StartsWith ("<ObjectListTag"), "GetIdElement"); }
public void GetIdElement_Null () { SignedXml sign = new SignedXml (); Assert.IsNull (sign.GetIdElement (null, "value")); Assert.IsNull (sign.GetIdElement (new XmlDocument (), null)); }
// What we want to do is pump the input throug the TransformChain and then // hash the output of the chain document is the document context for resolving relative references internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList refList) { // refList is a list of elements that might be targets of references // Now's the time to create our hashing algorithm _hashAlgorithm = CryptoHelpers.CreateFromName <HashAlgorithm>(_digestMethod); if (_hashAlgorithm == null) { throw new CryptographicException(SR.Cryptography_Xml_CreateHashAlgorithmFailed); } // Let's go get the target. string baseUri = (document == null ? System.Environment.CurrentDirectory + "\\" : document.BaseURI); Stream hashInputStream = null; WebResponse response = null; Stream inputStream = null; XmlResolver resolver = null; byte[] hashval = null; try { switch (_refTargetType) { case ReferenceTargetType.Stream: // This is the easiest case. We already have a stream, so just pump it through the TransformChain resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri)); hashInputStream = TransformChain.TransformToOctetStream((Stream)_refTarget, resolver, baseUri); break; case ReferenceTargetType.UriReference: // Second-easiest case -- dereference the URI & pump through the TransformChain // handle the special cases where the URI is null (meaning whole doc) // or the URI is just a fragment (meaning a reference to an embedded Object) if (_uri == null) { // We need to create a DocumentNavigator out of the XmlElement resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri)); // In the case of a Uri-less reference, we will simply pass null to the transform chain. // The first transform in the chain is expected to know how to retrieve the data to hash. hashInputStream = TransformChain.TransformToOctetStream((Stream)null, resolver, baseUri); } else if (_uri.Length == 0) { // This is the self-referential case. First, check that we have a document context. // The Enveloped Signature does not discard comments as per spec; those will be omitted during the transform chain process if (document == null) { throw new CryptographicException(SR.Format(SR.Cryptography_Xml_SelfReferenceRequiresContext, _uri)); } // Normalize the containing document resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri)); XmlDocument docWithNoComments = Utils.DiscardComments(Utils.PreProcessDocumentInput(document, resolver, baseUri)); hashInputStream = TransformChain.TransformToOctetStream(docWithNoComments, resolver, baseUri); } else if (_uri[0] == '#') { // If we get here, then we are constructing a Reference to an embedded DataObject // referenced by an Id = attribute. Go find the relevant object bool discardComments = true; string idref = Utils.GetIdFromLocalUri(_uri, out discardComments); if (idref == "xpointer(/)") { // This is a self referencial case if (document == null) { throw new CryptographicException(SR.Format(SR.Cryptography_Xml_SelfReferenceRequiresContext, _uri)); } // We should not discard comments here!!! resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri)); hashInputStream = TransformChain.TransformToOctetStream(Utils.PreProcessDocumentInput(document, resolver, baseUri), resolver, baseUri); break; } XmlElement elem = SignedXml.GetIdElement(document, idref); if (elem != null) { _namespaces = Utils.GetPropagatedAttributes(elem.ParentNode as XmlElement); } if (elem == null) { // Go throw the referenced items passed in if (refList != null) { foreach (XmlNode node in refList) { XmlElement tempElem = node as XmlElement; if ((tempElem != null) && (Utils.HasAttribute(tempElem, "Id", SignedXml.XmlDsigNamespaceUrl)) && (Utils.GetAttribute(tempElem, "Id", SignedXml.XmlDsigNamespaceUrl).Equals(idref))) { elem = tempElem; if (_signedXml._context != null) { _namespaces = Utils.GetPropagatedAttributes(_signedXml._context); } break; } } } } if (elem == null) { throw new CryptographicException(SR.Cryptography_Xml_InvalidReference); } XmlDocument normDocument = Utils.PreProcessElementInput(elem, resolver, baseUri); // Add the propagated attributes Utils.AddNamespaces(normDocument.DocumentElement, _namespaces); resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri)); if (discardComments) { // We should discard comments before going into the transform chain XmlDocument docWithNoComments = Utils.DiscardComments(normDocument); hashInputStream = TransformChain.TransformToOctetStream(docWithNoComments, resolver, baseUri); } else { // This is an XPointer reference, do not discard comments!!! hashInputStream = TransformChain.TransformToOctetStream(normDocument, resolver, baseUri); } } else { throw new CryptographicException(SR.Cryptography_Xml_UriNotResolved, _uri); } break; case ReferenceTargetType.XmlElement: // We need to create a DocumentNavigator out of the XmlElement resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri)); hashInputStream = TransformChain.TransformToOctetStream(Utils.PreProcessElementInput((XmlElement)_refTarget, resolver, baseUri), resolver, baseUri); break; default: throw new CryptographicException(SR.Cryptography_Xml_UriNotResolved, _uri); } // Compute the new hash value hashInputStream = SignedXmlDebugLog.LogReferenceData(this, hashInputStream); hashval = _hashAlgorithm.ComputeHash(hashInputStream); } finally { if (hashInputStream != null) { hashInputStream.Close(); } if (response != null) { response.Close(); } if (inputStream != null) { inputStream.Close(); } } return(hashval); }
private static void ValidateSignedInfo(SignedXml signedXml, XmlElement xmlElement) { if(signedXml.SignedInfo.References.Count == 0) { throw new InvalidSignatureException("No reference found in Xml signature, it doesn't validate the Xml data."); } if(signedXml.SignedInfo.References.Count != 1) { throw new InvalidSignatureException("Multiple references for Xml signatures are not allowed."); } var reference = (Reference)signedXml.SignedInfo.References[0]; var id = reference.Uri.Substring(1); var idElement = signedXml.GetIdElement(xmlElement.OwnerDocument, id); if(idElement != xmlElement) { throw new InvalidSignatureException("Incorrect reference on Xml signature. The reference must be to the root element of the element containing the signature."); } foreach (Transform transform in reference.TransformChain) { if (!allowedTransforms.Contains(transform.Algorithm)) { throw new InvalidSignatureException( "Transform \"" + transform.Algorithm + "\" found in Xml signature SHOULD NOT be used with SAML2."); } } }
// What we want to do is pump the input throug the TransformChain and then // hash the output of the chain // document is the document context for resolving relative references internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList refList) { // refList is a list of elements that might be targets of references // Now's the time to create our hashing algorithm m_hashAlgorithm = (HashAlgorithm)CryptoConfig.CreateFromName(DigestMethod); if (m_hashAlgorithm == null) { throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_CreateHashAlgorithmFailed")); } string strBaseUri = (document == null ? null : document.BaseURI); // For interop w/ Petteri & IBM -- may not be required by the spec //if (m_transforms.Count == 0) { //if (DataObject != null && DataObject.Data != null) { //AddTransform(new W3cCanonicalization()); //} //} // Let's go get the target. Stream hashInputStream; WebRequest theRequest = null; WebResponse theResponse = null; Stream inputStream = null; XmlResolver resolver = null; switch (m_refTargetType) { case ReferenceTargetType.Stream: // This is the easiest case. We already have a stream, so just pump it through // the TransformChain resolver = (m_signedXml.ResolverSet ? m_signedXml.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), strBaseUri)); hashInputStream = m_transformChain.TransformToOctetStream((Stream)m_refTarget, resolver, strBaseUri); break; case ReferenceTargetType.UriReference: // Second-easiest case -- dereference the URI & pump through the TransformChain // handle the special cases where the URI is null (meaning whole doc) // or the URI is just a fragment (meaning a reference to an embedded Object) if (m_strUri == "") { // This is the self-referential case. // The Enveloped Signature does not discard comments as per spec; those will be omitted during the transform chain process // First, check that we have a document context. if (document == null) { throw new CryptographicException(String.Format(SecurityResources.GetResourceString("Cryptography_Xml_SelfReferenceRequiresContext"), m_strUri)); } // Normalize the containing document resolver = (m_signedXml.ResolverSet ? m_signedXml.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), strBaseUri)); XmlDocument docWithNoComments = CanonicalXml.DiscardComments(SignedXml.PreProcessDocumentInput(document, resolver, strBaseUri)); hashInputStream = m_transformChain.TransformToOctetStream(docWithNoComments, resolver, strBaseUri); } else if (m_strUri[0] == '#') { // If we get here, then we are constructing a Reference to an embedded DataObject // referenced by an Id= attribute. Go find the relevant object String idref = m_strUri.Substring(1); bool bDiscardComments = true; // Deal with XPointer of types #xpointer(/) and #xpointer(id("ID")). Other XPointer support isn't handled here and is anyway optional if (idref == "xpointer(/)") { // This is a self referencial case if (document == null) { throw new CryptographicException(String.Format(SecurityResources.GetResourceString("Cryptography_Xml_SelfReferenceRequiresContext"), m_strUri)); } // We should not discard comments here!!! resolver = (m_signedXml.ResolverSet ? m_signedXml.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), strBaseUri)); hashInputStream = m_transformChain.TransformToOctetStream(SignedXml.PreProcessDocumentInput(document, resolver, strBaseUri), resolver, strBaseUri); goto end; } else if (idref.StartsWith("xpointer(id(")) { int startId = idref.IndexOf("id("); int endId = idref.IndexOf(")"); if (endId < 0 || endId < startId + 3) { throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_InvalidReference")); } idref = idref.Substring(startId + 3, endId - startId - 3); idref = idref.Replace("\'", ""); idref = idref.Replace("\"", ""); bDiscardComments = false; } XmlElement elem = m_signedXml.GetIdElement(document, idref); if (elem == null) { // Go throw the referenced items passed in if (refList != null) { foreach (XmlNode node in refList) { XmlElement tempElem = node as XmlElement; if ((tempElem != null) && (tempElem.HasAttribute("Id")) && (tempElem.GetAttribute("Id").Equals(idref))) { elem = tempElem; break; } } } } if (elem == null) { throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_InvalidReference")); } // Add the propagated attributes, clone the element first XmlElement elemClone = elem.Clone() as XmlElement; if (m_namespaces != null) { foreach (XmlNode attrib in m_namespaces) { string name = ((attrib.Prefix != String.Empty) ? attrib.Prefix + ":" + attrib.LocalName : attrib.LocalName); // Skip the attribute if one with the same qualified name already exists if (elemClone.HasAttribute(name) || (name.Equals("xmlns") && elemClone.NamespaceURI != String.Empty)) { continue; } XmlAttribute nsattrib = (XmlAttribute)elemClone.OwnerDocument.CreateAttribute(name); nsattrib.Value = attrib.Value; elemClone.SetAttributeNode(nsattrib); } } if (bDiscardComments) { // We should discard comments before going into the transform chain resolver = (m_signedXml.ResolverSet ? m_signedXml.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), strBaseUri)); XmlDocument docWithNoComments = CanonicalXml.DiscardComments(SignedXml.PreProcessElementInput(elemClone, resolver, strBaseUri)); hashInputStream = m_transformChain.TransformToOctetStream(docWithNoComments, resolver, strBaseUri); } else { // This is an XPointer reference, do not discard comments!!! resolver = (m_signedXml.ResolverSet ? m_signedXml.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), strBaseUri)); hashInputStream = m_transformChain.TransformToOctetStream(SignedXml.PreProcessElementInput(elemClone, resolver, strBaseUri), resolver, strBaseUri); } } else { theRequest = WebRequest.Create(m_strUri); if (theRequest == null) { goto default; } theResponse = theRequest.GetResponse(); if (theResponse == null) { goto default; } inputStream = theResponse.GetResponseStream(); if (inputStream == null) { goto default; } resolver = (m_signedXml.ResolverSet ? m_signedXml.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), m_strUri)); hashInputStream = m_transformChain.TransformToOctetStream(inputStream, resolver, m_strUri); } break; case ReferenceTargetType.XmlElement: // We need to create a DocumentNavigator out of the XmlElement resolver = (m_signedXml.ResolverSet ? m_signedXml.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), strBaseUri)); hashInputStream = m_transformChain.TransformToOctetStream(SignedXml.PreProcessElementInput((XmlElement)m_refTarget, resolver, strBaseUri), resolver, strBaseUri); break; default: throw new CryptographicException(); } end: // Compute the new hash value byte[] hashval = m_hashAlgorithm.ComputeHash(hashInputStream); // Close the response to free resources, before returning if (theResponse != null) { theResponse.Close(); } if (inputStream != null) { inputStream.Close(); } return(hashval); }
/// <summary> /// Verifies the signature. /// </summary> /// <param name="xml">The XML.</param> /// <returns>The issuer certificate</returns> protected virtual X509Certificate2 VerifySignature(XElement xml) { Contract.Requires(xml != null); Contract.Ensures(Contract.Result<X509Certificate2>() != null); if ((Configuration == null) || (Configuration.IssuerTokenResolver == null)) { throw new SecurityTokenException("No issuer token resolver configured"); } var xmlElement = xml.ToXmlElement(); var signedXml = new SignedXml(xmlElement); // find signature XmlNodeList nodeList = xmlElement.GetElementsByTagName("Signature"); // throw an exception if no signature was found. if (nodeList.Count <= 0) { throw new CryptographicException("Verification failed: No Signature was found in the document."); } // throw an exception if more than one signature was found. if (nodeList.Count > 1) { throw new CryptographicException("Verification failed: More that one signature was found for the document."); } // load the <signature> node. signedXml.LoadXml((XmlElement)nodeList[0]); // resolve the issuer certificate byte[] thumbprint = Convert.FromBase64String(GetIssuerThumbprint(signedXml)); var identifier = new X509ThumbprintKeyIdentifierClause(thumbprint); var issuerKey = Configuration.IssuerTokenResolver.ResolveToken(identifier) as X509SecurityToken; // check the signature var referenceUri = ((Reference)signedXml.SignedInfo.References[0]).Uri; if (!signedXml.CheckSignature(issuerKey.Certificate, true) || (referenceUri != "" && signedXml.GetIdElement(xmlElement.OwnerDocument, referenceUri) != xmlElement)) { throw new CryptographicException("Signature verification failed"); } if (issuerKey.Certificate != null) { return issuerKey.Certificate; } else { throw new CryptographicException("No issuer certificate found"); } }
public void LoadXml(XmlElement value) { if (value is null) { throw new ArgumentNullException(nameof(value)); } _id = Utils.GetAttribute(value, "Id", SignedXml.XmlDsigNamespaceUrl); _uri = Utils.GetAttribute(value, "URI", SignedXml.XmlDsigNamespaceUrl); _type = Utils.GetAttribute(value, "Type", SignedXml.XmlDsigNamespaceUrl); if (!Utils.VerifyAttributes(value, new string[] { "Id", "URI", "Type" })) { throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "Reference"); } XmlNamespaceManager nsm = new XmlNamespaceManager(value.OwnerDocument.NameTable); nsm.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl); // Transforms bool hasTransforms = false; TransformChain = new TransformChain(); XmlNodeList transformsNodes = value.SelectNodes("ds:Transforms", nsm); if (transformsNodes != null && transformsNodes.Count != 0) { if (transformsNodes.Count > 1) { throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "Reference/Transforms"); } hasTransforms = true; XmlElement transformsElement = transformsNodes[0] as XmlElement; if (!Utils.VerifyAttributes(transformsElement, (string[])null)) { throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "Reference/Transforms"); } XmlNodeList transformNodes = transformsElement.SelectNodes("ds:Transform", nsm); if (transformNodes != null) { if (transformNodes.Count != transformsElement.SelectNodes("*").Count) { throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "Reference/Transforms"); } if (transformNodes.Count > Utils.MaxTransformsPerReference) { throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "Reference/Transforms"); } foreach (XmlNode transformNode in transformNodes) { XmlElement transformElement = transformNode as XmlElement; string algorithm = Utils.GetAttribute(transformElement, "Algorithm", SignedXml.XmlDsigNamespaceUrl); if (algorithm == null || !Utils.VerifyAttributes(transformElement, "Algorithm")) { throw new CryptographicException(SR.Cryptography_Xml_UnknownTransform); } Transform transform = CryptoHelpers.CreateFromName <Transform>(algorithm); if (transform == null) { throw new CryptographicException(SR.Cryptography_Xml_UnknownTransform); } AddTransform(transform); // let the transform read the children of the transformElement for data transform.LoadInnerXml(transformElement.ChildNodes); // Hack! this is done to get around the lack of here() function support in XPath if (transform is XmlDsigEnvelopedSignatureTransform && _uri != null && (_uri.Length == 0 || _uri[0] == '#')) { // Walk back to the Signature tag. Find the nearest signature ancestor // Signature-->SignedInfo-->Reference-->Transforms-->Transform XmlNode signatureTag = transformElement.SelectSingleNode("ancestor::ds:Signature[1]", nsm); // Resolve the reference to get starting point for position calculation. XmlNode referenceTarget = _uri.Length == 0 ? transformElement.OwnerDocument : SignedXml.GetIdElement(transformElement.OwnerDocument, Utils.GetIdFromLocalUri(_uri, out bool _)); XmlNodeList signatureList = referenceTarget?.SelectNodes(".//ds:Signature", nsm); if (signatureList != null) { int position = 0; foreach (XmlNode node in signatureList) { position++; if (node == signatureTag) { ((XmlDsigEnvelopedSignatureTransform)transform).SignaturePosition = position; break; } } } } } } } // DigestMethod XmlNodeList digestMethodNodes = value.SelectNodes("ds:DigestMethod", nsm); if (digestMethodNodes == null || digestMethodNodes.Count == 0 || digestMethodNodes.Count > 1) { throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "Reference/DigestMethod"); } XmlElement digestMethodElement = digestMethodNodes[0] as XmlElement; _digestMethod = Utils.GetAttribute(digestMethodElement, "Algorithm", SignedXml.XmlDsigNamespaceUrl); if (_digestMethod == null || !Utils.VerifyAttributes(digestMethodElement, "Algorithm")) { throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "Reference/DigestMethod"); } // DigestValue XmlNodeList digestValueNodes = value.SelectNodes("ds:DigestValue", nsm); if (digestValueNodes == null || digestValueNodes.Count == 0 || digestValueNodes.Count > 1) { throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "Reference/DigestValue"); } XmlElement digestValueElement = digestValueNodes[0] as XmlElement; _digestValue = Convert.FromBase64String(Utils.DiscardWhiteSpaces(digestValueElement.InnerText)); if (!Utils.VerifyAttributes(digestValueElement, (string[])null)) { throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "Reference/DigestValue"); } // Verify that there aren't any extra nodes that aren't allowed int expectedChildNodeCount = hasTransforms ? 3 : 2; if (value.SelectNodes("*").Count != expectedChildNodeCount) { throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "Reference"); } // cache the Xml _cachedXml = value; }