# HG changeset patch # User Edouard Tisserant # Date 1663078929 -7200 # Node ID 50600b946ea7703c41db842ed1ea3604b640c72b # Parent 09dad7f00c36bd6a778da2efbe6fbb8d17971c00 OPC-US client: add support for x509 and user+password authentication. Commit for backup not including tests since encrypted OPC-UA test are still not valid. diff -r 09dad7f00c36 -r 50600b946ea7 opc_ua/client.py --- a/opc_ua/client.py Tue Sep 13 10:11:25 2022 +0200 +++ b/opc_ua/client.py Tue Sep 13 16:22:09 2022 +0200 @@ -108,9 +108,14 @@ paramList = authParams.get(AuthType, None) if paramList: for name,default in paramList: - res[name] = cfg("AuthType."+name) + value = cfg("AuthType."+name) + if value == "" or value is None: + value = default + # cryptomaterial is expected to be in project's user provide file directory + if name in ["Certificate","PrivateKey"]: + value = os.path.join(self.GetCTRoot()._getProjectFilesPath(), value) + res[name] = value - print(res) return res def GetFileName(self): @@ -134,6 +139,8 @@ CFLAGS = ' '.join(['-I"' + path + '"' for path in Open62541IncludePaths]) + # Note: all user provided files are systematicaly copied, including cryptomaterial + return [(c_path, CFLAGS)], LDFLAGS, True def GetVariableLocationTree(self): diff -r 09dad7f00c36 -r 50600b946ea7 opc_ua/opcua_client_maker.py --- a/opc_ua/opcua_client_maker.py Tue Sep 13 10:11:25 2022 +0200 +++ b/opc_ua/opcua_client_maker.py Tue Sep 13 16:22:09 2022 +0200 @@ -311,8 +311,13 @@ config = self.config_getter() self.client = Client(config["URI"]) - # TODO: crypto stuff - # client.set_security_string("Basic256Sha256,SignAndEncrypt,certificate-example.der,private-key-example.pem") + AuthType = config["AuthType"] + if AuthType=="UserPasword": + self.client.set_user(config["User"]) + self.client.set_password(config["Password"]) + elif AuthType=="x509": + self.client.set_security_string( + "{Policy},{Mode},{Certificate},{PrivateKey}".format(**config)) self.client.connect() self.client.load_type_definitions() # load definition of server specific structures/extension objects @@ -499,8 +504,39 @@ #include #include #include +#include + +#include +#include + +static UA_INLINE UA_ByteString +loadFile(const char *const path) {{ + UA_ByteString fileContents = UA_STRING_NULL; + + FILE *fp = fopen(path, "rb"); + if(!fp) {{ + errno = 0; + return fileContents; + }} + + fseek(fp, 0, SEEK_END); + fileContents.length = (size_t)ftell(fp); + fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte)); + if(fileContents.data) {{ + fseek(fp, 0, SEEK_SET); + size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp); + if(read != fileContents.length) + UA_ByteString_clear(&fileContents); + }} else {{ + fileContents.length = 0; + }} + fclose(fp); + + return fileContents; +}} static UA_Client *client; +static UA_ClientConfig *cc; #define DECL_VAR(ua_type, C_type, c_loc_name) \\ static UA_Variant c_loc_name##_variant; \\ @@ -516,6 +552,29 @@ }} +#define INIT_NoAuth() \\ + UA_ClientConfig_setDefault(cc); \\ + retval = UA_Client_connect(client, uri); + +/* Note : Policy is ignored here since open62541 client supports all policies by default */ +#define INIT_x509(Policy, UpperCaseMode, PrivateKey, Certificate) \\ + /* TODO try paths given in runtime CLI */ \\ + UA_ByteString certificate = loadFile(Certificate); \\ + UA_ByteString privateKey = loadFile(PrivateKey); \\ + \\ + printf("INIT_x509 %s,%s,%s,%s\\n", #Policy, #UpperCaseMode, PrivateKey, Certificate); \\ + cc->securityMode = UA_MESSAGESECURITYMODE_##UpperCaseMode; \\ + UA_ClientConfig_setDefaultEncryption(cc, certificate, privateKey, NULL, 0, NULL, 0); \\ + \\ + retval = UA_Client_connect(client, uri); \\ + \\ + UA_ByteString_clear(&certificate); \\ + UA_ByteString_clear(&privateKey); + +#define INIT_UserPassword(User, Password) \\ + printf("TODO INIT_UserPassword %s,%s\\n", User, Password); \\ + retval = UA_Client_connectUsername(client, uri, User, Password); + #define INIT_READ_VARIANT(ua_type, c_loc_name) \\ UA_Variant_init(&c_loc_name##_variant); @@ -526,11 +585,10 @@ {{ UA_StatusCode retval; client = UA_Client_new(); - UA_ClientConfig_setDefault(UA_Client_getConfig(client)); + cc = UA_Client_getConfig(client); + char *uri = "{uri}"; {init} - /* Connect to server */ - retval = UA_Client_connect(client, "{uri}"); if(retval != UA_STATUSCODE_GOOD) {{ UA_Client_delete(client); return EXIT_FAILURE; @@ -566,13 +624,25 @@ formatdict = dict( locstr = locstr, uri = config["URI"], - # TODO: pass authentication code. decl = "", cleanup = "", init = "", retrieve = "", publish = "" ) + + AuthType = config["AuthType"] + if AuthType == "x509": + config["UpperCaseMode"] = config["Mode"].upper() + formatdict["init"] += """ + INIT_x509({Policy}, {UpperCaseMode}, "{PrivateKey}", "{Certificate}")""".format(**config) + elif AuthType == "UserPassword": + formatdict["init"] += """ + INIT_UserPassword("{User}", "{Password}")""".format(**config) + else: + formatdict["init"] += """ + INIT_NoAuth()""" + for direction, data in self.iteritems(): iec_direction_prefix = {"input": "__I", "output": "__Q"}[direction] for row in data: @@ -586,13 +656,13 @@ DECL_VAR({ua_type}, {C_type}, {c_loc_name})""".format(**locals()) if direction == "input": - formatdict["init"] +=""" + formatdict["init"] += """ INIT_READ_VARIANT({ua_type}, {c_loc_name})""".format(**locals()) formatdict["retrieve"] += """ READ_VALUE({ua_type}, {ua_type_enum}, {c_loc_name}, {ua_nodeid_type}, {ua_nsidx}, {ua_node_id})""".format(**locals()) if direction == "output": - formatdict["init"] +=""" + formatdict["init"] += """ INIT_WRITE_VARIANT({ua_type}, {ua_type_enum}, {c_loc_name})""".format(**locals()) formatdict["publish"] += """ WRITE_VALUE({ua_type}, {c_loc_name}, {ua_nodeid_type}, {ua_nsidx}, {ua_node_id})""".format(**locals())