Tuesday, August 17, 2010

Silverlight error message: Invalid or malformed application: Check manifest

I'm a perfectionist. I like nice namespaces. I recently created a new Silverlight project and it worked well -- until I "fixed" the namespaces. I got this message:

Invalid or malformed application: Check manifest

Checking the manifest in the XAP file revealed that my EntryPointType had not been updated to reflect the new namespaces. The Silverlight project setttings were holding on to the old name. The simple fix is on the Silverlight tab, Startup object. That combo box still has the old name.

Silverlight, WCF, IIS, HTTPS, BasicHttpBinding, and UserNamePasswordValidator

Creating a WCF service and Silverlight client that work together seems like a fairly common requirement. You would probably also require username/password and HTTPS. And it would be best if your development bindings were similar to deployment bindings. Here is what I used:
  • Visual Studio 2010, .NET 4.0
  • Windows 7
  • IIS 7
Note that you cannot use the built-in ASP.NET Development Server. It does not support HTTPS. And you cannot use Message credentials without HTTPS. Here is how I did it:
  • Make sure you have IIS and the "IIS Metabase and IIS6 configuration compatibility" installed.
  • Run Visual Studio as an Administrator so that you can access IIS configuration features from within Visual Studio.
  • Create a new WCF Service Application.
    • Change the debug options on the Project Properties, Web tab to Use Local IIS Web Server.
    • Press the Create a Virtual Directory button.
    • Test that your service provides metadata. (http://localhost/WcfService1/Service1.svc?wsdl)
    • Optional: Enable directory browsing on your virtual directory.
  • Setup IIS, IE, and Visual Studio for HTTPS.
    • In Internet Information Server (IIS) Manager, select the local computer and the "Server Certificates."
    • Create a Self-Signed Certificate.
    • On the web site (not the virtual directory), add a binding for HTTPS and test that it works. (https://localhost/WcfService1/Service1.svc)
    • In Internet Explorer, install the self-signed certificate, placing the certificate in the Trusted Root Certification Authorities.
    • Change the WCF project's Project URL to use https and the machine name, instead of http and the localhost. (https://mishael/WcfService1)
    • Test that Visual Studio can launch the web service view in Internet Explorer without a certificate error.
  • Create a new Silverlight Application.
    • Add a Service Reference to the WCF service.
    • Add a button that calls the WCF service.

      int counter = 0;
      private void button1_Click(object sender, RoutedEventArgs e)
      {
          var c = new Service1Client();
          c.GetDataCompleted += c_GetDataCompleted;
          c.GetDataAsync(++counter);
      }
      
      void c_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
      {
          if (e.Error != null)
              textBlock1.Text = e.Error.Message;
          else
              textBlock1.Text = e.Result;
      }
    • Edit the generated ServiceReferences.ClientConfig to use https instead of http.
    • Edit the generated ServiceReferences.ClientConfig to use security mode of Transport.

      security mode="Transport"
  • Using the WCF Service Configuration Tool, update the WCF service as follows:
    • Create a new service, with the endpoint address pointing to the services.
    • Create a New Binding Configuration for basicHttpBinding. On the Security tab, change the mode to Transport.
    • Use the new binding configuration for the service endpoint. It will be something like this:

      
        
          
            
          
        
      
      
        
          
        
      
    • Testing the service endpoint with Internet Explorer (https://mishael/WcfService1/Service1.svc) reveals that there is an issue with multipleSiteBindingsEnabled. Comment out the multipleSiteBindingsEnabled attribute in the web.config.
  • Test that the Silverlight app can successfully communicate with the WCF service using HTTPS, including stopping at breakpoints in both the Silverlight client and WCF service.
  • Enable client username and password authentication as follows:
    • In the Web.config, switch the security from "Transport" to "TransportWithMessageCredential". and a client message credential type of "UserName".


      
         
      
    • In the ServiceReferences.ClientConfig, switch the security from "Transport" to "TransportWithMessageCredential".
    • Define a UserNamePasswordValidator class to do the username and password validation. (The base class UserNamePasswordValidator is in System.IdentifyModel.dll.)

      namespace WcfService1
      {
          public class MyUserNamePasswordValidator: UserNamePasswordValidator
          {
              public override void Validate(string userName, string password)
              {
                  if (userName == "Wally" && password == "password")
                      return;
                  throw new SecurityTokenValidationException("Unrecognized user.");
              }
          }
      }
    • Update the service behavior to use the new UserNamePasswordValidator class.

      
        
          
            
          
        
      
    • Update the Silverlight client code set a username and password.

      var c = new Service1Client();
      c.ClientCredentials.UserName.UserName = "Wally";
      c.ClientCredentials.UserName.Password = "password";
      
    • Using breakpoints, verify that the UserNamePasswordValidator class is being called.
  • In your WCF operation, throw a FaultException. Testing will reveal that on the client, all you get is a CommunicationException. Improve the availability of exception messages sent to the client as follows:
    • Update the Web.config to includeExceptionDetailInFaults.
      serviceDebug includeExceptionDetailInFaults="true"
    • Create a BehaviorExtensionElement class using the code here.
    • Update the Web.config to include this behavior extension.
      
        
          
        
      
      
    • Update the Web.config to add the extension to the endpoint behavior.
      
              
                
              
            
      
    • Update the Web.config to use this behavior for your endpoint.
      behaviorConfiguration="SilverlightFaultBehavior"

    Monday, August 16, 2010

    Setting up HTTPS for local development without installing IIS

    As the subject implies, I wanted to do some web development that used HTTPS. The WebDev.WebServer (aka Cassini) that comes with Visual Studio does not support HTTPS. I did not want to require IIS for myself or my colleagues.

    I'm not entirely sure of all the details, but this seemed to work:

    • Create a test certificate as described here. You must run as an Administrator.

      > makecert -ss My -sr LocalMachine -sky exchange -r -n "CN=Wallace Kelly" -sk WallaceKelly -pe
      
    • Get the new certificate's "thumbnail" as described here. I used the MMC snap in.
    • Configure a port with the SSL certificate, as described here. The special IP address 0.0.0.0 matches any IP address for the local machine. The certhash is the "thumbnail" without the spaces. The appid is a GUID. I used the GUID for the WebServer2, from the AssemblyInfo.cs. I'm not sure if this can be any GUID.

      > netsh http add sslcert ipport=0.0.0.0:8080 certhash=4dbcbe2656f62af17ceba1f760
      ba7d065fd919bf appid={D7A0778F-A61C-463F-8C39-893DF0AA3748}
      
      SSL Certificate successfully added
      
      
      > netsh http show sslcert
      
      SSL Certificate bindings:
      -------------------------
      
          IP:port                 : 0.0.0.0:8080
          Certificate Hash        : 4dbcbe2656f62af17ceba1f760ba7d065fd919bf
          Application ID          : {d7a0778f-a61c-463f-8c39-893df0aa3748}
          Certificate Store Name  : (null)
          Verify Client Certificate Revocation    : Enabled
          Verify Revocation Using Cached Client Certificate Only    : Disabled
          Usage Check    : Enabled
          Revocation Freshness Time : 0
          URL Retrieval Timeout   : 0
          Ctl Identifier          : (null)
          Ctl Store Name          : (null)
          DS Mapper Usage    : Disabled
          Negotiate Client Certificate    : Disabled
      
      
      
      
    • Use WebDev.WebServer2, found here.

      > WebDev.WebServer2.exe /path:"c:\temp" /prefix:"http://localhost" /port:8080