public void CtorGoodUri()
 {
     var uri = new UriIdentifier(this.goodUri);
     Assert.AreEqual(new Uri(this.goodUri), uri.Uri);
     Assert.IsFalse(uri.SchemeImplicitlyPrepended);
     Assert.IsFalse(uri.IsDiscoverySecureEndToEnd);
 }
		public void DiscoveryRequireSslWithInsecureXrdsInSecureHtmlHead() {
			var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false);
			Uri secureClaimedUri = new Uri("https://localhost/secureId");

			string html = string.Format("<html><head><meta http-equiv='X-XRDS-Location' content='{0}'/></head><body></body></html>", insecureXrdsSource);
			this.MockResponder.RegisterMockResponse(secureClaimedUri, "text/html", html);

			Identifier userSuppliedIdentifier = new UriIdentifier(secureClaimedUri, true);
			Assert.AreEqual(0, this.Discover(userSuppliedIdentifier).Count());
		}
示例#3
0
		internal static void RegisterMockXrdsResponse(this TestBase test, UriIdentifier directedIdentityAssignedIdentifier, IdentifierDiscoveryResult providerEndpoint) {
			IdentifierDiscoveryResult identityEndpoint = IdentifierDiscoveryResult.CreateForClaimedIdentifier(
				directedIdentityAssignedIdentifier,
				directedIdentityAssignedIdentifier,
				providerEndpoint.ProviderLocalIdentifier,
				new ProviderEndpointDescription(providerEndpoint.ProviderEndpoint, providerEndpoint.Capabilities),
				10,
				10);
			RegisterMockXrdsResponse(test, identityEndpoint);
		}
示例#4
0
        /// <summary>
        /// Tests equality between this URI and another URI.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">
        /// The <paramref name="obj"/> parameter is null.
        /// </exception>
        public override bool Equals(object obj)
        {
            UriIdentifier other = obj as UriIdentifier;

            if (other == null)
            {
                return(false);
            }
            return(this.Uri == other.Uri);
        }
        private static void ValidateXmlDSig(XrdsDocument document, UriIdentifier identifier, IncomingWebResponse response, string signingHost)
        {
            Requires.NotNull(document, "document");
            Requires.NotNull(identifier, "identifier");
            Requires.NotNull(response, "response");

            var signatureNode = document.Node.SelectSingleNode("/xrds:XRDS/ds:Signature", document.XmlNamespaceResolver);

            ErrorUtilities.VerifyProtocol(signatureNode != null, OpenIdStrings.MissingElement, "Signature");
            var signedInfoNode = signatureNode.SelectSingleNode("ds:SignedInfo", document.XmlNamespaceResolver);

            ErrorUtilities.VerifyProtocol(signedInfoNode != null, OpenIdStrings.MissingElement, "SignedInfo");
            ErrorUtilities.VerifyProtocol(
                signedInfoNode.SelectSingleNode("ds:CanonicalizationMethod[@Algorithm='http://docs.oasis-open.org/xri/xrd/2009/01#canonicalize-raw-octets']", document.XmlNamespaceResolver) != null,
                OpenIdStrings.UnsupportedCanonicalizationMethod);
            ErrorUtilities.VerifyProtocol(
                signedInfoNode.SelectSingleNode("ds:SignatureMethod[@Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1']", document.XmlNamespaceResolver) != null,
                OpenIdStrings.UnsupportedSignatureMethod);
            var certNodes = signatureNode.Select("ds:KeyInfo/ds:X509Data/ds:X509Certificate", document.XmlNamespaceResolver);

            ErrorUtilities.VerifyProtocol(certNodes.Count > 0, OpenIdStrings.MissingElement, "X509Certificate");
            var certs = certNodes.Cast <XPathNavigator>().Select(n => new X509Certificate2(Convert.FromBase64String(n.Value.Trim()))).ToList();

            // Verify that we trust the signer of the certificates.
            // Start by trying to validate just the certificate used to sign the XRDS document,
            // since we can do that with partial trust.
            Logger.OpenId.Debug("Verifying that we trust the certificate used to sign the discovery document.");
            if (!certs[0].Verify())
            {
                // We couldn't verify just the signing certificate, so try to verify the whole certificate chain.
                try {
                    Logger.OpenId.Debug("Verifying the whole certificate chain.");
                    VerifyCertChain(certs);
                    Logger.OpenId.Debug("Certificate chain verified.");
                } catch (SecurityException) {
                    Logger.Yadis.Warn("Signing certificate verification failed and we have insufficient code access security permissions to perform certificate chain validation.");
                    ErrorUtilities.ThrowProtocol(OpenIdStrings.X509CertificateNotTrusted);
                }
            }

            // Verify that the certificate is issued to the host on whom we are performing discovery.
            string hostName = certs[0].GetNameInfo(X509NameType.DnsName, false);

            ErrorUtilities.VerifyProtocol(string.Equals(hostName, signingHost, StringComparison.OrdinalIgnoreCase), OpenIdStrings.MisdirectedSigningCertificate, hostName, signingHost);

            // Verify the signature itself
            byte[] signature = Convert.FromBase64String(response.Headers["Signature"]);
            var    provider  = (RSACryptoServiceProvider)certs.First().PublicKey.Key;

            byte[] data = new byte[response.ResponseStream.Length];
            response.ResponseStream.Seek(0, SeekOrigin.Begin);
            response.ResponseStream.Read(data, 0, data.Length);
            ErrorUtilities.VerifyProtocol(provider.VerifyData(data, "SHA1", signature), OpenIdStrings.InvalidDSig);
        }
		public void DiscoveryWithRedirects() {
			Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, false);

			// Add a couple of chained redirect pages that lead to the claimedId.
			Uri userSuppliedUri = new Uri("https://localhost/someSecurePage");
			Uri insecureMidpointUri = new Uri("http://localhost/insecureStop");
			this.MockResponder.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri);
			this.MockResponder.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString()));

			// don't require secure SSL discovery for this test.
			Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, false);
			Assert.AreEqual(1, this.Discover(userSuppliedIdentifier).Count());
		}
		public void DiscoverRequireSslWithSecureRedirects() {
			Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, true);

			// Add a couple of chained redirect pages that lead to the claimedId.
			// All redirects should be secure.
			Uri userSuppliedUri = new Uri("https://localhost/someSecurePage");
			Uri secureMidpointUri = new Uri("https://localhost/secureStop");
			this.MockResponder.RegisterMockRedirect(userSuppliedUri, secureMidpointUri);
			this.MockResponder.RegisterMockRedirect(secureMidpointUri, new Uri(claimedId.ToString()));

			Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true);
			Assert.AreEqual(1, this.Discover(userSuppliedIdentifier).Count());
		}
		/// <summary>
		/// Creates the service endpoints described in this document, useful for requesting
		/// authentication of one of the OpenID Providers that result from it.
		/// </summary>
		/// <param name="xrds">The XrdsDocument instance to use in this process.</param>
		/// <param name="claimedIdentifier">The claimed identifier that was used to discover this XRDS document.</param>
		/// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
		/// <returns>
		/// A sequence of OpenID Providers that can assert ownership of the <paramref name="claimedIdentifier"/>.
		/// </returns>
		internal static IEnumerable<IdentifierDiscoveryResult> CreateServiceEndpoints(this IEnumerable<XrdElement> xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier) {
			Requires.NotNull(xrds, "xrds");
			Requires.NotNull(claimedIdentifier, "claimedIdentifier");
			Requires.NotNull(userSuppliedIdentifier, "userSuppliedIdentifier");

			var endpoints = new List<IdentifierDiscoveryResult>();
			endpoints.AddRange(xrds.GenerateOPIdentifierServiceEndpoints(userSuppliedIdentifier));
			endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(claimedIdentifier, userSuppliedIdentifier));

			Logger.Yadis.DebugFormat("Total services discovered in XRDS: {0}", endpoints.Count);
			Logger.Yadis.Debug(endpoints.ToStringDeferred(true));
			return endpoints;
		}
示例#9
0
 /// <summary>
 /// Performs YADIS discovery on some identifier.
 /// </summary>
 /// <param name="requestHandler">The mechanism to use for sending HTTP requests.</param>
 /// <param name="uri">The URI to perform discovery on.</param>
 /// <param name="requireSsl">Whether discovery should fail if any step of it is not encrypted.</param>
 /// <returns>
 /// The result of discovery on the given URL.
 /// Null may be returned if an error occurs,
 /// or if <paramref name="requireSsl"/> is true but part of discovery
 /// is not protected by SSL.
 /// </returns>
 public static DiscoveryResult Discover(IDirectWebRequestHandler requestHandler, UriIdentifier uri, bool requireSsl)
 {
     CachedDirectWebResponse response;
     try {
         if (requireSsl && !string.Equals(uri.Uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) {
             Logger.Yadis.WarnFormat("Discovery on insecure identifier '{0}' aborted.", uri);
             return null;
         }
         response = Request(requestHandler, uri, requireSsl, ContentTypes.Html, ContentTypes.XHtml, ContentTypes.Xrds).GetSnapshot(MaximumResultToScan);
         if (response.Status != System.Net.HttpStatusCode.OK) {
             Logger.Yadis.ErrorFormat("HTTP error {0} {1} while performing discovery on {2}.", (int)response.Status, response.Status, uri);
             return null;
         }
     } catch (ArgumentException ex) {
         // Unsafe URLs generate this
         Logger.Yadis.WarnFormat("Unsafe OpenId URL detected ({0}).  Request aborted.  {1}", uri, ex);
         return null;
     }
     CachedDirectWebResponse response2 = null;
     if (IsXrdsDocument(response)) {
         Logger.Yadis.Debug("An XRDS response was received from GET at user-supplied identifier.");
         response2 = response;
     } else {
         string uriString = response.Headers.Get(HeaderName);
         Uri url = null;
         if (uriString != null) {
             if (Uri.TryCreate(uriString, UriKind.Absolute, out url)) {
                 Logger.Yadis.DebugFormat("{0} found in HTTP header.  Preparing to pull XRDS from {1}", HeaderName, url);
             }
         }
         if (url == null && response.ContentType != null && response.ContentType.MediaType == ContentTypes.Html) {
             url = FindYadisDocumentLocationInHtmlMetaTags(response.GetResponseString());
             if (url != null) {
                 Logger.Yadis.DebugFormat("{0} found in HTML Http-Equiv tag.  Preparing to pull XRDS from {1}", HeaderName, url);
             }
         }
         if (url != null) {
             if (!requireSsl || string.Equals(url.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) {
                 response2 = Request(requestHandler, url, requireSsl, ContentTypes.Xrds).GetSnapshot(MaximumResultToScan);
                 if (response2.Status != HttpStatusCode.OK) {
                     Logger.Yadis.ErrorFormat("HTTP error {0} {1} while performing discovery on {2}.", (int)response2.Status, response2.Status, uri);
                     return null;
                 }
             } else {
                 Logger.Yadis.WarnFormat("XRDS document at insecure location '{0}'.  Aborting YADIS discovery.", url);
             }
         }
     }
     return new DiscoveryResult(uri, response, response2);
 }
示例#10
0
        /// <summary>
        /// Creates the service endpoints described in this document, useful for requesting
        /// authentication of one of the OpenID Providers that result from it.
        /// </summary>
        /// <param name="xrds">The XrdsDocument instance to use in this process.</param>
        /// <param name="claimedIdentifier">The claimed identifier that was used to discover this XRDS document.</param>
        /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
        /// <returns>
        /// A sequence of OpenID Providers that can assert ownership of the <paramref name="claimedIdentifier"/>.
        /// </returns>
        internal static IEnumerable<ServiceEndpoint> CreateServiceEndpoints(this XrdsDocument xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier)
        {
            var endpoints = new List<ServiceEndpoint>();
            endpoints.AddRange(xrds.GenerateOPIdentifierServiceEndpoints(userSuppliedIdentifier));

            // If any OP Identifier service elements were found, we must not proceed
            // to return any Claimed Identifier services.
            if (endpoints.Count == 0) {
                endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(claimedIdentifier, userSuppliedIdentifier));
            }
            Logger.Yadis.DebugFormat("Total services discovered in XRDS: {0}", endpoints.Count);
            Logger.Yadis.Debug(endpoints.ToStringDeferred(true));
            return endpoints;
        }
		public void DiscoverRequireSslWithInsecureRedirect() {
			Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, true);

			// Add a couple of chained redirect pages that lead to the claimedId.
			// Include an insecure HTTP jump in those redirects to verify that
			// the ultimate endpoint is never found as a result of high security profile.
			Uri userSuppliedUri = new Uri("https://localhost/someSecurePage");
			Uri insecureMidpointUri = new Uri("http://localhost/insecureStop");
			this.MockResponder.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri);
			this.MockResponder.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString()));

			Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true);
			this.Discover(userSuppliedIdentifier);
		}
示例#12
0
        /// <summary>
        /// Tests equality between this URI and another URI.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">
        /// The <paramref name="obj"/> parameter is null.
        /// </exception>
        public override bool Equals(object obj)
        {
            UriIdentifier other = obj as UriIdentifier;

            if (obj != null && other == null && Identifier.EqualityOnStrings)               // test hook to enable MockIdentifier comparison
            {
                other = Identifier.Parse(obj.ToString()) as UriIdentifier;
            }
            if (other == null)
            {
                return(false);
            }
            return(this.Uri == other.Uri);
        }
示例#13
0
        /// <summary>
        /// Gets the XRDS HTTP response for a given identifier.
        /// </summary>
        /// <param name="identifier">The identifier.</param>
        /// <param name="requestHandler">The request handler.</param>
        /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param>
        /// <returns>A HTTP response carrying an XRDS document, or <c>null</c> if one could not be obtained.</returns>
        /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception>
        private IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost)
        {
            Requires.NotNull(identifier, "identifier");
            Requires.NotNull(requestHandler, "requestHandler");
            Uri xrdsLocation = this.GetXrdsLocation(identifier, requestHandler, out signingHost);

            if (xrdsLocation == null)
            {
                return(null);
            }

            var response = GetXrdsResponse(identifier, requestHandler, xrdsLocation);

            return(response);
        }
        /// <summary>
        /// Gets the XRDS HTTP response for a given identifier.
        /// </summary>
        /// <param name="identifier">The identifier.</param>
        /// <param name="requestHandler">The request handler.</param>
        /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param>
        /// <returns>A HTTP response carrying an XRDS document, or <c>null</c> if one could not be obtained.</returns>
        /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception>
        private IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost)
        {
            Contract.Requires <ArgumentNullException>(identifier != null);
            Contract.Requires <ArgumentNullException>(requestHandler != null);
            Uri xrdsLocation = this.GetXrdsLocation(identifier, requestHandler, out signingHost);

            if (xrdsLocation == null)
            {
                return(null);
            }

            var response = GetXrdsResponse(identifier, requestHandler, xrdsLocation);

            return(response);
        }
示例#15
0
        public static Identifier Parse(string identifier, bool serializeExactValue)
        {
            Requires.NotNullOrEmpty(identifier, "identifier");

            Identifier id;

            if (XriIdentifier.IsValidXri(identifier))
            {
                id = new XriIdentifier(identifier);
            }
            else
            {
                id = new UriIdentifier(identifier);
            }

            id.IsDeserializedInstance = serializeExactValue;
            return(id);
        }
        /// <summary>
        /// Gets the URIs authorized to host host-meta documents on behalf of a given domain.
        /// </summary>
        /// <param name="identifier">The identifier.</param>
        /// <returns>A sequence of URIs that MAY provide the host-meta for a given identifier.</returns>
        private IEnumerable <HostMetaProxy> GetHostMetaLocations(UriIdentifier identifier)
        {
            Contract.Requires <ArgumentNullException>(identifier != null);

            // First try the proxies, as they are considered more "secure" than the local
            // host-meta for a domain since the domain may be defaced.
            IEnumerable <HostMetaProxy> result = this.TrustedHostMetaProxies;

            // Finally, look for the local host-meta.
            UriBuilder localHostMetaBuilder = new UriBuilder();

            localHostMetaBuilder.Scheme = identifier.IsDiscoverySecureEndToEnd || identifier.Uri.IsTransportSecure() ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
            localHostMetaBuilder.Host   = identifier.Uri.Host;
            localHostMetaBuilder.Path   = LocalHostMetaPath;
            result = result.Concat(new[] { new HostMetaProxy(localHostMetaBuilder.Uri.AbsoluteUri, identifier.Uri.Host) });

            return(result);
        }
示例#17
0
        public static Identifier Parse(string identifier, bool serializeExactValue)
        {
            Contract.Requires <ArgumentException>(!String.IsNullOrEmpty(identifier));
            Contract.Ensures(Contract.Result <Identifier>() != null);

            Identifier id;

            if (XriIdentifier.IsValidXri(identifier))
            {
                id = new XriIdentifier(identifier);
            }
            else
            {
                id = new UriIdentifier(identifier);
            }

            id.IsDeserializedInstance = serializeExactValue;
            return(id);
        }
示例#18
0
        /// <summary>
        /// Gets the XRDS HTTP response for a given identifier.
        /// </summary>
        /// <param name="identifier">The identifier.</param>
        /// <param name="requestHandler">The request handler.</param>
        /// <param name="xrdsLocation">The location of the XRDS document to retrieve.</param>
        /// <returns>
        /// A HTTP response carrying an XRDS document.
        /// </returns>
        /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception>
        private static IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, Uri xrdsLocation)
        {
            Requires.NotNull(identifier, "identifier");
            Requires.NotNull(requestHandler, "requestHandler");
            Requires.NotNull(xrdsLocation, "xrdsLocation");

            var request = (HttpWebRequest)WebRequest.Create(xrdsLocation);

            request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy;
            request.Accept      = ContentTypes.Xrds;
            var options  = identifier.IsDiscoverySecureEndToEnd ? DirectWebRequestOptions.RequireSsl : DirectWebRequestOptions.None;
            var response = requestHandler.GetResponse(request, options).GetSnapshot(Yadis.MaximumResultToScan);

            if (!string.Equals(response.ContentType.MediaType, ContentTypes.Xrds, StringComparison.Ordinal))
            {
                Logger.Yadis.WarnFormat("Host-meta pointed to XRDS at {0}, but Content-Type at that URL was unexpected value '{1}'.", xrdsLocation, response.ContentType);
            }

            return(response);
        }
        private static async Task ValidateXmlDSigAsync(XrdsDocument document, UriIdentifier identifier, HttpResponseMessage response, string signingHost)
        {
            Requires.NotNull(document, "document");
            Requires.NotNull(identifier, "identifier");
            Requires.NotNull(response, "response");

            var signatureNode = document.Node.SelectSingleNode("/xrds:XRDS/ds:Signature", document.XmlNamespaceResolver);

            ErrorUtilities.VerifyProtocol(signatureNode != null, OpenIdStrings.MissingElement, "Signature");
            var signedInfoNode = signatureNode.SelectSingleNode("ds:SignedInfo", document.XmlNamespaceResolver);

            ErrorUtilities.VerifyProtocol(signedInfoNode != null, OpenIdStrings.MissingElement, "SignedInfo");
            ErrorUtilities.VerifyProtocol(
                signedInfoNode.SelectSingleNode("ds:CanonicalizationMethod[@Algorithm='http://docs.oasis-open.org/xri/xrd/2009/01#canonicalize-raw-octets']", document.XmlNamespaceResolver) != null,
                OpenIdStrings.UnsupportedCanonicalizationMethod);
            ErrorUtilities.VerifyProtocol(
                signedInfoNode.SelectSingleNode("ds:SignatureMethod[@Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1']", document.XmlNamespaceResolver) != null,
                OpenIdStrings.UnsupportedSignatureMethod);
            var certNodes = signatureNode.Select("ds:KeyInfo/ds:X509Data/ds:X509Certificate", document.XmlNamespaceResolver);

            ErrorUtilities.VerifyProtocol(certNodes.Count > 0, OpenIdStrings.MissingElement, "X509Certificate");
            var certs = certNodes.Cast <XPathNavigator>().Select(n => new X509Certificate2(Convert.FromBase64String(n.Value.Trim()))).ToList();

            VerifyCertificateChain(certs);

            // Verify that the certificate is issued to the host on whom we are performing discovery.
            string hostName = certs[0].GetNameInfo(X509NameType.DnsName, false);

            ErrorUtilities.VerifyProtocol(string.Equals(hostName, signingHost, StringComparison.OrdinalIgnoreCase), OpenIdStrings.MisdirectedSigningCertificate, hostName, signingHost);

            // Verify the signature itself
            byte[] signature      = Convert.FromBase64String(response.Headers.GetValues("Signature").First());
            var    provider       = (RSACryptoServiceProvider)certs.First().PublicKey.Key;
            var    responseStream = await response.Content.ReadAsStreamAsync();

            byte[] data = new byte[responseStream.Length];
            responseStream.Seek(0, SeekOrigin.Begin);
            await responseStream.ReadAsync(data, 0, data.Length);

            ErrorUtilities.VerifyProtocol(provider.VerifyData(data, "SHA1", signature), OpenIdStrings.InvalidDSig);
        }
        /// <summary>
        /// Gets the XRDS HTTP response for a given identifier.
        /// </summary>
        /// <param name="identifier">The identifier.</param>
        /// <param name="requestHandler">The request handler.</param>
        /// <param name="xrdsLocation">The location of the XRDS document to retrieve.</param>
        /// <returns>
        /// A HTTP response carrying an XRDS document.
        /// </returns>
        /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception>
        private static IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, Uri xrdsLocation)
        {
            Contract.Requires <ArgumentNullException>(identifier != null);
            Contract.Requires <ArgumentNullException>(requestHandler != null);
            Contract.Requires <ArgumentNullException>(xrdsLocation != null);
            Contract.Ensures(Contract.Result <IncomingWebResponse>() != null);

            var request = (HttpWebRequest)WebRequest.Create(xrdsLocation);

            request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy;
            request.Accept      = ContentTypes.Xrds;
            var options  = identifier.IsDiscoverySecureEndToEnd ? DirectWebRequestOptions.RequireSsl : DirectWebRequestOptions.None;
            var response = requestHandler.GetResponse(request, options);

            if (!string.Equals(response.ContentType.MediaType, ContentTypes.Xrds, StringComparison.Ordinal))
            {
                Logger.Yadis.WarnFormat("Host-meta pointed to XRDS at {0}, but Content-Type at that URL was unexpected value '{1}'.", xrdsLocation, response.ContentType);
            }

            return(response);
        }
示例#21
0
        /// <summary>
        /// Tests equality between this URI and another URI.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">
        /// The <paramref name="obj"/> parameter is null.
        /// </exception>
        public override bool Equals(object obj)
        {
            UriIdentifier other = obj as UriIdentifier;

            if (obj != null && other == null && Identifier.EqualityOnStrings)               // test hook to enable MockIdentifier comparison
            {
                other = Identifier.Parse(obj.ToString()) as UriIdentifier;
            }
            if (other == null)
            {
                return(false);
            }

            if (this.ProblematicNormalization || other.ProblematicNormalization)
            {
                return(new SimpleUri(this.OriginalString).Equals(new SimpleUri(other.OriginalString)));
            }
            else
            {
                return(this.Uri == other.Uri);
            }
        }
示例#22
0
        /// <summary>
        /// Gets the host-meta for a given identifier.
        /// </summary>
        /// <param name="identifier">The identifier.</param>
        /// <param name="requestHandler">The request handler.</param>
        /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param>
        /// <returns>
        /// The host-meta response, or <c>null</c> if no host-meta document could be obtained.
        /// </returns>
        private IncomingWebResponse GetHostMeta(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost)
        {
            Requires.NotNull(identifier, "identifier");
            Requires.NotNull(requestHandler, "requestHandler");
            foreach (var hostMetaProxy in this.GetHostMetaLocations(identifier))
            {
                var hostMetaLocation = hostMetaProxy.GetProxy(identifier);
                var request          = (HttpWebRequest)WebRequest.Create(hostMetaLocation);
                request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy;
                var options = DirectWebRequestOptions.AcceptAllHttpResponses;
                if (identifier.IsDiscoverySecureEndToEnd)
                {
                    options |= DirectWebRequestOptions.RequireSsl;
                }
                var response = requestHandler.GetResponse(request, options).GetSnapshot(Yadis.MaximumResultToScan);
                try {
                    if (response.Status == HttpStatusCode.OK)
                    {
                        Logger.Yadis.InfoFormat("Found host-meta for {0} at: {1}", identifier.Uri.Host, hostMetaLocation);
                        signingHost = hostMetaProxy.GetSigningHost(identifier);
                        return(response);
                    }
                    else
                    {
                        Logger.Yadis.InfoFormat("Could not obtain host-meta for {0} from {1}", identifier.Uri.Host, hostMetaLocation);
                        response.Dispose();
                    }
                } catch {
                    response.Dispose();
                    throw;
                }
            }

            signingHost = null;
            return(null);
        }
示例#23
0
        /// <summary>
        /// Creates the service endpoints described in this document, useful for requesting
        /// authentication of one of the OpenID Providers that result from it.
        /// </summary>
        /// <param name="xrds">The XrdsDocument instance to use in this process.</param>
        /// <param name="claimedIdentifier">The claimed identifier that was used to discover this XRDS document.</param>
        /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
        /// <returns>
        /// A sequence of OpenID Providers that can assert ownership of the <paramref name="claimedIdentifier"/>.
        /// </returns>
        internal static IEnumerable <ServiceEndpoint> CreateServiceEndpoints(this XrdsDocument xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier)
        {
            var endpoints = new List <ServiceEndpoint>();

            endpoints.AddRange(xrds.GenerateOPIdentifierServiceEndpoints(claimedIdentifier));

            // If any OP Identifier service elements were found, we must not proceed
            // to return any Claimed Identifier services.
            if (endpoints.Count == 0)
            {
                endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(claimedIdentifier, userSuppliedIdentifier));
            }
            Logger.DebugFormat("Total services discovered in XRDS: {0}", endpoints.Count);
            Logger.Debug(endpoints.ToStringDeferred(true));
            return(endpoints);
        }
示例#24
0
        /// <summary>
        /// Creates the service endpoints described in this document, useful for requesting
        /// authentication of one of the OpenID Providers that result from it.
        /// </summary>
        /// <param name="xrds">The XrdsDocument instance to use in this process.</param>
        /// <param name="claimedIdentifier">The claimed identifier that was used to discover this XRDS document.</param>
        /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
        /// <returns>
        /// A sequence of OpenID Providers that can assert ownership of the <paramref name="claimedIdentifier"/>.
        /// </returns>
        internal static IEnumerable <IdentifierDiscoveryResult> CreateServiceEndpoints(this IEnumerable <XrdElement> xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier)
        {
            Requires.NotNull(xrds, "xrds");
            Requires.NotNull(claimedIdentifier, "claimedIdentifier");
            Requires.NotNull(userSuppliedIdentifier, "userSuppliedIdentifier");

            var endpoints = new List <IdentifierDiscoveryResult>();

            endpoints.AddRange(xrds.GenerateOPIdentifierServiceEndpoints(userSuppliedIdentifier));
            endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(claimedIdentifier, userSuppliedIdentifier));

            Logger.Yadis.DebugFormat("Total services discovered in XRDS: {0}", endpoints.Count);
            Logger.Yadis.Debug(endpoints.ToStringDeferred(true).ToString());
            return(endpoints);
        }
			/// <summary>
			/// Gets the signing host URI.
			/// </summary>
			/// <param name="identifier">The identifier being discovered.</param>
			/// <returns>A host name.</returns>
			public virtual string GetSigningHost(UriIdentifier identifier) {
				Contract.Requires<ArgumentNullException>(identifier != null);
				return string.Format(CultureInfo.InvariantCulture, this.SigningHostFormat, identifier.Uri.Host, this.GetProxy(identifier).Host);
			}
		/// <summary>
		/// Gets the URIs authorized to host host-meta documents on behalf of a given domain.
		/// </summary>
		/// <param name="identifier">The identifier.</param>
		/// <returns>A sequence of URIs that MAY provide the host-meta for a given identifier.</returns>
		private IEnumerable<HostMetaProxy> GetHostMetaLocations(UriIdentifier identifier) {
			Contract.Requires<ArgumentNullException>(identifier != null);

			// First try the proxies, as they are considered more "secure" than the local
			// host-meta for a domain since the domain may be defaced.
			IEnumerable<HostMetaProxy> result = this.TrustedHostMetaProxies;

			// Finally, look for the local host-meta.
			UriBuilder localHostMetaBuilder = new UriBuilder();
			localHostMetaBuilder.Scheme = identifier.IsDiscoverySecureEndToEnd || identifier.Uri.IsTransportSecure() ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
			localHostMetaBuilder.Host = identifier.Uri.Host;
			localHostMetaBuilder.Path = LocalHostMetaPath;
			result = result.Concat(new[] { new HostMetaProxy(localHostMetaBuilder.Uri.AbsoluteUri, identifier.Uri.Host) });

			return result;
		}
		/// <summary>
		/// Gets the location of the XRDS document that describes a given identifier.
		/// </summary>
		/// <param name="identifier">The identifier under discovery.</param>
		/// <param name="requestHandler">The request handler.</param>
		/// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param>
		/// <returns>An absolute URI, or <c>null</c> if one could not be determined.</returns>
		private Uri GetXrdsLocation(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) {
			Contract.Requires<ArgumentNullException>(identifier != null);
			Contract.Requires<ArgumentNullException>(requestHandler != null);
			using (var hostMetaResponse = this.GetHostMeta(identifier, requestHandler, out signingHost)) {
				if (hostMetaResponse == null) {
					return null;
				}

				using (var sr = hostMetaResponse.GetResponseReader()) {
					string line = sr.ReadLine();
					Match m = HostMetaLink.Match(line);
					if (m.Success) {
						Uri location = new Uri(m.Groups["location"].Value);
						Logger.Yadis.InfoFormat("Found link to XRDS at {0} in host-meta document {1}.", location, hostMetaResponse.FinalUri);
						return location;
					}
				}

				Logger.Yadis.WarnFormat("Could not find link to XRDS in host-meta document: {0}", hostMetaResponse.FinalUri);
				return null;
			}
		}
		/// <summary>
		/// Gets the XRDS HTTP response for a given identifier.
		/// </summary>
		/// <param name="identifier">The identifier.</param>
		/// <param name="requestHandler">The request handler.</param>
		/// <param name="xrdsLocation">The location of the XRDS document to retrieve.</param>
		/// <returns>
		/// A HTTP response carrying an XRDS document.
		/// </returns>
		/// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception>
		private static IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, Uri xrdsLocation) {
			Contract.Requires<ArgumentNullException>(identifier != null);
			Contract.Requires<ArgumentNullException>(requestHandler != null);
			Contract.Requires<ArgumentNullException>(xrdsLocation != null);
			Contract.Ensures(Contract.Result<IncomingWebResponse>() != null);

			var request = (HttpWebRequest)WebRequest.Create(xrdsLocation);
			request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy;
			request.Accept = ContentTypes.Xrds;
			var options = identifier.IsDiscoverySecureEndToEnd ? DirectWebRequestOptions.RequireSsl : DirectWebRequestOptions.None;
			var response = requestHandler.GetResponse(request, options).GetSnapshot(Yadis.MaximumResultToScan);
			if (!string.Equals(response.ContentType.MediaType, ContentTypes.Xrds, StringComparison.Ordinal)) {
				Logger.Yadis.WarnFormat("Host-meta pointed to XRDS at {0}, but Content-Type at that URL was unexpected value '{1}'.", xrdsLocation, response.ContentType);
			}

			return response;
		}
		public void TrailingPeriodsNotTrimmed() {
			TestAsFullAndPartialTrust(fullTrust => {
				string claimedIdentifier = "https://me.yahoo.com/a/AsDf.#asdf";
				Identifier id = claimedIdentifier;
				Assert.AreEqual(claimedIdentifier, id.OriginalString);
				Assert.AreEqual(claimedIdentifier, id.ToString());

				UriIdentifier idUri = new UriIdentifier(claimedIdentifier);
				Assert.AreEqual(claimedIdentifier, idUri.OriginalString);
				Assert.AreEqual(claimedIdentifier, idUri.ToString());
				if (fullTrust) {
					Assert.AreEqual(claimedIdentifier, idUri.Uri.AbsoluteUri);
				}
				Assert.AreEqual(Uri.UriSchemeHttps, idUri.Uri.Scheme); // in case custom scheme tricks are played, this must still match
				Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().ToString());
				Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().OriginalString);
				Assert.AreEqual(id.ToString(), new UriIdentifier((Uri)idUri).ToString(), "Round tripping UriIdentifier->Uri->UriIdentifier failed.");

				idUri = new UriIdentifier(new Uri(claimedIdentifier));
				Assert.AreEqual(claimedIdentifier, idUri.OriginalString);
				Assert.AreEqual(claimedIdentifier, idUri.ToString());
				if (fullTrust) {
					Assert.AreEqual(claimedIdentifier, idUri.Uri.AbsoluteUri);
				}
				Assert.AreEqual(Uri.UriSchemeHttps, idUri.Uri.Scheme); // in case custom scheme tricks are played, this must still match
				Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().ToString());
				Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().OriginalString);
				Assert.AreEqual(id.ToString(), new UriIdentifier((Uri)idUri).ToString(), "Round tripping UriIdentifier->Uri->UriIdentifier failed.");

				claimedIdentifier = "https://me.yahoo.com:443/a/AsDf.#asdf";
				id = claimedIdentifier;
				Assert.AreEqual(claimedIdentifier, id.OriginalString);
				Assert.AreEqual("https://me.yahoo.com/a/AsDf.#asdf", id.ToString());
			});
		}
		public void DoesNotStripFragment() {
			Uri original = new Uri("http://a/b#c");
			UriIdentifier identifier = new UriIdentifier(original);
			Assert.AreEqual(original.Fragment, identifier.Uri.Fragment);
		}
		public void CtorUriHttpsSchemeSecure() {
			var uri = new UriIdentifier(new Uri("https://host/path"), true);
			Assert.AreEqual("https://host/path", uri.Uri.AbsoluteUri);
			Assert.IsTrue(uri.IsDiscoverySecureEndToEnd);
		}
		public void UnicodeHostSupport() {
			var id = new UriIdentifier("http://server崎/村");
			Assert.AreEqual("server崎", id.Uri.Host);
		}
		public void TryRequireSslAdjustsIdentifier() {
			Identifier secureId;
			// Try Parse and ctor without explicit scheme
			var id = Identifier.Parse("www.yahoo.com");
			Assert.AreEqual("http://www.yahoo.com/", id.ToString());
			Assert.IsTrue(id.TryRequireSsl(out secureId));
			Assert.IsTrue(secureId.IsDiscoverySecureEndToEnd);
			Assert.AreEqual("https://www.yahoo.com/", secureId.ToString());

			id = new UriIdentifier("www.yahoo.com");
			Assert.AreEqual("http://www.yahoo.com/", id.ToString());
			Assert.IsTrue(id.TryRequireSsl(out secureId));
			Assert.IsTrue(secureId.IsDiscoverySecureEndToEnd);
			Assert.AreEqual("https://www.yahoo.com/", secureId.ToString());

			// Try Parse and ctor with explicit http:// scheme
			id = Identifier.Parse("http://www.yahoo.com");
			Assert.IsFalse(id.TryRequireSsl(out secureId));
			Assert.IsTrue(secureId.IsDiscoverySecureEndToEnd, "Although the TryRequireSsl failed, the created identifier should retain the Ssl status.");
			Assert.AreEqual("http://www.yahoo.com/", secureId.ToString());
			Assert.AreEqual(0, Discover(secureId).Count(), "Since TryRequireSsl failed, the created Identifier should never discover anything.");

			id = new UriIdentifier("http://www.yahoo.com");
			Assert.IsFalse(id.TryRequireSsl(out secureId));
			Assert.IsTrue(secureId.IsDiscoverySecureEndToEnd);
			Assert.AreEqual("http://www.yahoo.com/", secureId.ToString());
			Assert.AreEqual(0, Discover(secureId).Count());
		}
		////[Test, Ignore("The spec says http:// must be prepended in this case, but that just creates an invalid URI.  Our UntrustedWebRequest will stop disallowed schemes.")]
		public void CtorDisallowedScheme() {
			UriIdentifier id = new UriIdentifier(new Uri("ftp://host/path"));
			Assert.AreEqual("http://ftp://host/path", id.ToString());
			Assert.IsTrue(id.SchemeImplicitlyPrepended);
		}
		public void HttpSchemePrepended() {
			UriIdentifier id = new UriIdentifier("www.yahoo.com");
			Assert.AreEqual("http://www.yahoo.com/", id.ToString());
			Assert.IsTrue(id.SchemeImplicitlyPrepended);
		}
		public void DiscoveryRequireSslWithInsecureXrdsInSecureHttpHeader() {
			var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false);

			string html = "<html><head></head><body></body></html>";
			WebHeaderCollection headers = new WebHeaderCollection {
				{ "X-XRDS-Location", insecureXrdsSource }
			};
			this.MockResponder.RegisterMockResponse(VanityUriSsl, VanityUriSsl, "text/html", headers, html);

			Identifier userSuppliedIdentifier = new UriIdentifier(VanityUriSsl, true);
			Assert.AreEqual(0, this.Discover(userSuppliedIdentifier).Count());
		}
        /// <summary>
        /// Gets the services for an identifier that are described by an external XRDS document.
        /// </summary>
        /// <param name="xrds">The XRD elements to search for described-by services.</param>
        /// <param name="identifier">The identifier under discovery.</param>
        /// <param name="requestHandler">The request handler.</param>
        /// <returns>The discovered services.</returns>
        private static IEnumerable <IdentifierDiscoveryResult> GetExternalServices(IEnumerable <XrdElement> xrds, UriIdentifier identifier, IDirectWebRequestHandler requestHandler)
        {
            Contract.Requires <ArgumentNullException>(xrds != null);
            Contract.Requires <ArgumentNullException>(identifier != null);
            Contract.Requires <ArgumentNullException>(requestHandler != null);
            Contract.Ensures(Contract.Result <IEnumerable <IdentifierDiscoveryResult> >() != null);

            var results = new List <IdentifierDiscoveryResult>();

            foreach (var serviceElement in GetDescribedByServices(xrds))
            {
                var templateNode      = serviceElement.Node.SelectSingleNode("google:URITemplate", serviceElement.XmlNamespaceResolver);
                var nextAuthorityNode = serviceElement.Node.SelectSingleNode("google:NextAuthority", serviceElement.XmlNamespaceResolver);
                if (templateNode != null)
                {
                    Uri    externalLocation = new Uri(templateNode.Value.Trim().Replace("{%uri}", Uri.EscapeDataString(identifier.Uri.AbsoluteUri)));
                    string nextAuthority    = nextAuthorityNode != null?nextAuthorityNode.Value.Trim() : identifier.Uri.Host;

                    try {
                        var          externalXrdsResponse = GetXrdsResponse(identifier, requestHandler, externalLocation);
                        XrdsDocument externalXrds         = new XrdsDocument(XmlReader.Create(externalXrdsResponse.ResponseStream));
                        ValidateXmlDSig(externalXrds, identifier, externalXrdsResponse, nextAuthority);
                        results.AddRange(GetXrdElements(externalXrds, identifier).CreateServiceEndpoints(identifier, identifier));
                    } catch (ProtocolException ex) {
                        Logger.Yadis.WarnFormat("HTTP GET error while retrieving described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex);
                    } catch (XmlException ex) {
                        Logger.Yadis.ErrorFormat("Error while parsing described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex);
                    }
                }
            }

            return(results);
        }
 /// <summary>
 /// Gets the absolute proxy URI.
 /// </summary>
 /// <param name="identifier">The identifier being discovered.</param>
 /// <returns>The an absolute URI.</returns>
 public virtual Uri GetProxy(UriIdentifier identifier)
 {
     Contract.Requires <ArgumentNullException>(identifier != null);
     return(new Uri(string.Format(CultureInfo.InvariantCulture, this.ProxyFormat, Uri.EscapeDataString(identifier.Uri.Host))));
 }
		/// <summary>
		/// Validates the XML digital signature on an XRDS document.
		/// </summary>
		/// <param name="document">The XRDS document whose signature should be validated.</param>
		/// <param name="identifier">The identifier under discovery.</param>
		/// <param name="response">The response.</param>
		/// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param>
		/// <exception cref="ProtocolException">Thrown if the XRDS document has an invalid or a missing signature.</exception>
		private static void ValidateXmlDSig(XrdsDocument document, UriIdentifier identifier, IncomingWebResponse response, string signingHost) {
			Contract.Requires<ArgumentNullException>(document != null);
			Contract.Requires<ArgumentNullException>(identifier != null);
			Contract.Requires<ArgumentNullException>(response != null);

			var signatureNode = document.Node.SelectSingleNode("/xrds:XRDS/ds:Signature", document.XmlNamespaceResolver);
			ErrorUtilities.VerifyProtocol(signatureNode != null, OpenIdStrings.MissingElement, "Signature");
			var signedInfoNode = signatureNode.SelectSingleNode("ds:SignedInfo", document.XmlNamespaceResolver);
			ErrorUtilities.VerifyProtocol(signedInfoNode != null, OpenIdStrings.MissingElement, "SignedInfo");
			ErrorUtilities.VerifyProtocol(
				signedInfoNode.SelectSingleNode("ds:CanonicalizationMethod[@Algorithm='http://docs.oasis-open.org/xri/xrd/2009/01#canonicalize-raw-octets']", document.XmlNamespaceResolver) != null,
				"Unrecognized or missing canonicalization method.");
			ErrorUtilities.VerifyProtocol(
				signedInfoNode.SelectSingleNode("ds:SignatureMethod[@Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1']", document.XmlNamespaceResolver) != null,
				"Unrecognized or missing signature method.");
			var certNodes = signatureNode.Select("ds:KeyInfo/ds:X509Data/ds:X509Certificate", document.XmlNamespaceResolver);
			ErrorUtilities.VerifyProtocol(certNodes.Count > 0, OpenIdStrings.MissingElement, "X509Certificate");
			var certs = certNodes.Cast<XPathNavigator>().Select(n => new X509Certificate2(Convert.FromBase64String(n.Value.Trim()))).ToList();

			// Verify that we trust the signer of the certificates.
			// Start by trying to validate just the certificate used to sign the XRDS document,
			// since we can do that with partial trust.
			Logger.OpenId.Debug("Verifying that we trust the certificate used to sign the discovery document.");
			if (!certs[0].Verify()) {
				// We couldn't verify just the signing certificate, so try to verify the whole certificate chain.
				try {
					Logger.OpenId.Debug("Verifying the whole certificate chain.");
					VerifyCertChain(certs);
					Logger.OpenId.Debug("Certificate chain verified.");
				} catch (SecurityException) {
					Logger.Yadis.Warn("Signing certificate verification failed and we have insufficient code access security permissions to perform certificate chain validation.");
					ErrorUtilities.ThrowProtocol(OpenIdStrings.X509CertificateNotTrusted);
				}
			}

			// Verify that the certificate is issued to the host on whom we are performing discovery.
			string hostName = certs[0].GetNameInfo(X509NameType.DnsName, false);
			ErrorUtilities.VerifyProtocol(string.Equals(hostName, signingHost, StringComparison.OrdinalIgnoreCase), "X.509 signing certificate issued to {0}, but a certificate for {1} was expected.", hostName, signingHost);

			// Verify the signature itself
			byte[] signature = Convert.FromBase64String(response.Headers["Signature"]);
			var provider = (RSACryptoServiceProvider)certs.First().PublicKey.Key;
			byte[] data = new byte[response.ResponseStream.Length];
			response.ResponseStream.Seek(0, SeekOrigin.Begin);
			response.ResponseStream.Read(data, 0, data.Length);
			ErrorUtilities.VerifyProtocol(provider.VerifyData(data, "SHA1", signature), "Invalid XmlDSig signature on XRDS document.");
		}
        /// <summary>
        /// Gets the XRDS HTTP response for a given identifier.
        /// </summary>
        /// <param name="identifier">The identifier.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// A HTTP response carrying an XRDS document, or <c>null</c> if one could not be obtained.
        /// </returns>
        /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception>
        private async Task <ResultWithSigningHost <HttpResponseMessage> > GetXrdsResponseAsync(UriIdentifier identifier, CancellationToken cancellationToken)
        {
            Requires.NotNull(identifier, "identifier");

            var result = await this.GetXrdsLocationAsync(identifier, cancellationToken);

            if (result.Result == null)
            {
                return(new ResultWithSigningHost <HttpResponseMessage>());
            }

            var response = await this.GetXrdsResponseAsync(identifier, result.Result, cancellationToken);

            return(new ResultWithSigningHost <HttpResponseMessage>(response, result.SigningHost));
        }
		/// <summary>
		/// Gets the XRDS HTTP response for a given identifier.
		/// </summary>
		/// <param name="identifier">The identifier.</param>
		/// <param name="requestHandler">The request handler.</param>
		/// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param>
		/// <returns>A HTTP response carrying an XRDS document, or <c>null</c> if one could not be obtained.</returns>
		/// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception>
		private IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) {
			Contract.Requires<ArgumentNullException>(identifier != null);
			Contract.Requires<ArgumentNullException>(requestHandler != null);
			Uri xrdsLocation = this.GetXrdsLocation(identifier, requestHandler, out signingHost);
			if (xrdsLocation == null) {
				return null;
			}

			var response = GetXrdsResponse(identifier, requestHandler, xrdsLocation);

			return response;
		}
		/// <summary>
		/// Searches HTML for the HEAD META tags that describe OpenID provider services.
		/// </summary>
		/// <param name="claimedIdentifier">The final URL that provided this HTML document.
		/// This may not be the same as (this) userSuppliedIdentifier if the
		/// userSuppliedIdentifier pointed to a 301 Redirect.</param>
		/// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
		/// <param name="html">The HTML that was downloaded and should be searched.</param>
		/// <returns>
		/// A sequence of any discovered ServiceEndpoints.
		/// </returns>
		private static IEnumerable<IdentifierDiscoveryResult> DiscoverFromHtml(Uri claimedIdentifier, UriIdentifier userSuppliedIdentifier, string html) {
			var linkTags = new List<HtmlLink>(HtmlParser.HeadTags<HtmlLink>(html));
			foreach (var protocol in Protocol.AllPracticalVersions) {
				// rel attributes are supposed to be interpreted with case INsensitivity, 
				// and is a space-delimited list of values. (http://www.htmlhelp.com/reference/html40/values.html#linktypes)
				var serverLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryProviderKey) + @"\b", RegexOptions.IgnoreCase));
				if (serverLinkTag == null) {
					continue;
				}

				Uri providerEndpoint = null;
				if (Uri.TryCreate(serverLinkTag.Href, UriKind.Absolute, out providerEndpoint)) {
					// See if a LocalId tag of the discovered version exists
					Identifier providerLocalIdentifier = null;
					var delegateLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryLocalIdKey) + @"\b", RegexOptions.IgnoreCase));
					if (delegateLinkTag != null) {
						if (Identifier.IsValid(delegateLinkTag.Href)) {
							providerLocalIdentifier = delegateLinkTag.Href;
						} else {
							Logger.Yadis.WarnFormat("Skipping endpoint data because local id is badly formed ({0}).", delegateLinkTag.Href);
							continue; // skip to next version
						}
					}

					// Choose the TypeURI to match the OpenID version detected.
					string[] typeURIs = { protocol.ClaimedIdentifierServiceTypeURI };
					yield return IdentifierDiscoveryResult.CreateForClaimedIdentifier(
						claimedIdentifier,
						userSuppliedIdentifier,
						providerLocalIdentifier,
						new ProviderEndpointDescription(providerEndpoint, typeURIs),
						(int?)null,
						(int?)null);
				}
			}
		}
		/// <summary>
		/// Gets the host-meta for a given identifier.
		/// </summary>
		/// <param name="identifier">The identifier.</param>
		/// <param name="requestHandler">The request handler.</param>
		/// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param>
		/// <returns>
		/// The host-meta response, or <c>null</c> if no host-meta document could be obtained.
		/// </returns>
		private IncomingWebResponse GetHostMeta(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) {
			Contract.Requires<ArgumentNullException>(identifier != null);
			Contract.Requires<ArgumentNullException>(requestHandler != null);
			foreach (var hostMetaProxy in this.GetHostMetaLocations(identifier)) {
				var hostMetaLocation = hostMetaProxy.GetProxy(identifier);
				var request = (HttpWebRequest)WebRequest.Create(hostMetaLocation);
				request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy;
				var options = DirectWebRequestOptions.AcceptAllHttpResponses;
				if (identifier.IsDiscoverySecureEndToEnd) {
					options |= DirectWebRequestOptions.RequireSsl;
				}
				var response = requestHandler.GetResponse(request, options).GetSnapshot(Yadis.MaximumResultToScan);
				try {
					if (response.Status == HttpStatusCode.OK) {
						Logger.Yadis.InfoFormat("Found host-meta for {0} at: {1}", identifier.Uri.Host, hostMetaLocation);
						signingHost = hostMetaProxy.GetSigningHost(identifier);
						return response;
					} else {
						Logger.Yadis.InfoFormat("Could not obtain host-meta for {0} from {1}", identifier.Uri.Host, hostMetaLocation);
						response.Dispose();
					}
				} catch {
					response.Dispose();
					throw;
				}
			}

			signingHost = null;
			return null;
		}
示例#44
0
        /// <summary>
        /// Generates the OpenID Providers that are capable of asserting ownership
        /// of a particular URI claimed identifier.
        /// </summary>
        /// <param name="xrds">The XrdsDocument instance to use in this process.</param>
        /// <param name="claimedIdentifier">The claimed identifier.</param>
        /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
        /// <returns>
        /// A sequence of the providers that can assert ownership of the given identifier.
        /// </returns>
        private static IEnumerable <IdentifierDiscoveryResult> GenerateClaimedIdentifierServiceEndpoints(this IEnumerable <XrdElement> xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier)
        {
            Requires.NotNull(xrds, "xrds");
            Requires.NotNull(claimedIdentifier, "claimedIdentifier");

            return(from service in xrds.FindClaimedIdentifierServices()
                   from uri in service.UriElements
                   where uri.Uri != null
                   let providerEndpoint = new ProviderEndpointDescription(uri.Uri, service.TypeElementUris)
                                          select IdentifierDiscoveryResult.CreateForClaimedIdentifier(claimedIdentifier, userSuppliedIdentifier, service.ProviderLocalIdentifier, providerEndpoint, service.Priority, uri.Priority));
        }
			/// <summary>
			/// Gets the absolute proxy URI.
			/// </summary>
			/// <param name="identifier">The identifier being discovered.</param>
			/// <returns>The an absolute URI.</returns>
			public virtual Uri GetProxy(UriIdentifier identifier) {
				Contract.Requires<ArgumentNullException>(identifier != null);
				return new Uri(string.Format(CultureInfo.InvariantCulture, this.ProxyFormat, Uri.EscapeDataString(identifier.Uri.Host)));
			}
示例#46
0
 public static bool IsValid(string identifier)
 {
     Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(identifier));
     return(XriIdentifier.IsValidXri(identifier) || UriIdentifier.IsValidUri(identifier));
 }
		public void DiscoveryRequiresSslIgnoresInsecureEndpointsInXrds() {
			var insecureEndpoint = GetServiceEndpoint(0, ProtocolVersion.V20, 10, false);
			var secureEndpoint = GetServiceEndpoint(1, ProtocolVersion.V20, 20, true);
			UriIdentifier secureClaimedId = new UriIdentifier(VanityUriSsl, true);
			this.MockResponder.RegisterMockXrdsResponse(secureClaimedId, new IdentifierDiscoveryResult[] { insecureEndpoint, secureEndpoint });
			Assert.AreEqual(secureEndpoint.ProviderLocalIdentifier, this.Discover(secureClaimedId).Single().ProviderLocalIdentifier);
		}
        /// <summary>
        /// Gets the services for an identifier that are described by an external XRDS document.
        /// </summary>
        /// <param name="xrds">The XRD elements to search for described-by services.</param>
        /// <param name="identifier">The identifier under discovery.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// The discovered services.
        /// </returns>
        private async Task <IEnumerable <IdentifierDiscoveryResult> > GetExternalServicesAsync(IEnumerable <XrdElement> xrds, UriIdentifier identifier, CancellationToken cancellationToken)
        {
            Requires.NotNull(xrds, "xrds");
            Requires.NotNull(identifier, "identifier");

            var results = new List <IdentifierDiscoveryResult>();

            foreach (var serviceElement in GetDescribedByServices(xrds))
            {
                var templateNode      = serviceElement.Node.SelectSingleNode("google:URITemplate", serviceElement.XmlNamespaceResolver);
                var nextAuthorityNode = serviceElement.Node.SelectSingleNode("google:NextAuthority", serviceElement.XmlNamespaceResolver);
                if (templateNode != null)
                {
                    Uri    externalLocation = new Uri(templateNode.Value.Trim().Replace("{%uri}", Uri.EscapeDataString(identifier.Uri.AbsoluteUri)));
                    string nextAuthority    = nextAuthorityNode != null?nextAuthorityNode.Value.Trim() : identifier.Uri.Host;

                    try {
                        using (var externalXrdsResponse = await this.GetXrdsResponseAsync(identifier, externalLocation, cancellationToken)) {
                            var readerSettings = MessagingUtilities.CreateUntrustedXmlReaderSettings();
                            var responseStream = await externalXrdsResponse.Content.ReadAsStreamAsync();

                            XrdsDocument externalXrds = new XrdsDocument(XmlReader.Create(responseStream, readerSettings));
                            await ValidateXmlDSigAsync(externalXrds, identifier, externalXrdsResponse, nextAuthority);

                            results.AddRange(GetXrdElements(externalXrds, identifier).CreateServiceEndpoints(identifier, identifier));
                        }
                    } catch (ProtocolException ex) {
                        Logger.Yadis.WarnFormat("HTTP GET error while retrieving described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex);
                    } catch (XmlException ex) {
                        Logger.Yadis.ErrorFormat("Error while parsing described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex);
                    }
                }
            }

            return(results);
        }
示例#49
0
 public static bool IsValid(string identifier)
 {
     return(XriIdentifier.IsValidXri(identifier) || UriIdentifier.IsValidUri(identifier));
 }
示例#50
0
        /// <summary>
        /// Creates the service endpoints described in this document, useful for requesting
        /// authentication of one of the OpenID Providers that result from it.
        /// </summary>
        /// <param name="xrds">The XrdsDocument instance to use in this process.</param>
        /// <param name="claimedIdentifier">The claimed identifier that was used to discover this XRDS document.</param>
        /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
        /// <returns>
        /// A sequence of OpenID Providers that can assert ownership of the <paramref name="claimedIdentifier"/>.
        /// </returns>
        internal static IEnumerable <IdentifierDiscoveryResult> CreateServiceEndpoints(this IEnumerable <XrdElement> xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier)
        {
            Contract.Requires <ArgumentNullException>(xrds != null);
            Contract.Requires <ArgumentNullException>(claimedIdentifier != null);
            Contract.Requires <ArgumentNullException>(userSuppliedIdentifier != null);
            Contract.Ensures(Contract.Result <IEnumerable <IdentifierDiscoveryResult> >() != null);

            var endpoints = new List <IdentifierDiscoveryResult>();

            endpoints.AddRange(xrds.GenerateOPIdentifierServiceEndpoints(userSuppliedIdentifier));
            endpoints.AddRange(xrds.GenerateClaimedIdentifierServiceEndpoints(claimedIdentifier, userSuppliedIdentifier));

            Logger.Yadis.DebugFormat("Total services discovered in XRDS: {0}", endpoints.Count);
            Logger.Yadis.Debug(endpoints.ToStringDeferred(true));
            return(endpoints);
        }
示例#51
0
 /// <summary>
 /// Generates the OpenID Providers that are capable of asserting ownership
 /// of a particular URI claimed identifier.
 /// </summary>
 /// <param name="xrds">The XrdsDocument instance to use in this process.</param>
 /// <param name="claimedIdentifier">The claimed identifier.</param>
 /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
 /// <returns>
 /// A sequence of the providers that can assert ownership of the given identifier.
 /// </returns>
 private static IEnumerable <ServiceEndpoint> GenerateClaimedIdentifierServiceEndpoints(this XrdsDocument xrds, UriIdentifier claimedIdentifier, UriIdentifier userSuppliedIdentifier)
 {
     return(from service in xrds.FindClaimedIdentifierServices()
            from uri in service.UriElements
            where uri.Uri != null
            let providerEndpoint = new ProviderEndpointDescription(uri.Uri, service.TypeElementUris)
                                   select ServiceEndpoint.CreateForClaimedIdentifier(claimedIdentifier, userSuppliedIdentifier, service.ProviderLocalIdentifier, providerEndpoint, service.Priority, uri.Priority));
 }
        /// <summary>
        /// Gets the host-meta for a given identifier.
        /// </summary>
        /// <param name="identifier">The identifier.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// The host-meta response, or <c>null</c> if no host-meta document could be obtained.
        /// </returns>
        private async Task <ResultWithSigningHost <HttpResponseMessage> > GetHostMetaAsync(UriIdentifier identifier, CancellationToken cancellationToken)
        {
            Requires.NotNull(identifier, "identifier");

            using (var httpClient = this.HostFactories.CreateHttpClient(identifier.IsDiscoverySecureEndToEnd, Yadis.IdentifierDiscoveryCachePolicy)) {
                foreach (var hostMetaProxy in this.GetHostMetaLocations(identifier))
                {
                    var hostMetaLocation = hostMetaProxy.GetProxy(identifier);
                    var response         = await httpClient.GetAsync(hostMetaLocation, cancellationToken);

                    try {
                        if (response.IsSuccessStatusCode)
                        {
                            Logger.Yadis.InfoFormat("Found host-meta for {0} at: {1}", identifier.Uri.Host, hostMetaLocation);
                            return(new ResultWithSigningHost <HttpResponseMessage>(response, hostMetaProxy.GetSigningHost(identifier)));
                        }
                        else
                        {
                            Logger.Yadis.InfoFormat("Could not obtain host-meta for {0} from {1}", identifier.Uri.Host, hostMetaLocation);
                            response.Dispose();
                        }
                    } catch {
                        response.Dispose();
                        throw;
                    }
                }
            }

            return(new ResultWithSigningHost <HttpResponseMessage>());
        }
		public void DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags() {
			var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false);
			string html = string.Format(
				@"
	<html><head>
		<meta http-equiv='X-XRDS-Location' content='{0}'/> <!-- this one will be insecure and ignored -->
		<link rel='openid2.provider' href='{1}' />
		<link rel='openid2.local_id' href='{2}' />
	</head><body></body></html>",
				HttpUtility.HtmlEncode(insecureXrdsSource),
				HttpUtility.HtmlEncode(OPUriSsl.AbsoluteUri),
				HttpUtility.HtmlEncode(OPLocalIdentifiersSsl[1].AbsoluteUri));
			this.MockResponder.RegisterMockResponse(VanityUriSsl, "text/html", html);

			Identifier userSuppliedIdentifier = new UriIdentifier(VanityUriSsl, true);

			// We verify that the XRDS was ignored and the LINK tags were used
			// because the XRDS OP-LocalIdentifier uses different local identifiers.
			Assert.AreEqual(OPLocalIdentifiersSsl[1].AbsoluteUri, this.Discover(userSuppliedIdentifier).Single().ProviderLocalIdentifier.ToString());
		}
		/// <summary>
		/// Gets the services for an identifier that are described by an external XRDS document.
		/// </summary>
		/// <param name="xrds">The XRD elements to search for described-by services.</param>
		/// <param name="identifier">The identifier under discovery.</param>
		/// <param name="requestHandler">The request handler.</param>
		/// <returns>The discovered services.</returns>
		private static IEnumerable<IdentifierDiscoveryResult> GetExternalServices(IEnumerable<XrdElement> xrds, UriIdentifier identifier, IDirectWebRequestHandler requestHandler) {
			Contract.Requires<ArgumentNullException>(xrds != null);
			Contract.Requires<ArgumentNullException>(identifier != null);
			Contract.Requires<ArgumentNullException>(requestHandler != null);
			Contract.Ensures(Contract.Result<IEnumerable<IdentifierDiscoveryResult>>() != null);

			var results = new List<IdentifierDiscoveryResult>();
			foreach (var serviceElement in GetDescribedByServices(xrds)) {
				var templateNode = serviceElement.Node.SelectSingleNode("google:URITemplate", serviceElement.XmlNamespaceResolver);
				var nextAuthorityNode = serviceElement.Node.SelectSingleNode("google:NextAuthority", serviceElement.XmlNamespaceResolver);
				if (templateNode != null) {
					Uri externalLocation = new Uri(templateNode.Value.Trim().Replace("{%uri}", Uri.EscapeDataString(identifier.Uri.AbsoluteUri)));
					string nextAuthority = nextAuthorityNode != null ? nextAuthorityNode.Value.Trim() : identifier.Uri.Host;
					try {
						using (var externalXrdsResponse = GetXrdsResponse(identifier, requestHandler, externalLocation)) {
							XrdsDocument externalXrds = new XrdsDocument(XmlReader.Create(externalXrdsResponse.ResponseStream));
							ValidateXmlDSig(externalXrds, identifier, externalXrdsResponse, nextAuthority);
							results.AddRange(GetXrdElements(externalXrds, identifier).CreateServiceEndpoints(identifier, identifier));
						}
					} catch (ProtocolException ex) {
						Logger.Yadis.WarnFormat("HTTP GET error while retrieving described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex);
					} catch (XmlException ex) {
						Logger.Yadis.ErrorFormat("Error while parsing described-by XRDS document {0}: {1}", externalLocation.AbsoluteUri, ex);
					}
				}
			}

			return results;
		}
示例#55
0
 /// <summary>
 /// Gets the absolute proxy URI.
 /// </summary>
 /// <param name="identifier">The identifier being discovered.</param>
 /// <returns>The an absolute URI.</returns>
 public virtual Uri GetProxy(UriIdentifier identifier)
 {
     Requires.NotNull(identifier, "identifier");
     return(new Uri(string.Format(CultureInfo.InvariantCulture, this.ProxyFormat, Uri.EscapeDataString(identifier.Uri.Host))));
 }
        /// <summary>
        /// Searches HTML for the HEAD META tags that describe OpenID provider services.
        /// </summary>
        /// <param name="claimedIdentifier">The final URL that provided this HTML document.
        /// This may not be the same as (this) userSuppliedIdentifier if the
        /// userSuppliedIdentifier pointed to a 301 Redirect.</param>
        /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
        /// <param name="html">The HTML that was downloaded and should be searched.</param>
        /// <returns>
        /// A sequence of any discovered ServiceEndpoints.
        /// </returns>
        private static IEnumerable <IdentifierDiscoveryResult> DiscoverFromHtml(Uri claimedIdentifier, UriIdentifier userSuppliedIdentifier, string html)
        {
            var linkTags = new List <HtmlLink>(HtmlParser.HeadTags <HtmlLink>(html));

            foreach (var protocol in Protocol.AllPracticalVersions)
            {
                // rel attributes are supposed to be interpreted with case INsensitivity,
                // and is a space-delimited list of values. (http://www.htmlhelp.com/reference/html40/values.html#linktypes)
                var serverLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryProviderKey) + @"\b", RegexOptions.IgnoreCase));
                if (serverLinkTag == null)
                {
                    continue;
                }

                Uri providerEndpoint = null;
                if (Uri.TryCreate(serverLinkTag.Href, UriKind.Absolute, out providerEndpoint))
                {
                    // See if a LocalId tag of the discovered version exists
                    Identifier providerLocalIdentifier = null;
                    var        delegateLinkTag         = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryLocalIdKey) + @"\b", RegexOptions.IgnoreCase));
                    if (delegateLinkTag != null)
                    {
                        if (Identifier.IsValid(delegateLinkTag.Href))
                        {
                            providerLocalIdentifier = delegateLinkTag.Href;
                        }
                        else
                        {
                            Logger.Yadis.WarnFormat("Skipping endpoint data because local id is badly formed ({0}).", delegateLinkTag.Href);
                            continue;                             // skip to next version
                        }
                    }

                    // Choose the TypeURI to match the OpenID version detected.
                    string[] typeURIs = { protocol.ClaimedIdentifierServiceTypeURI };
                    yield return(IdentifierDiscoveryResult.CreateForClaimedIdentifier(
                                     claimedIdentifier,
                                     userSuppliedIdentifier,
                                     providerLocalIdentifier,
                                     new ProviderEndpointDescription(providerEndpoint, typeURIs),
                                     (int?)null,
                                     (int?)null));
                }
            }
        }
示例#57
0
 /// <summary>
 /// Gets the signing host URI.
 /// </summary>
 /// <param name="identifier">The identifier being discovered.</param>
 /// <returns>A host name.</returns>
 public virtual string GetSigningHost(UriIdentifier identifier)
 {
     Requires.NotNull(identifier, "identifier");
     return(string.Format(CultureInfo.InvariantCulture, this.SigningHostFormat, identifier.Uri.Host, this.GetProxy(identifier).Host));
 }
示例#58
0
 public static bool IsValid(string identifier)
 {
     Requires.NotNullOrEmpty(identifier, "identifier");
     return(XriIdentifier.IsValidXri(identifier) || UriIdentifier.IsValidUri(identifier));
 }
 /// <summary>
 /// Gets the signing host URI.
 /// </summary>
 /// <param name="identifier">The identifier being discovered.</param>
 /// <returns>A host name.</returns>
 public virtual string GetSigningHost(UriIdentifier identifier)
 {
     Contract.Requires <ArgumentNullException>(identifier != null);
     return(string.Format(CultureInfo.InvariantCulture, this.SigningHostFormat, identifier.Uri.Host, this.GetProxy(identifier).Host));
 }
示例#60
0
		/// <summary>
		/// Converts a given identifier to its secure equivalent.
		/// UriIdentifiers originally created with an implied HTTP scheme change to HTTPS.
		/// Discovery is made to require SSL for the entire resolution process.
		/// </summary>
		/// <param name="secureIdentifier">The newly created secure identifier.
		/// If the conversion fails, <paramref name="secureIdentifier"/> retains
		/// <i>this</i> identifiers identity, but will never discover any endpoints.</param>
		/// <returns>
		/// True if the secure conversion was successful.
		/// False if the Identifier was originally created with an explicit HTTP scheme.
		/// </returns>
		internal override bool TryRequireSsl(out Identifier secureIdentifier) {
			// If this Identifier is already secure, reuse it.
			if (IsDiscoverySecureEndToEnd) {
				secureIdentifier = this;
				return true;
			}

			// If this identifier already uses SSL for initial discovery, return one
			// that guarantees it will be used throughout the discovery process.
			if (string.Equals(Uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) {
				secureIdentifier = new UriIdentifier(this.Uri, true);
				return true;
			}

			// Otherwise, try to make this Identifier secure by normalizing to HTTPS instead of HTTP.
			if (this.SchemeImplicitlyPrepended) {
				UriBuilder newIdentifierUri = new UriBuilder(this.Uri);
				newIdentifierUri.Scheme = Uri.UriSchemeHttps;
				if (newIdentifierUri.Port == 80) {
					newIdentifierUri.Port = 443;
				}
				secureIdentifier = new UriIdentifier(newIdentifierUri.Uri, true);
				return true;
			}

			// This identifier is explicitly NOT https, so we cannot change it.
			secureIdentifier = new NoDiscoveryIdentifier(this, true);
			return false;
		}