Message ProcessClientKeyExchange(Message request)
        {
            // FIXME: use correct buffer size
            MessageBuffer buffer = request.CreateBufferedCopy(0x10000);
            WSTrustRequestSecurityTokenResponseReader reader =
                new WSTrustRequestSecurityTokenResponseReader(Constants.WstTlsnegoProofTokenType, buffer.CreateMessage().GetReaderAtBodyContents(), SecurityTokenSerializer, null);

            reader.Read();

            TlsServerSessionInfo tlsInfo;

            if (!sessions.TryGetValue(reader.Value.Context, out tlsInfo))
            {
                throw new SecurityNegotiationException(String.Format("The context '{0}' does not exist in this SSL negotiation manager", reader.Value.Context));
            }
            TlsServerSession tls = tlsInfo.Tls;

            AppendNegotiationMessageXml(buffer.CreateMessage().GetReaderAtBodyContents(), tlsInfo);
//Console.WriteLine (System.Text.Encoding.UTF8.GetString (tlsInfo.Messages.ToArray ()));

            tls.ProcessClientKeyExchange(reader.Value.BinaryExchange.Value);

            byte [] serverFinished = tls.ProcessServerFinished();

            // The shared key is computed as recommended in WS-Trust:
            // P_SHA1(encrypted_key,SHA1(exc14n(RST..RSTRs))+"CK-HASH")
            byte [] hash          = SHA1.Create().ComputeHash(tlsInfo.Messages.ToArray());
            byte [] key           = tls.CreateHash(tls.MasterSecret, hash, "CK-HASH");
            byte [] keyTlsApplied = tls.ProcessApplicationData(key);
            foreach (byte b in hash)
            {
                Console.Write("{0:X02} ", b);
            }
            Console.WriteLine();
            foreach (byte b in key)
            {
                Console.Write("{0:X02} ", b);
            }
            Console.WriteLine();

            WstRequestSecurityTokenResponseCollection col =
                new WstRequestSecurityTokenResponseCollection();
            WstRequestSecurityTokenResponse rstr =
                new WstRequestSecurityTokenResponse(SecurityTokenSerializer);

            rstr.Context   = reader.Value.Context;
            rstr.TokenType = Constants.WsscContextToken;
            DateTime from = DateTime.Now;
            // FIXME: not sure if arbitrary key is used here.
            SecurityContextSecurityToken sct = SecurityContextSecurityToken.CreateCookieSecurityContextToken(
                // Create a new context.
                // (do not use sslnego context here.)
                new UniqueId(),
                "uuid-" + Guid.NewGuid(),
                key,
                from,
                // FIXME: use LocalServiceSecuritySettings.NegotiationTimeout
                from.AddHours(8),
                null,
                owner.Manager.ServiceCredentials.SecureConversationAuthentication.SecurityStateEncoder);

            rstr.RequestedSecurityToken = sct;
            // without this ProcessApplicationData(), .NET seems
            // to fail recovering the key.
            rstr.RequestedProofToken          = keyTlsApplied;
            rstr.RequestedAttachedReference   = new LocalIdKeyIdentifierClause(sct.Id);
            rstr.RequestedUnattachedReference = new SecurityContextKeyIdentifierClause(sct.ContextId, null);
            WstLifetime lt = new WstLifetime();

            lt.Created                = from;
            lt.Expires                = from.Add(SecurityBindingElement.LocalServiceSettings.IssuedCookieLifetime);
            rstr.Lifetime             = lt;
            rstr.BinaryExchange       = new WstBinaryExchange(Constants.WstBinaryExchangeValueTls);
            rstr.BinaryExchange.Value = serverFinished;

            col.Responses.Add(rstr);

            // Authenticator is mandatory for MS sslnego.
            rstr               = new WstRequestSecurityTokenResponse(SecurityTokenSerializer);
            rstr.Context       = reader.Value.Context;
            rstr.Authenticator = tls.CreateHash(key, hash, "AUTH-HASH");
            col.Responses.Add(rstr);

            sessions.Remove(reader.Value.Context);

            // FIXME: get correct tokenRequestor address (probably identity authorized?)
            if (owner.IssuedSecurityTokenHandler != null)
            {
                owner.IssuedSecurityTokenHandler(sct, request.Headers.ReplyTo);
            }

            return(Message.CreateMessage(request.Version, Constants.WstIssueReplyAction, col));
        }
		Message ProcessClientKeyExchange (Message request)
		{
			// FIXME: use correct buffer size
			MessageBuffer buffer = request.CreateBufferedCopy (0x10000);
			WSTrustRequestSecurityTokenResponseReader reader =
				new WSTrustRequestSecurityTokenResponseReader (Constants.WstTlsnegoProofTokenType, buffer.CreateMessage ().GetReaderAtBodyContents (), SecurityTokenSerializer, null);
			reader.Read ();

			TlsServerSessionInfo tlsInfo;
			if (!sessions.TryGetValue (reader.Value.Context, out tlsInfo))
				throw new SecurityNegotiationException (String.Format ("The context '{0}' does not exist in this SSL negotiation manager", reader.Value.Context));
			TlsServerSession tls = tlsInfo.Tls;

			AppendNegotiationMessageXml (buffer.CreateMessage ().GetReaderAtBodyContents (), tlsInfo);
//Console.WriteLine (System.Text.Encoding.UTF8.GetString (tlsInfo.Messages.ToArray ()));

			tls.ProcessClientKeyExchange (reader.Value.BinaryExchange.Value);

			byte [] serverFinished = tls.ProcessServerFinished ();

			// The shared key is computed as recommended in WS-Trust:
			// P_SHA1(encrypted_key,SHA1(exc14n(RST..RSTRs))+"CK-HASH")
			byte [] hash = SHA1.Create ().ComputeHash (tlsInfo.Messages.ToArray ());
			byte [] key = tls.CreateHash (tls.MasterSecret, hash, "CK-HASH");
			byte [] keyTlsApplied = tls.ProcessApplicationData (key);
foreach (byte b in hash) Console.Write ("{0:X02} ", b); Console.WriteLine ();
foreach (byte b in key) Console.Write ("{0:X02} ", b); Console.WriteLine ();

			WstRequestSecurityTokenResponseCollection col =
				new WstRequestSecurityTokenResponseCollection ();
			WstRequestSecurityTokenResponse rstr =
				new WstRequestSecurityTokenResponse (SecurityTokenSerializer);
			rstr.Context = reader.Value.Context;
			rstr.TokenType = Constants.WsscContextToken;
			DateTime from = DateTime.Now;
			// FIXME: not sure if arbitrary key is used here.
			SecurityContextSecurityToken sct = SecurityContextSecurityToken.CreateCookieSecurityContextToken (
				// Create a new context.
				// (do not use sslnego context here.)
				new UniqueId (),
				"uuid-" + Guid.NewGuid (),
				key,
				from,
				// FIXME: use LocalServiceSecuritySettings.NegotiationTimeout
				from.AddHours (8),
				null,
				owner.Manager.ServiceCredentials.SecureConversationAuthentication.SecurityStateEncoder);
			rstr.RequestedSecurityToken = sct;
			// without this ProcessApplicationData(), .NET seems
			// to fail recovering the key.
			rstr.RequestedProofToken = keyTlsApplied;
			rstr.RequestedAttachedReference = new LocalIdKeyIdentifierClause (sct.Id);
			rstr.RequestedUnattachedReference = new SecurityContextKeyIdentifierClause (sct.ContextId, null);
			WstLifetime lt = new WstLifetime ();
			lt.Created = from;
			lt.Expires = from.Add (SecurityBindingElement.LocalServiceSettings.IssuedCookieLifetime);
			rstr.Lifetime = lt;
			rstr.BinaryExchange = new WstBinaryExchange (Constants.WstBinaryExchangeValueTls);
			rstr.BinaryExchange.Value = serverFinished;

			col.Responses.Add (rstr);

			// Authenticator is mandatory for MS sslnego.
			rstr = new WstRequestSecurityTokenResponse (SecurityTokenSerializer);
			rstr.Context = reader.Value.Context;
			rstr.Authenticator = tls.CreateHash (key, hash, "AUTH-HASH");
			col.Responses.Add (rstr);

			sessions.Remove (reader.Value.Context);

			// FIXME: get correct tokenRequestor address (probably identity authorized?)
			if (owner.IssuedSecurityTokenHandler != null)
				owner.IssuedSecurityTokenHandler (sct, request.Headers.ReplyTo);

			return Message.CreateMessage (request.Version, Constants.WstIssueReplyAction, col);
		}
		void ReadLifetime ()
		{
			WstLifetime lt = new WstLifetime ();
			res.Lifetime = lt;
			if (reader.IsEmptyElement)
				throw new XmlException (String.Format ("WS-Trust 'Lifetime' element is expected to have contents. {0}", LineInfo ()));
			reader.Read ();
			while (true) {
				reader.MoveToContent ();
				if (reader.NodeType != XmlNodeType.Element)
					break;
				if (reader.NamespaceURI == Constants.WsuNamespace) {
					switch (reader.LocalName) {
					case "Created":
						lt.Created = XmlConvert.ToDateTime (reader.ReadElementContentAsString (), XmlDateTimeSerializationMode.RoundtripKind);
						continue;
					case "Expires":
						lt.Expires = XmlConvert.ToDateTime (reader.ReadElementContentAsString (), XmlDateTimeSerializationMode.RoundtripKind);
						continue;
					}
				}
				throw new XmlException (String.Format ("Unexpected Lifetime content. Name is {0} and namespace URI is {1} {2}", reader.Name, reader.NamespaceURI, LineInfo ()));
			}
			reader.ReadEndElement ();
		}