From 1a3081e22e5317eb9f15a6a8134a4bdc592f7aac Mon Sep 17 00:00:00 2001 From: nic Date: Thu, 7 Jun 2018 16:31:46 +0200 Subject: [PATCH 1/2] Client signature verification by server --- opcua/server/uaprocessor.py | 43 +++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/opcua/server/uaprocessor.py b/opcua/server/uaprocessor.py index 87066374c..d7a6785a6 100644 --- a/opcua/server/uaprocessor.py +++ b/opcua/server/uaprocessor.py @@ -165,6 +165,49 @@ def _process_message(self, typeid, requesthdr, algohdr, seqhdr, body): data = self._connection.security_policy.client_certificate + self.session.nonce self._connection.security_policy.asymmetric_cryptography.verify(data, params.ClientSignature.Signature) + # + # somewhere create a list of trusted certificates + # + from opcua.crypto import uacrypto + trusted_certificates = [uacrypto.load_certificate('certificate-example.der')] + + # + # To be implmeneted in the asymmetric_cryptography.verify (just called function above) + # + from opcua.crypto import uacrypto + client_cert = params.UserIdentityToken.CertificateData + sig1 = params.ClientSignature.Signature + sig2 = params.UserTokenSignature.Signature + verification_passed = False + + ''' + # test + cert1 = uacrypto.load_certificate('certificate-example.der') + cert2 = uacrypto.load_certificate('cert.pem') + import pdb + pdb.set_trace() + ''' + + for cert in trusted_certificates: + if uacrypto.der_from_x509(cert) == client_cert: + # even if certificates match, veryfy that a matching key is assigned with the signature + # not sure what is the role of data here but the verification can't be done with any data + try: + uacrypto.verify_sha1(cert, data, sig2) + verification_passed = True + except: + self.logger.warning('Faulty client signature received by server during connection activation') + break + + if verification_passed: + self.logger.info('Verification of signature with a trusted certificate is confirmed') + else: + self.logger.warning('An untrusted client activates a session with the server!!!') + + # + ####### + # + result = self.session.activate_session(params) response = ua.ActivateSessionResponse() From 9a1dffd360e9de3843c693c9747a6a18e043463e Mon Sep 17 00:00:00 2001 From: nic Date: Thu, 7 Jun 2018 17:01:28 +0200 Subject: [PATCH 2/2] add working examples of client and server --- examples/XMLSchemas/xml_opc_schema_test1.xml | 257 +++++++++++++++++++ examples/cert.pem | 15 ++ examples/client_encrypt_malicious.py | 36 +++ examples/key.pem | 16 ++ examples/server_encrypt_malicious.py | 74 ++++++ 5 files changed, 398 insertions(+) create mode 100644 examples/XMLSchemas/xml_opc_schema_test1.xml create mode 100644 examples/cert.pem create mode 100644 examples/client_encrypt_malicious.py create mode 100644 examples/key.pem create mode 100644 examples/server_encrypt_malicious.py diff --git a/examples/XMLSchemas/xml_opc_schema_test1.xml b/examples/XMLSchemas/xml_opc_schema_test1.xml new file mode 100644 index 000000000..863276006 --- /dev/null +++ b/examples/XMLSchemas/xml_opc_schema_test1.xml @@ -0,0 +1,257 @@ + + + + + iab_namespace + + + i=10 + i=12 + i=35 + i=40 + i=46 + i=47 + + + iab + The base type for all object nodes. + + i=85 + i=58 + ns=2;i=2 + ns=2;i=3 + ns=2;i=4 + ns=2;i=5 + + + + Analytics + The base type for all object nodes. + + ns=2;i=1 + i=58 + ns=2;i=6 + + + + cRIO + The base type for all object nodes. + + ns=2;i=1 + i=58 + ns=2;i=10 + + + + Twin + The base type for all object nodes. + + ns=2;i=1 + i=58 + ns=2;i=19 + + + + Customer + The base type for all object nodes. + + ns=2;i=1 + i=58 + ns=2;i=20 + + + + Measurement_stats + Measurement_stats + + ns=2;i=2 + i=61 + ns=2;i=7 + ns=2;i=21 + + + + temp_mean + temp_mean + + ns=2;i=6 + i=63 + ns=2;i=8 + ns=2;i=9 + + + 9.99 + + + + aproperty_float + aproperty_float + + ns=2;i=7 + i=68 + + + 9.99 + + + + aproperty_string + aproperty_string + + ns=2;i=7 + i=68 + + + 9.99 + + + + measurements + measurements + + ns=2;i=3 + i=61 + ns=2;i=12 + ns=2;i=15 + ns=2;i=18 + ns=2;s=testvariable + ns=2;s=anothertest + + + + temperature + temperature + + ns=2;i=10 + i=63 + ns=2;i=13 + ns=2;i=14 + + + 9.99 + + + + range + range + + ns=2;i=12 + i=68 + + + 9.99 + + + + accuracy + accuracy + + ns=2;i=12 + i=68 + + + 9.99 + + + + vibration + vibration + + ns=2;i=10 + i=63 + ns=2;i=16 + ns=2;i=17 + + + 9.99 + + + + range + range + + ns=2;i=15 + i=68 + + + 9.99 + + + + accuracy + accuracy + + ns=2;i=15 + i=68 + + + 9.99 + + + + timestep + timestep + + ns=2;i=10 + i=68 + + + 9.99 + + + + testvariable + testvariable + + ns=2;i=4 + i=63 + + + 9.99 + + + + testvariable + testvariable + + ns=2;i=5 + i=63 + + + 9.99 + + + + vibration_something + vibration_something + + ns=2;i=6 + i=63 + + + 9.99 + + + + testvariable + testvariable + + ns=2;i=10 + i=63 + + + 9.99 + + + + anothertest + anothertest + + ns=2;i=10 + i=63 + + + 9.99 + + + diff --git a/examples/cert.pem b/examples/cert.pem new file mode 100644 index 000000000..e453a8454 --- /dev/null +++ b/examples/cert.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICWzCCAcSgAwIBAgIJAI2vU+xObjKcMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTgwNDEwMTIzNzQ5WhcNMTkwNDEwMTIzNzQ5WjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB +gQDWxSvyTgs3Dc0wcOk2TwTvozP49fCsV8RfMjuDl4wRJSFWNteH50bBmhUUXhWO +5kFHwjCt0wBujjUfqg3vE3JY0+pa70X2iGXZ0Tk3/XSPUVE/cPa7DQleCTIhR5Di +Mo5leg3O339c0BdskYsV6UvZVv3wO2j/8yhE+CfTkkKAgwIDAQABo1MwUTAdBgNV +HQ4EFgQUqJSdMz0aYGfU5ySIBy2GA433BBMwHwYDVR0jBBgwFoAUqJSdMz0aYGfU +5ySIBy2GA433BBMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQBp +UU0+r7uisgdTgLEaEMU3acsgbd+VVULHJnvju3QVbPotRuhvjZsTiq934aYv9To0 +7V4ydPhi13sRu1tR89GX7u45Z0+uPBZI+yCLxxvCOBrA5THpM9yc2owhAbaIF/br +tVKSYH/9WtFCxMjAXrCGzN7HJnRLmR8Gumkq1t/X3w== +-----END CERTIFICATE----- diff --git a/examples/client_encrypt_malicious.py b/examples/client_encrypt_malicious.py new file mode 100644 index 000000000..9391c928a --- /dev/null +++ b/examples/client_encrypt_malicious.py @@ -0,0 +1,36 @@ +from opcua import Client +from opcua import ua +import time +import random +import logging +logging.basicConfig(level=logging.DEBUG) + +if __name__ == '__main__': + client = Client("opc.tcp://localhost:4841/freeopcua/server/") + + ## one can set different sets of key/cert for testing + + #client.load_private_key('key.pem') + #client.load_client_certificate('cert.pem') + client.load_private_key('private-key-example.pem') + client.load_client_certificate('certificate-example.der') + + client.set_security_string("Basic256,SignAndEncrypt,cert.pem,key.pem") + #client.set_security_string("Basic256,SignAndEncrypt,certificate-example.der,private-key-example.pem") + + ## this doesn't work because this cert.key pair is used for encryption of data + #client.set_security_string("Basic256,SignAndEncrypt,cert.pem,private-key-example.pem") + + time.sleep(5) + + client.connect() + print("connectedddd") + + var_temp = client.get_node("ns=2;i=12") + var_vibration = client.get_node("ns=2;i=15") + + while True: + var_temp.get_value() + time.sleep(10) + + diff --git a/examples/key.pem b/examples/key.pem new file mode 100644 index 000000000..33253dc77 --- /dev/null +++ b/examples/key.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANbFK/JOCzcNzTBw +6TZPBO+jM/j18KxXxF8yO4OXjBElIVY214fnRsGaFRReFY7mQUfCMK3TAG6ONR+q +De8TcljT6lrvRfaIZdnROTf9dI9RUT9w9rsNCV4JMiFHkOIyjmV6Dc7ff1zQF2yR +ixXpS9lW/fA7aP/zKET4J9OSQoCDAgMBAAECgYEAiTEaiYXhFCH02OTHlLGVbZ7L +LFWuj+jgwA7OhZjhGHKngPM3atEKGdHbdU9EZiwftz8M6XzH4ddlh/yROg8qEvh5 +XC6fSr5sCHiwXs3Q22RtHbY1iGoB6oxe2q4U/ds7nGEOYaDKhFb+PjthjTwWSIho +7DoQ+dKRRujyF5afGQECQQDrcUoYh4PIuh/PnwxxqB5OGmnHs9Aek1nUAqZt9l30 +Z00L+IDqP0+oFtfJ6k/lcHB2NxkbsOKVN4fydKlwEnNTAkEA6YXONaDLTvdELWF5 +fLu0DMyqGg1CsoT1TCtgWpvDsOkqKNgWu2aqxK2hGQhiCVCLqxosFIiW3QQzGjD7 +/1DIEQJASLg7HIxupcbnIGeNnmVAxvrdhTMCqw70Bcmzwe8AYgauA26D0tYvMkmr +6M+YUdOCyOCHvsfJlDEMqlRiKR1pxQJBALdgo7XpNr4j3h3k1YnlvFXwwgMDNsUJ +M7dEuL1uZBWgg8LfLbqrvJ4F2uPVeWbnIUaTntnhCNc2XQrMPJcZ0IECQH88VqZU +azRaGQ1jy3HjmiuriFFDcw01vEsI+VIZMTnShixYxWm7n9w9tITyhIPAdt+Kc/4s +dx8pcP9Gst8ldlI= +-----END PRIVATE KEY----- diff --git a/examples/server_encrypt_malicious.py b/examples/server_encrypt_malicious.py new file mode 100644 index 000000000..6c3cb3940 --- /dev/null +++ b/examples/server_encrypt_malicious.py @@ -0,0 +1,74 @@ +import sys +sys.path.insert(0, "..") +from datetime import datetime +from opcua import ua, uamethod, Server + +import logging +import ipdb +logging.basicConfig(level=logging.DEBUG) +import time + +try: + from IPython import embed +except ImportError: + import code + + def embed(): + vars = globals() + vars.update(locals()) + shell = code.InteractiveConsole(vars) + shell.interact() + +# server definition +server = Server() + +server.set_endpoint("opc.tcp://localhost:4841/freeopcua/server/") +server.set_server_name("Test Turbo Server") + +server.set_security_policy([ua.SecurityPolicyType.Basic256_SignAndEncrypt]) +''' +server.set_security_policy([ + ua.SecurityPolicyType.NoSecurity, + ua.SecurityPolicyType.Basic128Rsa15_SignAndEncrypt, + ua.SecurityPolicyType.Basic128Rsa15_Sign, + ua.SecurityPolicyType.Basic256_SignAndEncrypt, + ua.SecurityPolicyType.Basic256_Sign]) +''' +server.set_security_IDs(["Basic256"]) + +server.load_certificate("certificate-example.der") +server.load_private_key("private-key-example.pem") +#server.load_private_key("key.pem") + +uri = "iab_namespace" +idx = server.register_namespace(uri) + +########################################################################## +server.import_xml("XMLSchemas/xml_opc_schema_test1.xml") +########################################################################## +# modeler can also set variables to writable +#v1 = server.get_node("i=20004") +#v1.set_writable() + +v1 = server.get_node("ns=2;i=12") +v2 = server.get_node("ns=2;i=15") + +if __name__ == "__main__": + + server.start() + + # TODO why can't set variables to writable before running the server???? + server.historize_node_data_change(v1) + server.historize_node_data_change(v2) + + + # in case we decide to use good old loop instead of interativeshell + try: + while True: + time.sleep(10) + v1.set_value(1111111) + finally: + server.stop() + + +