Monday, December 7, 2009

Another Brick in the Wall

Moments (ok, hours) ago, I posted about my difficulties with WCF authentication and identity propagation. I've now made some progress on the message-level security front. However this progress only removed some of the bricks I've hit in the WCF brick wall.

I've learned now that makecert.exe needs to be run with full administrative privileges (not just a Windows SDK command prompt) when running Vista or Windows 7 (I'm on the latter). It can make certificates all day long, but when it comes to saving them to the local certificate stores, it will fail. By running an administrative command prompt (in my case, I can the Windows SDK command prompt as Administrator), makecert.exe can successfully write certificates to the local store, e.g.:

makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=LocalDevServerCert -sky exchange -pe


Doing this, I now have my externally-accessible outer WCF service communicating to my internal WCF service. The inner-serviced is using wsHttpBinding, a custom UserNamePasswordValidator and the now-installed custom certificate:


<wsHttpBinding>
<binding name="customServiceToFacadeBinding">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
...
<serviceBehaviors>
<behavior name="...">
<serviceCredentials>
<userNameAuthentication
userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="..., ..."
/>
<serviceCertificate findValue="LocalDevServerCert" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
<serviceBehavior>


Likewise, in the outer-service's endpoint configuration to the inner-service, I'm using related configuration:


<wsHttpBinding>
<binding name="customServiceToFacadeBinding">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
...
<client>
<endpoint name="WSHttpBinding_AttorneyFacade"
address="..."
binding="wsHttpBinding"
bindingConfiguration="customServiceToFacadeBinding"
contract="..."
behaviorConfiguration="ClientCredentialsBehavior">
<identity>
<!-- Usually, this is 'localhost', but in cert mode, it needs to match the subject(?) of the certificate -->
<dns value="LocalDevServerCert" />
</identity>
</endpoint>
</client>


In this way, I can now explicitly set the ClientCredentials.UserName.UserName in the outer-service's WCF client and invoke the inner-service's operation and the identity flows through.

But wait, there's more!

I'm still stuck where Silverlight calls the outer-service. I'm limited to the customBinding where I specify a transport (e.g. httpTransport or httpsTransport) or basicHttpBinding (i.e. HTTP, HTTPS). Either way, if I attempt to use transport-level security, such as UserNameOverTransport on customBinding or TransportWithMessageCredential on basicHttpBinding, I'm left with errors indicating that WCF won't send credentials over a non-secure transport -- that is, HTTP. Again, HTTPS is not supported by Cassini, and I can't get Visual Studio to work with IIS.

Next I'm going to investigate using Certificates for message-level protection between the Silverlight client and the outer-service. In production, I probably will use transport-level security via SSL on IIS; however, for local development, I could accept certificate-based message-level protection.

Still, why can't Cassini just support SSL? Or, why can't WCF allow credentials to be sent over an unsecured transport when bound to localhost? Either solution would make developers' lives easier!

No comments: