1#!/usr/bin/env python 2# coding=utf-8 3############################################## 4# Copyright (c) 2021-2022 Huawei Device Co., Ltd. 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16############################################## 17import json 18import os 19import re 20import stat 21import sys 22from subprocess import Popen 23from subprocess import PIPE 24 25global_config = {} 26 27templates = { 28 'generate-keypair': { 29 'required': ['keyAlias', 'keyAlg', 'keySize', 'keystoreFile'], 30 'others': ['keyPwd', 'keystorePwd'] 31 }, 32 'generate-csr': { 33 'required': ['keyAlias', 'signAlg', 'subject', 'keystoreFile', 'outFile'], 34 'others': ['keyPwd', 'keystorePwd'] 35 }, 36 'generate-ca': { 37 'required': ['keyAlias', 'signAlg', 'keyAlg', 'keySize', 'subject', 'keystoreFile', 'outFile'], 38 'others': ['keyPwd', 'keystorePwd', 'issuer', 'issuerKeyAlias', 'issuerKeyPwd', 'validity', 39 'basicConstraintsPathLen'] 40 }, 41 'generate-app-cert': { 42 'required': ['keyAlias', 'signAlg', 'issuer', 'issuerKeyAlias', 'subject', 'keystoreFile', 43 'subCaCertFile', 'rootCaCertFile', 'outForm', 'outFile'], 44 'others': ['keyPwd', 'keystorePwd', 'issuerKeyPwd', 'validity', 45 'basicConstraintsPathLen'] 46 }, 47 'generate-profile-cert': { 48 'required': ['keyAlias', 'signAlg', 'issuer', 'issuerKeyAlias', 'subject', 'keystoreFile', 49 'subCaCertFile', 'rootCaCertFile', 'outForm', 'outFile'], 50 'others': ['keyPwd', 'keystorePwd', 'issuerKeyPwd', 'validity', 51 'basicConstraintsPathLen'] 52 }, 53 'sign-profile': { 54 'required': ['keyAlias', 'signAlg', 'mode', 'profileCertFile', 'inFile', 'keystoreFile', 'outFile'], 55 'others': ['keyPwd', 'keystorePwd'] 56 }, 57 'sign-app': { 58 'required': ['keyAlias', 'signAlg', 'mode', 'appCertFile', 'profileFile', 'inFile', 'keystoreFile', 'outFile'], 59 'others': ['keyPwd', 'keystorePwd', 'inForm', 'signCode'] 60 }, 61} 62 63 64def print_help(): 65 content = "\n" \ 66 "Usage:python autosign.py <generate|sign> \n" \ 67 " signtool.jar : Main progress jar file\n" \ 68 "\n" \ 69 "Example: \n" \ 70 " python autosign.py createAppCertAndProfile \n" \ 71 " python autosign.py signHap" \ 72 "\n" 73 print(content) 74 75 76def get_from_single_config(config_key, item_key, required=False): 77 param = global_config.get(config_key, {}).get(item_key, None) 78 if not param: 79 param = global_config.get('common', {}).get(item_key, None) 80 if not param: 81 if required: 82 print('Prepare loading: {}, config: {}'.format(config_key, global_config.get(config_key))) 83 print("Params {} is required.".format(item_key)) 84 exit(1) 85 return param 86 87 88def prepare_dir(dir_name): 89 if not os.path.exists(dir_name): 90 os.mkdir(dir_name) 91 92 93def load_engine(engine_config): 94 tar_dir = global_config.get('config', {}).get('targetDir') 95 prepare_dir(tar_dir) 96 97 cmds = [] 98 for eng_k, eng_v in engine_config.items(): 99 template = templates.get(eng_v) 100 cmd = [eng_v] 101 for required_key in template.get('required'): 102 param = get_from_single_config(eng_k, required_key, True) 103 if required_key.endswith('File') and required_key != 'inFile' and os.path.basename(param) == param: 104 param = os.path.join(tar_dir, param) 105 cmd.append('-{}'.format(required_key)) 106 cmd.append(param) 107 108 for others_key in template.get('others'): 109 param = get_from_single_config(eng_k, others_key, False) 110 if param: 111 cmd.append('-{}'.format(others_key)) 112 cmd.append(param) 113 cmds.append(cmd) 114 return cmds 115 116 117def run_target(cmd): 118 command = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, shell=False) 119 out = command.stdout.readlines() 120 with open("log.txt", mode='a+', encoding='utf-8') as f: 121 if len(out) > 0: 122 f.writelines(' '.join(cmd) + "\r\n") 123 for line in out: 124 f.writelines(str(line.strip()) + "\r\n") 125 126 success = True 127 error = command.stderr.readlines() 128 with open("error.txt", mode='a+', encoding='utf-8') as f: 129 if len(error) > 0: 130 f.writelines(' '.join(cmd) + "\r\n") 131 132 for line in error: 133 success = False 134 f.writelines(str(line.strip()) + "\r\n") 135 136 command.wait() 137 return success 138 139 140def run_with_engine(engine, jar): 141 cmds = load_engine(engine) 142 for cmd in cmds: 143 cmd.insert(0, jar) 144 cmd.insert(0, '-jar') 145 cmd.insert(0, 'java') 146 result = run_target(cmd) 147 if not result: 148 print("Command error on executing cmd, please check error.txt") 149 print(' '.join(cmd)) 150 exit(1) 151 print("Success!") 152 pass 153 154 155def do_sign(jar): 156 sign_engine_config = { 157 'sign.profile': 'sign-profile', 158 'sign.app': 'sign-app' 159 } 160 run_with_engine(sign_engine_config, jar) 161 162 163def do_sign_hap(jar): 164 sign_hap_engine_config = { 165 'sign.app': 'sign-app' 166 } 167 run_with_engine(sign_hap_engine_config, jar) 168 169 170def do_sign_elf(jar): 171 sign_elf_engine_config = { 172 'sign.app': 'sign-app' 173 } 174 run_with_engine(sign_elf_engine_config, jar) 175 176 177def do_generate(jar): 178 cert_engine_config = { 179 'app.keypair': 'generate-keypair', 180 'profile.keypair': 'generate-keypair', 181 'csr': 'generate-csr', 182 'root-ca': 'generate-ca', 183 'sub-ca.app': 'generate-ca', 184 'sub-ca.profile': 'generate-ca', 185 'cert.app': 'generate-app-cert', 186 'cert.profile': 'generate-profile-cert', 187 } 188 run_with_engine(cert_engine_config, jar) 189 190 191def do_generate_root_cert(jar): 192 root_engine_config = { 193 'profile.keypair': 'generate-keypair', 194 'root-ca': 'generate-ca', 195 'sub-ca.app': 'generate-ca', 196 'sub-ca.profile': 'generate-ca', 197 'cert.profile': 'generate-profile-cert', 198 } 199 run_with_engine(root_engine_config, jar) 200 201 202def do_generate_app_cert(jar): 203 app_cert_engine_config = { 204 'app.keypair': 'generate-keypair', 205 'cert.app': 'generate-app-cert', 206 } 207 run_with_engine(app_cert_engine_config, jar) 208 209 210def do_sign_profile(jar): 211 app_cert_engine_config = { 212 'sign.profile': 'sign-profile', 213 } 214 run_with_engine(app_cert_engine_config, jar) 215 216 217def convert_to_map(line, temp_map): 218 line = line.strip('\n') 219 strs = line.split('=', 1) 220 221 if len(strs) == 2: 222 if strs[1].startswith('$'): 223 temp_map[strs[0]] = temp_map[strs[1][1:]] 224 else: 225 temp_map[strs[0]] = strs[1] 226 227 228def load_config(config): 229 config_file = config 230 temp_map = {} 231 with open(config_file, 'r', encoding='utf-8') as f: 232 for line in f.readlines(): 233 if not re.match(r'\s*//[\s\S]*', line): 234 convert_to_map(line, temp_map) 235 236 for mk, mv in temp_map.items(): 237 strs = mk.rsplit('.', 1) 238 if not global_config.get(strs[0]): 239 global_config[strs[0]] = {} 240 global_config[strs[0]][strs[-1]] = mv 241 242 243def process_cmd(): 244 args = sys.argv 245 if len(args) <= 1 or '--help' == args[1] or '-h' == args[1]: 246 print_help() 247 exit(0) 248 249 action = args[1] 250 if action not in ['createRootAndSubCert', 'createAppCertAndProfile', 'signHap', 'signElf']: 251 print("Not support cmd") 252 print_help() 253 exit(1) 254 return action 255 256 257def process_jar(): 258 read_jar_file = global_config.get('config', {}).get('signtool') 259 if not os.path.exists(read_jar_file): 260 print("Jar file '{}' not found".format(read_jar_file)) 261 exit(1) 262 return read_jar_file 263 264 265def replace_cert_in_profile(): 266 profile_file = global_config.get('sign.profile', {}).get('inFile') 267 app_cert_file = global_config.get('cert.app', {}).get('outFile') 268 tar_dir = global_config.get('config', {}).get('targetDir') 269 app_cert_file = os.path.join(tar_dir, app_cert_file) 270 if not os.path.exists(profile_file): 271 print("profile file '{}' not found".format(jar_file)) 272 exit(1) 273 if not os.path.exists(app_cert_file): 274 print("app cert file '{}' not found".format(jar_file)) 275 exit(1) 276 277 app_cert = '' 278 # read app cert 279 with open(app_cert_file, 'r', encoding='utf-8') as f: 280 app_cert_temp = f.read() 281 app_cert = app_cert_temp.split("-----END CERTIFICATE-----")[0] + "-----END CERTIFICATE-----\n" 282 283 profile = {} 284 # read profile 285 with open(profile_file, 'r', encoding='utf-8') as f: 286 profile = json.load(f) 287 288 profile["bundle-info"]["distribution-certificate"] = app_cert 289 290 # save profile 291 flags = os.O_WRONLY | os.O_TRUNC 292 modes = stat.S_IWUSR 293 with os.fdopen(os.open(profile_file, flags, modes), 'w') as profile_write: 294 json.dump(profile, profile_write) 295 296 297if __name__ == '__main__': 298 act = process_cmd() 299 if act == 'createRootAndSubCert': 300 load_config('createRootAndSubCert.config') 301 jar_file = process_jar() 302 do_generate_root_cert(jar_file) 303 elif act == 'createAppCertAndProfile': 304 load_config('createAppCertAndProfile.config') 305 jar_file = process_jar() 306 do_generate_app_cert(jar_file) 307 replace_cert_in_profile() 308 do_sign_profile(jar_file) 309 elif act == 'signHap': 310 load_config('signHap.config') 311 jar_file = process_jar() 312 do_sign_hap(jar_file) 313 elif act == 'signElf': 314 load_config('signElf.config') 315 jar_file = process_jar() 316 do_sign_elf(jar_file) 317