/// <summary>
        /// Ensures that all message parameters that must be signed are in fact included
        /// in the signature.
        /// </summary>
        /// <param name="signedMessage">The signed message.</param>
        private static void EnsureParametersRequiringSignatureAreSigned(ITamperResistantOpenIdMessage signedMessage)
        {
            // Verify that the signed parameter order includes the mandated fields.
            // We do this in such a way that derived classes that add mandated fields automatically
            // get included in the list of checked parameters.
            Protocol protocol = Protocol.Lookup(signedMessage.Version);
            var      partsRequiringProtection = from part in MessageDescription.Get(signedMessage.GetType(), signedMessage.Version).Mapping.Values
                                                where part.RequiredProtection != ProtectionLevel.None
                                                select part.Name;

            ErrorUtilities.VerifyInternal(partsRequiringProtection.All(name => name.StartsWith(protocol.openid.Prefix, StringComparison.Ordinal)), "Signing only works when the parameters start with the 'openid.' prefix.");
            string[] signedParts   = signedMessage.SignedParameterOrder.Split(',');
            var      unsignedParts = from partName in partsRequiringProtection
                                     where !signedParts.Contains(partName.Substring(protocol.openid.Prefix.Length))
                                     select partName;

            ErrorUtilities.VerifyProtocol(!unsignedParts.Any(), OpenIdStrings.SignatureDoesNotIncludeMandatoryParts, string.Join(", ", unsignedParts.ToArray()));
        }
        /// <summary>
        /// Gets the value to use for the openid.signed parameter.
        /// </summary>
        /// <param name="signedMessage">The signable message.</param>
        /// <returns>
        /// A comma-delimited list of parameter names, omitting the 'openid.' prefix, that determines
        /// the inclusion and order of message parts that will be signed.
        /// </returns>
        private string GetSignedParameterOrder(ITamperResistantOpenIdMessage signedMessage)
        {
            ErrorUtilities.VerifyArgumentNotNull(signedMessage, "signedMessage");

            Protocol protocol = Protocol.Lookup(signedMessage.Version);

            MessageDescription description = MessageDescription.Get(signedMessage.GetType(), signedMessage.Version);
            var signedParts = from part in description.Mapping.Values
                              where (part.RequiredProtection & System.Net.Security.ProtectionLevel.Sign) != 0 &&
                              part.GetValue(signedMessage) != null
                              select part.Name;
            string prefix = Protocol.V20.openid.Prefix;

            ErrorUtilities.VerifyInternal(signedParts.All(name => name.StartsWith(prefix, StringComparison.Ordinal)), "All signed message parts must start with 'openid.'.");

            if (this.opSecuritySettings.SignOutgoingExtensions)
            {
                // Tack on any ExtraData parameters that start with 'openid.'.
                List <string> extraSignedParameters = new List <string>(signedMessage.ExtraData.Count);
                foreach (string key in signedMessage.ExtraData.Keys)
                {
                    if (key.StartsWith(protocol.openid.Prefix, StringComparison.Ordinal))
                    {
                        extraSignedParameters.Add(key);
                    }
                    else
                    {
                        Logger.DebugFormat("The extra parameter '{0}' will not be signed because it does not start with 'openid.'.", key);
                    }
                }
                signedParts = signedParts.Concat(extraSignedParameters);
            }

            int    skipLength   = prefix.Length;
            string signedFields = string.Join(",", signedParts.Select(name => name.Substring(skipLength)).ToArray());

            return(signedFields);
        }