La sécurisation du service WCF fut une partie très intéressante à implémenter du fait que je souhaitais réellement faire quelque chose de personnalisé. Dans un premier temps, il s'est agit de mettre en place HTTPS sur ma machine. Pour cela, j'ai simplement utilisé la possibilité de faire générer un certificat SSL sur ma machine et de l'associer ensuite à l'application Web hébergeant mon service WCF. Je me suis donc servi de la Management Console de IIS afin de générer un certificat « auto-signé » à des fins de test. Une fois ce certificat créé, il m'a suffit d'accéder au site Web par défaut pour y associer ce certificat SSL.

Association au site Web par défaut
Enfin, mon application Web étant un répertoire virtuel du site par défaut, j'ai ensuite indiqué à IIS de lui appliquer ce certificat.

Application du certificat au répertoire virtuel
Bien évidemment, ce certificat n'étant pas vérifié par une autorité compétente, un avertissement sera affiché par les navigateurs devant s'y connecter, ce qui impliquera une gestion particulière lors de l'accès au service WCF par la suite via le proxy qui sera généré.

Erreur de certificat

Erreur de certificat
Il restera ensuite à autoriser « HttpsGetEnable » au niveau des métadonnées du service afin de rendre l'accès possible au WSDL via HTTPS.
Pour plus d'information là-dessus, je vous invite à consulter l'article
suivant.
L'étape suivante a consisté à définir une authentification personnalisée au niveau du service. Pour cela, il suffit de créer une classe héritant de la classe abstraite « UserNamePasswordValidator » et d'implémenter la méthode « Validate ».
public class MyUserNameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if ( userName == null || password == null )
throw new FaultException("Please provide non empty login and password!");
if (!(userName == "fcolin" && password == "bewise"))
throw new FaultException("Unauthorized access!");
}
}
Il reste maintenant à indiquer au pipeline d'exécution la nouvelle manière de valider l'identifiant et le mot de passe transmis pour authentification. Pour cela, nous procéderons via le fichier de configuration du site en modifiant le behavior associé au service via l'attribut « behaviorConfiguration ». Via, l'attribut « customUserNamePasswordValidatorType » nous pouvons spécifier la classe et l'assembly qui seront chargées de tout cela.
<services>
<service behaviorConfiguration="NewBehavior" name="Sample.Upload.Services.Upload">
<endpoint binding="basicHttpBinding"
bindingConfiguration="NewBinding1"
contract="Sample.Upload.ServiceContracts.IUpload" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="NewBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<userNameAuthentication
userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="Sample.Upload.Services.MyUserNameValidator, Sample.Upload.Services"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
J'indique ensuite le type de sécurité souhaité (Message, transport, etc.) au niveau de la configuration du binding :
<security mode="TransportWithMessageCredential" >
<transport clientCredentialType="Basic" />
</security>
« TransportWithMessageCredential » signifie que la sécurisation du message est réalisée par la couche transport (HTTPS) et que les Credentials sont transmis au sein du message en lui-même.
La dernière partie consiste à ce que le client transmette ces informations sur HTTPS. Pour cela, il suffit :
- De changer l'adresse dans le fichier de configuration pour passer en HTTPS dans le EndPoint
- Dans le proxy généré avec Visual Studio 2008 (« Add Service Reference »), il existe une propriété pour cela (la variable « client » étant une instance du proxy généré).
client.ClientCredentials.UserName.UserName = textBox1.Text;
client.ClientCredentials.UserName.Password = textBox2.Text;
Si je n'étais pas passé par ce proxy généré, j'aurais simplement utilisé la propriété « ClientCredentials.UserName » de l'instance du ChannelFactory<Tchannel> utilisé en lieu et place.
Du fait que le certificat SSL utilisé du côté du service est auto-généré, cela pose bien évidemment un souci au niveau du proxy en déclenchant une exception. Pour résoudre cette problématique, il suffit d'utiliser une propriété de la classe statique « ServicePointManager » qui gère la création et la destruction d'instance de ServicePoint. Cette dernière permet de spécifier le comportement de validation du client dès lors qu'il reçoit le certificat du serveur.
System.Net.ServicePointManager.ServerCertificateValidationCallback
= (senderObject, certificate, chain, sslPolicyErrors) => true;
Vous noterez l'utilisation d'une expression lambda afin de spécifier le comportement de validation pour faire en sorte qu'il soit toujours valide.