Example #1
0
 def __init__(self, url, timeout=1):
     """
     used url argument to connect to server.
     if you are unsure of url, write at least hostname and port
     and call get_endpoints
     timeout is the timeout to get an answer for requests to server
     """
     self.logger = logging.getLogger(__name__)
     self.server_url = urlparse(url)
     self.name = "Pure Python Client"
     self.description = self.name
     self.application_uri = "urn:freeopcua:client"
     self.product_uri = "urn:freeopcua.github.no:client"
     self.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#None"
     self.security_mode = ua.MessageSecurityMode.None_
     self.secure_channel_id = None
     self.default_timeout = 3600000
     self.secure_channel_timeout = self.default_timeout
     self.session_timeout = self.default_timeout
     self.policy_ids = {
         ua.UserTokenType.Anonymous: b'anonymous',
         ua.UserTokenType.UserName: b'user_name',
     }
     self.server_certificate = None
     self.bclient = BinaryClient(timeout)
     self._nonce = None
     self._session_counter = 1
     self.keepalive = None
Example #2
0
 def __init__(self, url, timeout=1):
     """
     used url argument to connect to server.
     if you are unsure of url, write at least hostname and port
     and call get_endpoints
     timeout is the timeout to get an answer for requests to server
     """
     self.logger = logging.getLogger(__name__)
     self.server_url = urlparse(url)
     self.name = "Pure Python Client"
     self.description = self.name
     self.application_uri = "urn:freeopcua:client"
     self.product_uri = "urn:freeopcua.github.no:client"
     self.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#None"
     self.security_mode = ua.MessageSecurityMode.None_
     self.secure_channel_id = None
     self.default_timeout = 3600000
     self.secure_channel_timeout = self.default_timeout
     self.session_timeout = self.default_timeout
     self.policy_ids = {ua.UserTokenType.Anonymous: b"anonymous", ua.UserTokenType.UserName: b"user_name"}
     self.server_certificate = None
     self.bclient = BinaryClient(timeout)
     self._nonce = None
     self._session_counter = 1
     self.keepalive = None
Example #3
0
 def __init__(self, url):
     """
     used url argument to connect to server.
     if you are unsure of url, write at least hostname and port
     and call get_endpoints
     """
     self.logger = logging.getLogger(__name__)
     self.server_url = urlparse(url)
     self.name = "Pure Python Client"
     self.description = self.name
     self.application_uri = "urn:freeopcua:client"
     self.product_uri = "urn:freeopcua.github.no:client"
     self.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#None"
     self.secure_channel_id = None
     self.default_timeout = 3600000
     self.secure_channel_timeout = self.default_timeout
     self.session_timeout = self.default_timeout
     self.bclient = BinaryClient()
     self._nonce = None
     self._session_counter = 1
     self.keepalive = None
Example #4
0
 def __init__(self, url):
     """
     used url argument to connect to server.
     if you are unsure of url, write at least hostname and port
     and call get_endpoints
     """
     self.logger = logging.getLogger(__name__)
     self.server_url = urlparse(url)
     self.name = "Pure Python Client"
     self.description = self.name
     self.application_uri = "urn:freeopcua:client"
     self.product_uri = "urn:freeopcua.github.no:client"
     self.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#None"
     self.secure_channel_id = None
     self.default_timeout = 3600000
     self.secure_channel_timeout = self.default_timeout
     self.session_timeout = self.default_timeout
     self.bclient = BinaryClient()
     self._nonce = None
     self._session_counter = 1
     self.keepalive = None
Example #5
0
class Client(object):

    """
    High level client to connect to an OPC-UA server.
    This class makes it easy to connect and browse address space.
    It attemps to expose as much functionality as possible
    but if you want to do to special things you will probably need
    to work with the BinaryClient object, available as self.bclient
    which offers a raw OPC-UA interface.

    """

    def __init__(self, url, timeout=1):
        """
        used url argument to connect to server.
        if you are unsure of url, write at least hostname and port
        and call get_endpoints
        timeout is the timeout to get an answer for requests to server
        """
        self.logger = logging.getLogger(__name__)
        self.server_url = urlparse(url)
        self.name = "Pure Python Client"
        self.description = self.name
        self.application_uri = "urn:freeopcua:client"
        self.product_uri = "urn:freeopcua.github.no:client"
        self.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#None"
        self.security_mode = ua.MessageSecurityMode.None_
        self.secure_channel_id = None
        self.default_timeout = 3600000
        self.secure_channel_timeout = self.default_timeout
        self.session_timeout = self.default_timeout
        self.policy_ids = {ua.UserTokenType.Anonymous: b"anonymous", ua.UserTokenType.UserName: b"user_name"}
        self.server_certificate = None
        self.bclient = BinaryClient(timeout)
        self._nonce = None
        self._session_counter = 1
        self.keepalive = None

    def get_server_endpoints(self):
        """
        Connect, ask server for endpoints, and disconnect
        """
        self.connect_socket()
        self.send_hello()
        self.open_secure_channel()
        endpoints = self.get_endpoints()
        self.close_secure_channel()
        return endpoints

    def connect(self):
        """
        High level method
        Connect, create and activate session
        """
        self.connect_socket()
        self.send_hello()
        self.open_secure_channel()
        self.create_session()
        self.activate_session()

    def disconnect(self):
        """
        High level method
        Close session, secure channel and socket
        """
        self.close_session()
        self.close_secure_channel()
        self.disconnect_socket()

    def connect_socket(self):
        """
        connect to socket defined in url
        """
        self.bclient.connect_socket(self.server_url.hostname, self.server_url.port)

    def disconnect_socket(self):
        self.bclient.disconnect_socket()

    def send_hello(self):
        """
        Send OPC-UA hello to server
        """
        ack = self.bclient.send_hello(self.server_url.geturl())
        # FIXME check ack

    def open_secure_channel(self, renew=False):
        """
        Open secure channel, if renew is True, renew channel
        """
        params = ua.OpenSecureChannelParameters()
        params.ClientProtocolVersion = 0
        params.RequestType = ua.SecurityTokenRequestType.Issue
        if renew:
            params.RequestType = ua.SecurityTokenRequestType.Renew
        params.SecurityMode = self.security_mode
        params.RequestedLifetime = self.secure_channel_timeout
        params.ClientNonce = "\x00"
        result = self.bclient.open_secure_channel(params)
        self.secure_channel_timeout = result.SecurityToken.RevisedLifetime

    def close_secure_channel(self):
        return self.bclient.close_secure_channel()

    def get_endpoints(self):
        params = ua.GetEndpointsParameters()
        params.EndpointUrl = self.server_url.geturl()
        params.ProfileUris = ["http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"]
        params.LocaleIds = ["http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"]
        return self.bclient.get_endpoints(params)

    def create_session(self):
        desc = ua.ApplicationDescription()
        desc.ApplicationUri = self.application_uri
        desc.ProductUri = self.product_uri
        desc.ApplicationName = ua.LocalizedText(self.name)
        desc.ApplicationType = ua.ApplicationType.Client

        params = ua.CreateSessionParameters()
        params.ClientNonce = utils.create_nonce()
        params.ClientCertificate = b""
        params.ClientDescription = desc
        params.EndpointUrl = self.server_url.geturl()
        params.SessionName = self.description + " Session" + str(self._session_counter)
        params.RequestedSessionTimeout = 3600000
        params.MaxResponseMessageSize = 0  # means no max size
        response = self.bclient.create_session(params)
        print("Certificate is ", response.ServerCertificate)
        self.server_certificate = response.ServerCertificate
        for ep in response.ServerEndpoints:
            if ep.SecurityMode == self.security_mode:
                # remember PolicyId's: we will use them in activate_session()
                for token in ep.UserIdentityTokens:
                    self.policy_ids[token.TokenType] = token.PolicyId
        self.session_timeout = response.RevisedSessionTimeout
        self.keepalive = KeepAlive(
            self, min(self.session_timeout, self.secure_channel_timeout) * 0.7
        )  # 0.7 is from spec
        self.keepalive.start()
        return response

    def activate_session(self):
        params = ua.ActivateSessionParameters()
        params.LocaleIds.append("en")
        if not self.server_url.username:
            params.UserIdentityToken = ua.AnonymousIdentityToken()
            params.UserIdentityToken.PolicyId = self.policy_ids[ua.UserTokenType.Anonymous]
        else:
            params.UserIdentityToken = ua.UserNameIdentityToken()
            params.UserIdentityToken.UserName = self.server_url.username
            if self.server_url.password:
                raise NotImplementedError
                # p = bytes(self.server_url.password, "utf8")
                # p = self.server_url.password
                # from Crypto.Cipher import PKCS1_OAEP
                # from Crypto.PublicKey import RSA
                # from binascii import a2b_base64
                # from Crypto.Util.asn1 import DerSequence
                # from IPython import embed
                # import ssl
                # print("TYPE", type(self.server_certificate))
                # pem = self.server_certificate
                # Convert from PEM to DER
                # lines = str(pem).replace(" ",'').split()
                # data = ''.join(lines[1:-1])
                # embed()
                # 3data = bytes(data, "utf8")
                # print("DATA", data)
                # der = a2b_base64(pem)
                # ssl.PEM_HEADER=""
                # ssl.PEM_FOOTER=""
                # der = ssl.PEM_cert_to_DER_cert(pem)
                # print("DER", der)

                # Extract subjectPublicKeyInfo field from X.509 certificate (see RFC3280)
                # cert = DerSequence()
                # cert.decode(der)
                # tbsCertificate = DerSequence()
                # tbsCertificate.decode(cert[0])
                # key = tbsCertificate[6]
                ##print("KEY2", key)

                # r = RSA.importKey(key)
                # cipher = PKCS1_OAEP.new(r)
                # ciphertext = cipher.encrypt(p)
                # params.UserIdentityToken.Password = ciphertext
                # print("KKK", self.policy_ids[ua.UserTokenType.UserName])
            params.UserIdentityToken.PolicyId = self.policy_ids[ua.UserTokenType.UserName]
            params.UserIdentityToken.EncryptionAlgorithm = "http://www.w3.org/2001/04/xmlenc#rsa-oaep"
        return self.bclient.activate_session(params)

    def close_session(self):
        """
        Close session
        """
        if self.keepalive:
            self.keepalive.stop()
        return self.bclient.close_session(True)

    def get_root_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.RootFolder))

    def get_objects_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.ObjectsFolder))

    def get_server_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.Server))

    def get_node(self, nodeid):
        """
        Get node using NodeId object or a string representing a NodeId
        """
        return Node(self.bclient, nodeid)

    def create_subscription(self, period, handler):
        """
        Create a subscription.
        returns a Subscription object which allow
        to subscribe to events or data on server
        """
        params = ua.CreateSubscriptionParameters()
        params.RequestedPublishingInterval = period
        params.RequestedLifetimeCount = 3000
        params.RequestedMaxKeepAliveCount = 10000
        params.MaxNotificationsPerPublish = 4294967295
        params.PublishingEnabled = True
        params.Priority = 0
        return Subscription(self.bclient, params, handler)

    def get_namespace_array(self):
        ns_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_NamespaceArray))
        return ns_node.get_value()

    def get_namespace_index(self, uri):
        uries = self.get_namespace_array()
        return uries.index(uri)
Example #6
0
class Client(object):

    """
    High level client to connect to an OPC-UA server.
    This class makes it easy to connect and browse address space.
    It attemps to expose as much functionality as possible
    but if you want to do to special things you will probably need
    to work with the BinaryClient object, available as self.bclient
    which offers a raw OPC-UA interface.
    """

    def __init__(self, url, timeout=1):
        """
        used url argument to connect to server.
        if you are unsure of url, write at least hostname and port
        and call get_endpoints
        timeout is the timeout to get an answer for requests to server
        public member of this call are available to be set by API users

        """
        self.logger = logging.getLogger(__name__)
        self.server_url = urlparse(url)
        self.name = "Pure Python Client"
        self.description = self.name
        self.application_uri = "urn:freeopcua:client"
        self.product_uri = "urn:freeopcua.github.no:client"
        self.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#None"
        self.security_mode = ua.MessageSecurityMode.None_
        self.secure_channel_id = None
        self.default_timeout = 3600000
        self.secure_channel_timeout = self.default_timeout
        self.session_timeout = self.default_timeout
        self._policy_ids = []
        self.server_certificate = ""
        self.client_certificate = ""
        self.private_key = ""
        self.bclient = BinaryClient(timeout)
        self._nonce = None
        self._session_counter = 1
        self.keepalive = None

    def load_client_certificate(self, path):
        """
        load our certificate from file, either pem or der
        """
        _, ext = os.path.splitext(path)
        with open(path, "br") as f:
            self.client_certificate = f.read()
        if ext == ".pem":
            self.client_certificate = uacrypto.dem_to_der(self.client_certificate)

    def load_private_key(self, path):
        with open(path, "br") as f:
            self.private_key = f.read()

    def connect_and_get_server_endpoints(self):
        """
        Connect, ask server for endpoints, and disconnect
        """
        self.connect_socket()
        self.send_hello()
        self.open_secure_channel()
        endpoints = self.get_endpoints()
        self.close_secure_channel()
        self.disconnect_socket()
        return endpoints

    def connect_and_find_servers(self):
        """
        Connect, ask server for a list of known servers, and disconnect
        """
        self.connect_socket()
        self.send_hello()
        self.open_secure_channel()  # spec says it should not be necessary to open channel
        servers = self.find_servers()
        self.close_secure_channel()
        self.disconnect_socket()
        return servers

    def connect_and_find_servers_on_network(self):
        """
        Connect, ask server for a list of known servers on network, and disconnect
        """
        self.connect_socket()
        self.send_hello()
        self.open_secure_channel()
        servers = self.find_servers_on_network()
        self.close_secure_channel()
        self.disconnect_socket()
        return servers

    def connect(self):
        """
        High level method
        Connect, create and activate session
        """
        self.connect_socket()
        self.send_hello()
        self.open_secure_channel()
        self.create_session()
        self.activate_session(username=self.server_url.username, password=self.server_url.password, certificate=self.client_certificate)

    def disconnect(self):
        """
        High level method
        Close session, secure channel and socket
        """
        self.close_session()
        self.close_secure_channel()
        self.disconnect_socket()

    def connect_socket(self):
        """
        connect to socket defined in url
        """
        self.bclient.connect_socket(self.server_url.hostname, self.server_url.port)

    def disconnect_socket(self):
        self.bclient.disconnect_socket()

    def send_hello(self):
        """
        Send OPC-UA hello to server
        """
        ack = self.bclient.send_hello(self.server_url.geturl())
        # FIXME check ack

    def open_secure_channel(self, renew=False):
        """
        Open secure channel, if renew is True, renew channel
        """
        params = ua.OpenSecureChannelParameters()
        params.ClientProtocolVersion = 0
        params.RequestType = ua.SecurityTokenRequestType.Issue
        if renew:
            params.RequestType = ua.SecurityTokenRequestType.Renew
        params.SecurityMode = self.security_mode
        params.RequestedLifetime = self.secure_channel_timeout
        params.ClientNonce = '\x00'
        result = self.bclient.open_secure_channel(params)
        self.secure_channel_timeout = result.SecurityToken.RevisedLifetime

    def close_secure_channel(self):
        return self.bclient.close_secure_channel()

    def get_endpoints(self):
        params = ua.GetEndpointsParameters()
        params.EndpointUrl = self.server_url.geturl()
        return self.bclient.get_endpoints(params)

    def register_server(self, server, discovery_configuration=None):
        """
        register a server to discovery server
        if discovery_configuration is provided, the newer register_server2 service call is used
        """
        serv = ua.RegisteredServer()
        serv.ServerUri = server.application_uri
        serv.ProductUri = server.product_uri
        serv.DiscoveryUrls = [server.endpoint.geturl()]
        serv.ServerType = server.application_type
        serv.ServerNames = [ua.LocalizedText(server.name)]
        serv.IsOnline = True
        if discovery_configuration:
            params = ua.RegisterServer2Parameters()
            params.Server = serv
            params.DiscoveryConfiguration = discovery_configuration
            return self.bclient.register_server2(params)
        else:
            return self.bclient.register_server(serv)

    def find_servers(self, uris=None):
        """
        send a FindServer request to the server. The answer should be a list of
        servers the server knows about
        A list of uris can be provided, only server having matching uris will be returned
        """
        if uris is None:
            uris = []
        params = ua.FindServersParameters()
        params.EndpointUrl = self.server_url.geturl()
        params.ServerUris = uris 
        return self.bclient.find_servers(params)

    def find_servers_on_network(self):
        params = ua.FindServersOnNetworkParameters()
        return self.bclient.find_servers_on_network(params)

    def create_session(self):
        desc = ua.ApplicationDescription()
        desc.ApplicationUri = self.application_uri
        desc.ProductUri = self.product_uri
        desc.ApplicationName = ua.LocalizedText(self.name)
        desc.ApplicationType = ua.ApplicationType.Client

        params = ua.CreateSessionParameters()
        params.ClientNonce = utils.create_nonce()
        params.ClientCertificate = b''
        params.ClientDescription = desc
        params.EndpointUrl = self.server_url.geturl()
        params.SessionName = self.description + " Session" + str(self._session_counter)
        params.RequestedSessionTimeout = 3600000
        params.MaxResponseMessageSize = 0  # means no max size
        params.ClientCertificate = self.client_certificate
        response = self.bclient.create_session(params)
        self.server_certificate = response.ServerCertificate
        for ep in response.ServerEndpoints:
            if urlparse(ep.EndpointUrl).scheme == self.server_url.scheme and ep.SecurityMode == self.security_mode:
                # remember PolicyId's: we will use them in activate_session()
                self._policy_ids = ep.UserIdentityTokens
        self.session_timeout = response.RevisedSessionTimeout
        self.keepalive = KeepAlive(self, min(self.session_timeout, self.secure_channel_timeout) * 0.7)  # 0.7 is from spec
        self.keepalive.start()
        return response

    def server_policy_id(self, token_type, default):
        """
        Find PolicyId of server's UserTokenPolicy by token_type.
        Return default if there's no matching UserTokenPolicy.
        """
        for policy in self._policy_ids:
            if policy.TokenType == token_type:
                return policy.PolicyId
        return default

    def activate_session(self, username=None, password=None, certificate=None):
        """
        Activate session using either username and password or private_key
        """
        params = ua.ActivateSessionParameters()
        params.LocaleIds.append("en")
        if not username and not certificate:
            params.UserIdentityToken = ua.AnonymousIdentityToken()
            params.UserIdentityToken.PolicyId = self.server_policy_id(ua.UserTokenType.Anonymous, b"anonymous")
        elif certificate:
            params.UserIdentityToken = ua.X509IdentityToken()
            params.UserIdentityToken.PolicyId = self.server_policy_id(ua.UserTokenType.Certificate, b"certificate_basic256")
            params.UserIdentityToken.CertificateData = certificate
            sig = uacrypto.sign_sha1(self.private_key, certificate)
            params.UserTokenSignature = ua.SignatureData()
            params.UserTokenSignature.Algorithm = b"http://www.w3.org/2000/09/xmldsig#rsa-sha1"
            params.UserTokenSignature.Signature = sig
        else:
            params.UserIdentityToken = ua.UserNameIdentityToken()
            params.UserIdentityToken.UserName = username 
            if self.server_url.password:
                pubkey = uacrypto.pubkey_from_dercert(self.server_certificate)
                data = uacrypto.encrypt_rsa_oaep(pubkey, bytes(password, "utf8"))
                params.UserIdentityToken.Password = data
            params.UserIdentityToken.PolicyId = self.server_policy_id(ua.UserTokenType.UserName, b"username_basic256")
            params.UserIdentityToken.EncryptionAlgorithm = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep'
        return self.bclient.activate_session(params)

    def close_session(self):
        """
        Close session
        """
        if self.keepalive:
            self.keepalive.stop()
        return self.bclient.close_session(True)

    def get_root_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.RootFolder))

    def get_objects_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.ObjectsFolder))

    def get_server_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.Server))

    def get_node(self, nodeid):
        """
        Get node using NodeId object or a string representing a NodeId
        """
        return Node(self.bclient, nodeid)

    def create_subscription(self, period, handler):
        """
        Create a subscription.
        returns a Subscription object which allow
        to subscribe to events or data on server
        handler argument is a class with data_change and/or event methods.
        These methods will be called when notfication from server are received.
        See example-client.py.
        Do not do expensive/slow or network operation from these methods 
        since they are called directly from receiving thread. This is a design choice,
        start another thread if you need to do such a thing.
        """
        params = ua.CreateSubscriptionParameters()
        params.RequestedPublishingInterval = period
        params.RequestedLifetimeCount = 3000
        params.RequestedMaxKeepAliveCount = 10000
        params.MaxNotificationsPerPublish = 4294967295
        params.PublishingEnabled = True
        params.Priority = 0
        return Subscription(self.bclient, params, handler)

    def get_namespace_array(self):
        ns_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_NamespaceArray))
        return ns_node.get_value()

    def get_namespace_index(self, uri):
        uries = self.get_namespace_array()
        return uries.index(uri)
Example #7
0
class Client(object):

    """
    High level client to connect to an OPC-UA server.
    This class makes it easy to connect and browse address space.
    It attemps to expose as much functionality as possible
    but if you want to do to special things you will probably need
    to work with the BinaryClient object, available as self.bclient
    which offers a raw OPC-UA interface.

    """

    def __init__(self, url):
        """
        used url argument to connect to server.
        if you are unsure of url, write at least hostname and port
        and call get_endpoints
        """
        self.logger = logging.getLogger(__name__)
        self.server_url = urlparse(url)
        self.name = "Pure Python Client"
        self.description = self.name
        self.application_uri = "urn:freeopcua:client"
        self.product_uri = "urn:freeopcua.github.no:client"
        self.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#None"
        self.secure_channel_id = None
        self.default_timeout = 3600000
        self.secure_channel_timeout = self.default_timeout
        self.session_timeout = self.default_timeout
        self.bclient = BinaryClient()
        self._nonce = None
        self._session_counter = 1
        self.keepalive = None

    def get_server_endpoints(self):
        """
        Connect, ask server for endpoints, and disconnect
        """
        self.connect_socket()
        self.send_hello()
        self.open_secure_channel()
        endpoints = self.get_endpoints()
        self.close_secure_channel()
        return endpoints

    def connect(self):
        """
        High level method
        Connect, create and activate session
        """
        self.connect_socket()
        self.send_hello()
        self.open_secure_channel()
        self.create_session()
        self.activate_session()

    def disconnect(self):
        """
        High level method
        Close session, secure channel and socket
        """
        self.close_session()
        self.close_secure_channel()
        self.disconnect_socket()

    def connect_socket(self):
        """
        connect to socket defined in url
        """
        self.bclient.connect_socket(self.server_url.hostname, self.server_url.port)

    def disconnect_socket(self):
        self.bclient.disconnect_socket()

    def send_hello(self):
        """
        Send OPC-UA hello to server
        """
        ack = self.bclient.send_hello(self.server_url.geturl())
        # FIXME check ack

    def open_secure_channel(self, renew=False):
        """
        Open secure channel, if renew is True, renew channel
        """
        params = ua.OpenSecureChannelParameters()
        params.ClientProtocolVersion = 0
        params.RequestType = ua.SecurityTokenRequestType.Issue
        if renew:
            params.RequestType = ua.SecurityTokenRequestType.Renew
        params.SecurityMode = ua.MessageSecurityMode.None_
        params.RequestedLifetime = self.secure_channel_timeout
        params.ClientNonce = '\x00'
        result = self.bclient.open_secure_channel(params)
        self.secure_channel_timeout = result.SecurityToken.RevisedLifetime

    def close_secure_channel(self):
        return self.bclient.close_secure_channel()

    def get_endpoints(self):
        params = ua.GetEndpointsParameters()
        params.EndpointUrl = self.server_url.geturl()
        params.ProfileUris = ["http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"]
        params.LocaleIds = ["http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"]
        return self.bclient.get_endpoints(params)

    def create_session(self):
        desc = ua.ApplicationDescription()
        desc.ApplicationUri = self.application_uri
        desc.ProductUri = self.product_uri
        desc.ApplicationName = ua.LocalizedText(self.name)
        desc.ApplicationType = ua.ApplicationType.Client

        params = ua.CreateSessionParameters()
        params.ClientNonce = utils.create_nonce()
        params.ClientCertificate = b''
        params.ClientDescription = desc
        params.EndpointUrl = self.server_url.geturl()
        params.SessionName = self.description + " Session" + str(self._session_counter)
        params.RequestedSessionTimeout = 3600000
        params.MaxResponseMessageSize = 0  # means no max size
        response = self.bclient.create_session(params)
        self.session_timeout = response.RevisedSessionTimeout
        self.keepalive = KeepAlive(self, min(self.session_timeout, self.secure_channel_timeout) * 0.7)  # 0.7 is from spec
        self.keepalive.start()
        return response

    def activate_session(self):
        params = ua.ActivateSessionParameters()
        params.LocaleIds.append("en")
        if not self.server_url.username:
            params.UserIdentityToken = ua.AnonymousIdentityToken()
            params.UserIdentityToken.PolicyId = b"anonymous"
        else:
            params.UserIdentityToken = ua.UserNameIdentityToken()
            params.UserIdentityToken.UserName = self.server_url.username 
            if self.server_url.password:
                params.UserIdentityToken.Password = bytes(self.server_url.password)
            params.UserIdentityToken.PolicyId = b"user_name"
            #params.EncryptionAlgorithm = ''
        return self.bclient.activate_session(params)

    def close_session(self):
        """
        Close session
        """
        if self.keepalive:
            self.keepalive.stop()
        return self.bclient.close_session(True)

    def get_root_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.RootFolder))

    def get_objects_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.ObjectsFolder))

    def get_server_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.Server))

    def get_node(self, nodeid):
        """
        Get node using NodeId object or a string representing a NodeId
        """
        return Node(self.bclient, nodeid)

    def create_subscription(self, period, handler):
        """
        Create a subscription.
        returns a Subscription object which allow
        to subscribe to events or data on server
        """
        params = ua.CreateSubscriptionParameters()
        params.RequestedPublishingInterval = period
        params.RequestedLifetimeCount = 3000
        params.RequestedMaxKeepAliveCount = 10000
        params.MaxNotificationsPerPublish = 4294967295
        params.PublishingEnabled = True
        params.Priority = 0
        return Subscription(self.bclient, params, handler)

    def get_namespace_array(self):
        ns_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_NamespaceArray))
        return ns_node.get_value()

    def get_namespace_index(self, uri):
        uries = self.get_namespace_array()
        return uries.index(uri)
Example #8
0
class Client(object):
    """
    High level client to connect to an OPC-UA server.
    This class makes it easy to connect and browse address space.
    It attemps to expose as much functionality as possible
    but if you want to do to special things you will probably need
    to work with the BinaryClient object, available as self.bclient
    which offers a raw OPC-UA interface.

    """
    def __init__(self, url, timeout=1):
        """
        used url argument to connect to server.
        if you are unsure of url, write at least hostname and port
        and call get_endpoints
        timeout is the timeout to get an answer for requests to server
        """
        self.logger = logging.getLogger(__name__)
        self.server_url = urlparse(url)
        self.name = "Pure Python Client"
        self.description = self.name
        self.application_uri = "urn:freeopcua:client"
        self.product_uri = "urn:freeopcua.github.no:client"
        self.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#None"
        self.security_mode = ua.MessageSecurityMode.None_
        self.secure_channel_id = None
        self.default_timeout = 3600000
        self.secure_channel_timeout = self.default_timeout
        self.session_timeout = self.default_timeout
        self.policy_ids = {
            ua.UserTokenType.Anonymous: b'anonymous',
            ua.UserTokenType.UserName: b'user_name',
        }
        self.server_certificate = None
        self.bclient = BinaryClient(timeout)
        self._nonce = None
        self._session_counter = 1
        self.keepalive = None

    def get_server_endpoints(self):
        """
        Connect, ask server for endpoints, and disconnect
        """
        self.connect_socket()
        self.send_hello()
        self.open_secure_channel()
        endpoints = self.get_endpoints()
        self.close_secure_channel()
        self.disconnect_socket()
        return endpoints

    def find_all_servers(self):
        """
        Connect, ask server for a list of known servers, and disconnect
        """
        self.connect_socket()
        self.send_hello()
        self.open_secure_channel()
        servers = self.find_servers()
        self.close_secure_channel()
        self.disconnect_socket()
        return servers

    def connect(self):
        """
        High level method
        Connect, create and activate session
        """
        self.connect_socket()
        self.send_hello()
        self.open_secure_channel()
        self.create_session()
        self.activate_session()

    def disconnect(self):
        """
        High level method
        Close session, secure channel and socket
        """
        self.close_session()
        self.close_secure_channel()
        self.disconnect_socket()

    def connect_socket(self):
        """
        connect to socket defined in url
        """
        self.bclient.connect_socket(self.server_url.hostname,
                                    self.server_url.port)

    def disconnect_socket(self):
        self.bclient.disconnect_socket()

    def send_hello(self):
        """
        Send OPC-UA hello to server
        """
        ack = self.bclient.send_hello(self.server_url.geturl())
        # FIXME check ack

    def open_secure_channel(self, renew=False):
        """
        Open secure channel, if renew is True, renew channel
        """
        params = ua.OpenSecureChannelParameters()
        params.ClientProtocolVersion = 0
        params.RequestType = ua.SecurityTokenRequestType.Issue
        if renew:
            params.RequestType = ua.SecurityTokenRequestType.Renew
        params.SecurityMode = self.security_mode
        params.RequestedLifetime = self.secure_channel_timeout
        params.ClientNonce = '\x00'
        result = self.bclient.open_secure_channel(params)
        self.secure_channel_timeout = result.SecurityToken.RevisedLifetime

    def close_secure_channel(self):
        return self.bclient.close_secure_channel()

    def get_endpoints(self):
        params = ua.GetEndpointsParameters()
        params.EndpointUrl = self.server_url.geturl()
        return self.bclient.get_endpoints(params)

    def find_servers(self):
        params = ua.FindServersParameters()
        return self.bclient.find_servers(params)

    def create_session(self):
        desc = ua.ApplicationDescription()
        desc.ApplicationUri = self.application_uri
        desc.ProductUri = self.product_uri
        desc.ApplicationName = ua.LocalizedText(self.name)
        desc.ApplicationType = ua.ApplicationType.Client

        params = ua.CreateSessionParameters()
        params.ClientNonce = utils.create_nonce()
        params.ClientCertificate = b''
        params.ClientDescription = desc
        params.EndpointUrl = self.server_url.geturl()
        params.SessionName = self.description + " Session" + str(
            self._session_counter)
        params.RequestedSessionTimeout = 3600000
        params.MaxResponseMessageSize = 0  # means no max size
        response = self.bclient.create_session(params)
        #print("Certificate is ", response.ServerCertificate)
        self.server_certificate = response.ServerCertificate
        for ep in response.ServerEndpoints:
            if ep.SecurityMode == self.security_mode:
                # remember PolicyId's: we will use them in activate_session()
                for token in ep.UserIdentityTokens:
                    self.policy_ids[token.TokenType] = token.PolicyId
        self.session_timeout = response.RevisedSessionTimeout
        self.keepalive = KeepAlive(
            self,
            min(self.session_timeout, self.secure_channel_timeout) *
            0.7)  # 0.7 is from spec
        self.keepalive.start()
        return response

    def activate_session(self):
        params = ua.ActivateSessionParameters()
        params.LocaleIds.append("en")
        if not self.server_url.username:
            params.UserIdentityToken = ua.AnonymousIdentityToken()
            params.UserIdentityToken.PolicyId = self.policy_ids[
                ua.UserTokenType.Anonymous]
        else:
            params.UserIdentityToken = ua.UserNameIdentityToken()
            params.UserIdentityToken.UserName = self.server_url.username
            if self.server_url.password:
                raise NotImplementedError
                #p = bytes(self.server_url.password, "utf8")
                #p = self.server_url.password
                #from Crypto.Cipher import PKCS1_OAEP
                #from Crypto.PublicKey import RSA
                #from binascii import a2b_base64
                #from Crypto.Util.asn1 import DerSequence
                #from IPython import embed
                #import ssl
                #print("TYPE", type(self.server_certificate))
                #pem = self.server_certificate
                # Convert from PEM to DER
                #lines = str(pem).replace(" ",'').split()
                #data = ''.join(lines[1:-1])
                #embed()
                #3data = bytes(data, "utf8")
                #print("DATA", data)
                #der = a2b_base64(pem)
                #ssl.PEM_HEADER=""
                #ssl.PEM_FOOTER=""
                #der = ssl.PEM_cert_to_DER_cert(pem)
                #print("DER", der)

                # Extract subjectPublicKeyInfo field from X.509 certificate (see RFC3280)
                #cert = DerSequence()
                #cert.decode(der)
                #tbsCertificate = DerSequence()
                #tbsCertificate.decode(cert[0])
                #key = tbsCertificate[6]
                ##print("KEY2", key)

                #r = RSA.importKey(key)
                #cipher = PKCS1_OAEP.new(r)
                #ciphertext = cipher.encrypt(p)
                #params.UserIdentityToken.Password = ciphertext
                #print("KKK", self.policy_ids[ua.UserTokenType.UserName])
            params.UserIdentityToken.PolicyId = self.policy_ids[
                ua.UserTokenType.UserName]
            params.UserIdentityToken.EncryptionAlgorithm = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep'
        return self.bclient.activate_session(params)

    def close_session(self):
        """
        Close session
        """
        if self.keepalive:
            self.keepalive.stop()
        return self.bclient.close_session(True)

    def get_root_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.RootFolder))

    def get_objects_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.ObjectsFolder))

    def get_server_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.Server))

    def get_node(self, nodeid):
        """
        Get node using NodeId object or a string representing a NodeId
        """
        return Node(self.bclient, nodeid)

    def create_subscription(self, period, handler):
        """
        Create a subscription.
        returns a Subscription object which allow
        to subscribe to events or data on server
        handler argument is a class with data_change and/or event methods.
        These methods will be called when notfication from server are received.
        See example-client.py.
        Do not do expensive/slow or network operation from these methods 
        since they are called directly from receiving thread. This is a design choice,
        start another thread if you need to do such a thing.
        """
        params = ua.CreateSubscriptionParameters()
        params.RequestedPublishingInterval = period
        params.RequestedLifetimeCount = 3000
        params.RequestedMaxKeepAliveCount = 10000
        params.MaxNotificationsPerPublish = 4294967295
        params.PublishingEnabled = True
        params.Priority = 0
        return Subscription(self.bclient, params, handler)

    def get_namespace_array(self):
        ns_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_NamespaceArray))
        return ns_node.get_value()

    def get_namespace_index(self, uri):
        uries = self.get_namespace_array()
        return uries.index(uri)
Example #9
0
class Client(object):
    """
    High level client to connect to an OPC-UA server.
    This class makes it easy to connect and browse address space.
    It attemps to expose as much functionality as possible
    but if you want to do to special things you will probably need
    to work with the BinaryClient object, available as self.bclient
    which offers a raw OPC-UA interface.

    """
    def __init__(self, url):
        """
        used url argument to connect to server.
        if you are unsure of url, write at least hostname and port
        and call get_endpoints
        """
        self.logger = logging.getLogger(__name__)
        self.server_url = urlparse(url)
        self.name = "Pure Python Client"
        self.description = self.name
        self.application_uri = "urn:freeopcua:client"
        self.product_uri = "urn:freeopcua.github.no:client"
        self.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#None"
        self.secure_channel_id = None
        self.default_timeout = 3600000
        self.secure_channel_timeout = self.default_timeout
        self.session_timeout = self.default_timeout
        self.bclient = BinaryClient()
        self._nonce = None
        self._session_counter = 1
        self.keepalive = None

    def get_server_endpoints(self):
        """
        Connect, ask server for endpoints, and disconnect
        """
        self.connect_socket()
        self.send_hello()
        self.open_secure_channel()
        endpoints = self.get_endpoints()
        self.close_secure_channel()
        return endpoints

    def connect(self):
        """
        High level method
        Connect, create and activate session
        """
        self.connect_socket()
        self.send_hello()
        self.open_secure_channel()
        self.create_session()
        self.activate_session()

    def disconnect(self):
        """
        High level method
        Close session, secure channel and socket
        """
        self.close_session()
        self.close_secure_channel()
        self.disconnect_socket()

    def connect_socket(self):
        """
        connect to socket defined in url
        """
        self.bclient.connect_socket(self.server_url.hostname,
                                    self.server_url.port)

    def disconnect_socket(self):
        self.bclient.disconnect_socket()

    def send_hello(self):
        """
        Send OPC-UA hello to server
        """
        ack = self.bclient.send_hello(self.server_url.geturl())
        # FIXME check ack

    def open_secure_channel(self, renew=False):
        """
        Open secure channel, if renew is True, renew channel
        """
        params = ua.OpenSecureChannelParameters()
        params.ClientProtocolVersion = 0
        params.RequestType = ua.SecurityTokenRequestType.Issue
        if renew:
            params.RequestType = ua.SecurityTokenRequestType.Renew
        params.SecurityMode = ua.MessageSecurityMode.None_
        params.RequestedLifetime = self.secure_channel_timeout
        params.ClientNonce = '\x00'
        result = self.bclient.open_secure_channel(params)
        self.secure_channel_timeout = result.SecurityToken.RevisedLifetime

    def close_secure_channel(self):
        return self.bclient.close_secure_channel()

    def get_endpoints(self):
        params = ua.GetEndpointsParameters()
        params.EndpointUrl = self.server_url.geturl()
        params.ProfileUris = [
            "http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"
        ]
        params.LocaleIds = [
            "http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"
        ]
        return self.bclient.get_endpoints(params)

    def create_session(self):
        desc = ua.ApplicationDescription()
        desc.ApplicationUri = self.application_uri
        desc.ProductUri = self.product_uri
        desc.ApplicationName = ua.LocalizedText(self.name)
        desc.ApplicationType = ua.ApplicationType.Client

        params = ua.CreateSessionParameters()
        params.ClientNonce = utils.create_nonce()
        params.ClientCertificate = b''
        params.ClientDescription = desc
        params.EndpointUrl = self.server_url.geturl()
        params.SessionName = self.description + " Session" + str(
            self._session_counter)
        params.RequestedSessionTimeout = 3600000
        params.MaxResponseMessageSize = 0  # means no max size
        response = self.bclient.create_session(params)
        self.session_timeout = response.RevisedSessionTimeout
        self.keepalive = KeepAlive(
            self,
            min(self.session_timeout, self.secure_channel_timeout) *
            0.7)  # 0.7 is from spec
        self.keepalive.start()
        return response

    def activate_session(self):
        params = ua.ActivateSessionParameters()
        params.LocaleIds.append("en")
        if not self.server_url.username:
            params.UserIdentityToken = ua.AnonymousIdentityToken()
            params.UserIdentityToken.PolicyId = b"anonymous"
        else:
            params.UserIdentityToken = ua.UserNameIdentityToken()
            params.UserIdentityToken.UserName = self.server_url.username
            if self.server_url.password:
                params.UserIdentityToken.Password = bytes(
                    self.server_url.password)
            params.UserIdentityToken.PolicyId = b"user_name"
            #params.EncryptionAlgorithm = ''
        return self.bclient.activate_session(params)

    def close_session(self):
        """
        Close session
        """
        if self.keepalive:
            self.keepalive.stop()
        return self.bclient.close_session(True)

    def get_root_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.RootFolder))

    def get_objects_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.ObjectsFolder))

    def get_server_node(self):
        return self.get_node(ua.TwoByteNodeId(ua.ObjectIds.Server))

    def get_node(self, nodeid):
        """
        Get node using NodeId object or a string representing a NodeId
        """
        return Node(self.bclient, nodeid)

    def create_subscription(self, period, handler):
        """
        Create a subscription.
        returns a Subscription object which allow
        to subscribe to events or data on server
        """
        params = ua.CreateSubscriptionParameters()
        params.RequestedPublishingInterval = period
        params.RequestedLifetimeCount = 3000
        params.RequestedMaxKeepAliveCount = 10000
        params.MaxNotificationsPerPublish = 4294967295
        params.PublishingEnabled = True
        params.Priority = 0
        return Subscription(self.bclient, params, handler)

    def get_namespace_array(self):
        ns_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_NamespaceArray))
        return ns_node.get_value()

    def get_namespace_index(self, uri):
        uries = self.get_namespace_array()
        return uries.index(uri)