The LoginByToken sample creates a custom policy assertion that is derived from the .NET class SecurityPolicyAssertion. The assertion class gives the .NET framework access to the SAML token and the X509 certificate.

The sample performs the following operations to set up the security policy and message handling.

Sets the ServicePointManager properties to specify SSL3 and HTTP 100-Continue response handling. 100-Continue response handling supports more efficient communication between the client and vCenter server. When the client-side .NET framework sends a request to the server, it sends the request header and waits for a 100-Continue response from the server. After it receives that response, it sends the request body to the server.

Creates an X509Certificate2 object, specifies the certificate file, and imports the certificate. The certificate file specification indicates a PKCS #12 format file (Public-Key Cryptography Standards) – PfxCertificateFile. The file contains the client’s private key and public certificate. The PfxCertificateFile setting is defined in the app.config file in the LoginByToken project. The definition specifies the location of the file.

Creates a custom security assertion to store the SAML token and the certificate. The token and certificate will be included in the policy data for the LoginByToken request.

Defines a custom output filter that is derived from the .NET class SendSecurityFilter.

The following example shows the LoginByTokenSample class method GetSecurityPolicyAssertionForHokToken. The method returns a CustomSecurityAssertionHok instance which overrides the .NET class SecurityPolicyAssertion. The security assertion contains the SAML token and the X509 certificate token. This code is taken from the LoginByToken project file samples/LoginByToken/CustomSecurityAssertionHok.cs.

private SecurityPolicyAssertion GetSecurityPolicyAssertionForHokToken(XmlElement xmlToken)
{

    //When this property is set to true, client requests that use the POST method 
    //expect to receive a 100-Continue response from the server to indicate that 
    //the client should send the data to be posted. This mechanism allows clients 
    //to avoid sending large amounts of data over the network when the server, 
    //based on the request headers, intends to reject the request
    ServicePointManager.Expect100Continue = true;
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;

    X509Certificate2 certificateToBeAdded = new X509Certificate2();
    string certificateFile = ConfigurationManager.AppSettings["PfxCertificateFile"];
    string password = ConfigurationManager.AppSettings["PfxCertificateFilePassword"];
    certificateToBeAdded.Import(certificateFile,
                                password ?? string.Empty,
                                X509KeyStorageFlags.MachineKeySet);

    var customSecurityAssertion = new CustomSecurityAssertionHok();
    customSecurityAssertion.BinaryToken = xmlToken;
    customSecurityAssertion.TokenType = strSamlV2TokenType;
    customSecurityAssertion.SecurityToken = new X509SecurityToken(certificateToBeAdded);

    return customSecurityAssertion;
}

A vCenter Single Sign-On client provides a custom output filter for the custom security assertion. The custom filter provides three methods:

CustomSecurityClientOutputFilterHok class constructor – Creates token and message signature objects for the SOAP message.

SecureMessage—An override method for the .NET method SendSecurityFilter.SecureMessage. The override method adds the SAML token and message signature to the .NET Security element.

CreateKeyInfoSignatureElement – Creates an XML document that specifies the SAML token type and ID.

The following code example demonstrates how to create a custom output filter.

internal class CustomSecurityClientOutputFilterHok : SendSecurityFilter
{
   IssuedToken issuedToken = null;
   string samlAssertionId = null;
   MessageSignature messageSignature = null;

   /// Create a custom SOAP request filter.
   /// (Save the token and certificate.)
   public CustomSecurityClientOutputFilterHok(CustomSecurityAssertionHok parentAssertion)
       : base(parentAssertion.ServiceActor, true)
   {
      issuedToken = new IssuedToken(parentAssertion.BinaryToken, parentAssertion.TokenType);
      samlAssertionId = parentAssertion.BinaryToken.Attributes.GetNamedItem("ID").Value;
      messageSignature = new MessageSignature(parentAssertion.SecurityToken);
   }

   ///  Secure the SOAP message before its sent to the server.
   public override void SecureMessage(SoapEnvelope envelope, Security security)
   {
      //create KeyInfo XML element
      messageSignature.KeyInfo = new KeyInfo();
      messageSignature.KeyInfo.LoadXml(CreateKeyInfoSignatureElement());

      security.Tokens.Add(issuedToken);
      security.Elements.Add(messageSignature);
   }

   /// Helper method to create a custom key info signature element.
   /// Returns Key info XML element.
   private XmlElement CreateKeyInfoSignatureElement()
   {
      var xmlDocument = new XmlDocument();
      xmlDocument.LoadXml(@"<root><SecurityTokenReference 
xmlns=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"" 
xmlns:wsse=""http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"" 
wsse:TokenType=""http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"">
<KeyIdentifier 
xmlns=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"" 
ValueType=""http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID"">"
 + samlAssertionId +
@"</KeyIdentifier></SecurityTokenReference></root>");
      return xmlDocument.DocumentElement;
   }
}