2017-04-07 6 views
0

Je développe un pilote ONVIF utilisant .NET 4 (Windows Forms, pas WCF). J'ai commencé à importer des fichiers WSDL en tant que service dans Visual Studio. Je suis en mesure d'envoyer la commande à un dispositif de cette manière:ONVIF service wsdl: impossible d'authentifier

HttpTransportBindingElement httpTransportBindingElement = new HttpTransportBindingElement(); 
[...] 

TextMessageEncodingBindingElement messegeElement = new TextMessageEncodingBindingElement(); 
[...] 
CustomBinding binding = new CustomBinding(messegeElement, httpTransportBindingElement); 
[...] 

EndpointAddress serviceAddress = new EndpointAddress(url); 

DeviceClient deviceClient = new DeviceClient(binding, serviceAddress); 

Device channel = deviceClient.ChannelFactory.CreateChannel(); 

DeviceServiceCapabilities dsc = channel.GetServiceCapabilities(); 

Mais je ne suis pas en mesure de gérer l'authentification HTTP Digest. J'ai passé des jours à chercher des exemples et des solutions Google, mais le seul moyen semble être d'écrire du code XML à la main. Il n'y a pas de solution propre comme:

deviceClient.ChannelFactory.Credentials.HttpDigest.ClientCredential.UserName = USERNAME; 
deviceClient.ChannelFactory.Credentials.HttpDigest.ClientCredential.Password = digestPassword; 

(cela ne fonctionne pas)?

Répondre

0

Pour les futurs lecteurs, enfin j'ai pu effectuer deux types d'authentification sans utiliser WSE 3.0. Ceci est du code partiel (pour la brièveté), basée sur l'interface IClientMessageInspector (vous pouvez trouver beaucoup d'autres exemples basés sur cette interface):

public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
    { 
     if (HTTPDigestAuthentication) 
     { 
      string digestHeader = string.Format("Digest username=\"{0}\",realm=\"{1}\",nonce=\"{2}\",uri=\"{3}\"," + 
               "cnonce=\"{4}\",nc={5:00000000},qop={6},response=\"{7}\",opaque=\"{8}\"", 
               _username, realm, nonce, new Uri(this.URI).AbsolutePath, cnonce, counter, qop, digestResponse, opaque); 

      HttpRequestMessageProperty httpRequest = new HttpRequestMessageProperty(); 
      httpRequest.Headers.Add("Authorization", digestHeader); 
      request.Properties.Add(HttpRequestMessageProperty.Name, httpRequest); 

      return Convert.DBNull; 
     } 
     else if (UsernametokenAuthorization) 
     { 
      string headerText = "<wsse:UsernameToken xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" + 
           "<wsse:Username>" + _username + "</wsse:Username>" + 
           "<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest\">" + digestPassword + "</wsse:Password>" + 
           "<wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">" + Convert.ToBase64String(nonce) + "</wsse:Nonce>" + 
           "<wsu:Created xmlns=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" + created + "</wsu:Created>" + 
           "</wsse:UsernameToken>"; 

      XmlDocument MyDoc = new XmlDocument(); 
      MyDoc.LoadXml(headerText); 

      MessageHeader myHeader = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", MyDoc.DocumentElement, false); 

      request.Headers.Add(myHeader); 

      return Convert.DBNull; 
     } 

     return request; 
    } 
+0

Il y a beaucoup de choses qui manquent dans votre exemple, et des choses très importantes ... Comment obtenez-vous les variables utilisées pour formater votre en-tête digest? (par exemple nonce? avez-vous besoin de faire une demande pour cela?) – cube45

+0

Oui, vous avez raison: vous devez envoyer une requête sans authentification, le serveur répondra avec un message d'erreur contenant nonce, royaume et ainsi de suite. – ElmoDev001

+0

Est-ce que vous créez votre propre HttpClient au début de BeforeSendRequest? Pourriez-vous éditer votre réponse et fournir un exemple plus complet? – cube45

1

Tout d'abord, vous devez installer le package Microsoft.Web.Services3. (Voir> Autres fenêtres> Console du gestionnaire de paquets). Ensuite, vous devez ajouter un comportement de résumé à votre point de terminaison. La première partie du code est la classe PasswordDigestBehavior et ensuite, elle est utilisée pour la connexion à un service de périphérique ONVIF.

public class PasswordDigestBehavior : IEndpointBehavior 
{ 
    public String Username { get; set; } 
    public String Password { get; set; } 

    public PasswordDigestBehavior(String username, String password) 
    { 
     this.Username = username; 
     this.Password = password; 
    } 


    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) 
    { 
     // do nothing 
    } 

    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) 
    { 
     //clientRuntime.MessageInspectors.Add(new PasswordDigestMessageInspector(this.Username, this.Password)); 
     clientRuntime.MessageInspectors.Add(new PasswordDigestMessageInspector(this.Username, this.Password)); 
    } 

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher) 
    { 
     throw new NotImplementedException(); 
    } 

    public void Validate(ServiceEndpoint endpoint) 
    { 
     // do nothing... 
    } 
} 


public class PasswordDigestMessageInspector : IClientMessageInspector 
{ 
    public String Username { get; set; } 
    public String Password { get; set; } 

    public PasswordDigestMessageInspector(String username, String password) 
    { 
     this.Username = username; 
     this.Password = password; 
    } 

    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) 
    { 
     // do nothing 
    } 

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
    { 
     // Use the WSE 3.0 security token class 
     var option = PasswordOption.SendHashed; 
     if (string.IsNullOrEmpty(Username) || string.IsNullOrEmpty(Password)) 
      option = PasswordOption.SendPlainText; 

     UsernameToken token = new UsernameToken(this.Username, this.Password, option); 

     // Serialize the token to XML 
     XmlDocument xmlDoc = new XmlDocument(); 
     XmlElement securityToken = token.GetXml(xmlDoc); 

     // find nonce and add EncodingType attribute for BSP compliance 
     XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDoc.NameTable); 
     nsMgr.AddNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); 
     XmlNodeList nonces = securityToken.SelectNodes("//wsse:Nonce", nsMgr); 
     XmlAttribute encodingAttr = xmlDoc.CreateAttribute("EncodingType"); 
     encodingAttr.Value = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"; 
     if (nonces.Count > 0) 
     { 
      nonces[0].Attributes.Append(encodingAttr); 
      //nonces[0].Attributes[0].Value = "foo"; 
     } 


     // 
     MessageHeader securityHeader = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", securityToken, false); 
     request.Headers.Add(securityHeader); 

     // complete 
     return Convert.DBNull; 
    } 
} 

Et voici comment l'utiliser:

var endPointAddress = new EndpointAddress("http://DEVICE_IPADDRESS/onvif/device_service"); 
      var httpTransportBinding = new HttpTransportBindingElement { AuthenticationScheme = AuthenticationSchemes.Digest }; 
      var textMessageEncodingBinding = new TextMessageEncodingBindingElement { MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None) }; 
      var customBinding = new CustomBinding(textMessageEncodingBinding, httpTransportBinding); 
      var passwordDigestBehavior = new PasswordDigestBehavior(USERNAME, PASSWORD); 
      var deviceService = new DeviceClient(customBinding, endPointAddress); 
      deviceService.Endpoint.Behaviors.Add(passwordDigestBehavior); 
+0

Votre solution fonctionne, mais effectue l'authentification SOAP et non HTTP Digest . Quoi qu'il en soit finalement, j'étais capable d'effectuer les deux types d'authentification sans utiliser WSE 3.0. – ElmoDev001