1 /* 2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package com.ohos.hapsigntool; 17 18 import com.ohos.entity.RetMsg; 19 import com.ohos.entity.SignAppParameters; 20 import com.ohos.entity.SignProfileParameters; 21 import com.ohos.entity.VerifyAppParameters; 22 import com.ohos.entity.VerifyProfileParameters; 23 import com.ohos.hapsigntool.api.ServiceApi; 24 import com.ohos.hapsigntool.api.SignToolServiceImpl; 25 import com.ohos.hapsigntool.entity.Options; 26 import com.ohos.hapsigntool.error.CustomException; 27 import com.ohos.hapsigntool.error.ERROR; 28 import com.ohos.hapsigntool.error.ParamException; 29 import com.ohos.hapsigntool.utils.FileUtils; 30 import com.ohos.hapsigntool.utils.StringUtils; 31 import com.ohos.hapsigntoolcmd.CmdUtil; 32 import com.ohos.hapsigntoolcmd.CmdUtil.Method; 33 import com.ohos.hapsigntoolcmd.HelpDocument; 34 import com.ohos.hapsigntoolcmd.Params; 35 36 import org.apache.logging.log4j.LogManager; 37 import org.apache.logging.log4j.Logger; 38 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.Locale; 42 43 /** 44 * HapSignTool. 45 * 46 * @since 2021/12/28 47 */ 48 public final class HapSignTool { 49 /** 50 * Add log info. 51 */ 52 private static final Logger LOGGER = LogManager.getLogger(HapSignTool.class); 53 54 /** 55 * Tool version. 56 */ 57 private static final String VERSION = "1.0.0"; 58 59 /** 60 * Local sign. 61 */ 62 private static final String LOCAL_SIGN = "localSign"; 63 64 /** 65 * Remote sign. 66 */ 67 private static final String REMOTE_SIGN = "remoteSign"; 68 69 /** 70 * Signed. 71 */ 72 private static final String SIGNED = "1"; 73 74 /** 75 * No signed. 76 */ 77 private static final String NOT_SIGNED = "0"; 78 79 private static final List<String> informList = new ArrayList<>(); 80 81 static { 82 informList.add("bin"); 83 informList.add("elf"); 84 informList.add("zip"); 85 } 86 HapSignTool()87 private HapSignTool() { 88 } 89 90 /** 91 * Main entry. 92 * 93 * @param args arguments 94 */ main(String[] args)95 public static void main(String[] args) { 96 try { 97 boolean isSuccess = processCmd(args); 98 if (!isSuccess) { 99 System.exit(1); 100 } 101 } catch (CustomException e) { 102 LOGGER.error(String.format(Locale.ROOT, "%s, code: %d. Details: %s", e.getError(), 103 e.getError().getErrorCode(), e.getMessage())); 104 System.exit(1); 105 } catch (Exception e) { 106 LOGGER.error(String.format(Locale.ROOT, "UNKNOWN_ERROR, code: %d. Details: %s", 107 ERROR.UNKNOWN_ERROR.getErrorCode(), e.getMessage())); 108 System.exit(1); 109 } 110 } 111 112 /** 113 * Process command. 114 * 115 * @param args arguments 116 * @return command processing result 117 * @throws CustomException custom exception for command execution failure 118 */ processCmd(String[] args)119 public static boolean processCmd(String[] args) throws CustomException { 120 if (args.length == 0 || StringUtils.isEmpty(args[0])) { 121 help(); 122 } else if ("-h".equals(args[0]) || "-help".equals(args[0])) { 123 help(); 124 } else if ("-v".equals(args[0]) || "-version".equals(args[0])) { 125 version(); 126 } else { 127 ServiceApi api = new SignToolServiceImpl(); 128 Params params = CmdUtil.convert2Params(args); 129 LOGGER.debug(params.toString()); 130 LOGGER.info("Start {}", params.getMethod()); 131 boolean isSuccess = dispatchParams(params, api); 132 if (isSuccess) { 133 LOGGER.info(String.format("%s %s", params.getMethod(), "success")); 134 } else { 135 LOGGER.info(String.format("%s %s", params.getMethod(), "failed")); 136 } 137 return isSuccess; 138 } 139 return true; 140 } 141 callGenerators(Params params, ServiceApi api)142 private static boolean callGenerators(Params params, ServiceApi api) { 143 boolean isSuccess = false; 144 switch (params.getMethod()) { 145 case Method.GENERATE_APP_CERT: 146 isSuccess = runAppCert(params.getOptions(), api); 147 break; 148 case Method.GENERATE_CA: 149 isSuccess = runCa(params.getOptions(), api); 150 break; 151 case Method.GENERATE_CERT: 152 isSuccess = runCert(params.getOptions(), api); 153 break; 154 case Method.GENERATE_CSR: 155 isSuccess = runCsr(params.getOptions(), api); 156 break; 157 case Method.GENERATE_KEYPAIR: 158 isSuccess = runKeypair(params.getOptions(), api); 159 break; 160 case Method.GENERATE_PROFILE_CERT: 161 isSuccess = runProfileCert(params.getOptions(), api); 162 break; 163 default: 164 CustomException.throwException(ERROR.COMMAND_ERROR, "Unsupported cmd"); 165 break; 166 } 167 return isSuccess; 168 } 169 dispatchParams(Params params, ServiceApi api)170 private static boolean dispatchParams(Params params, ServiceApi api) { 171 boolean isSuccess; 172 switch (params.getMethod()) { 173 case Method.SIGN_APP: 174 isSuccess = runSignApp(params.getOptions(), api); 175 break; 176 case Method.SIGN_PROFILE: 177 isSuccess = runSignProfile(params.getOptions(), api); 178 break; 179 case Method.VERIFY_APP: 180 isSuccess = runVerifyApp(params.getOptions(), api); 181 break; 182 case Method.VERIFY_PROFILE: 183 isSuccess = runVerifyProfile(params.getOptions(), api); 184 break; 185 default: 186 isSuccess = callGenerators(params, api); 187 break; 188 } 189 return isSuccess; 190 } 191 checkEndCertArguments(Options params)192 private static void checkEndCertArguments(Options params) { 193 params.required(Options.KEY_ALIAS, Options.ISSUER, Options.ISSUER_KEY_ALIAS, Options.SUBJECT, 194 Options.SIGN_ALG, Options.KEY_STORE_FILE); 195 String signAlg = params.getString(Options.SIGN_ALG); 196 CmdUtil.judgeEndSignAlgType(signAlg); 197 String outForm = params.getString(Options.OUT_FORM); 198 if (!StringUtils.isEmpty(outForm)) { 199 CmdUtil.verifyType(outForm, Options.OUT_FORM_SCOPE); 200 } 201 if (StringUtils.isEmpty(outForm) || "certChain".equals(outForm)) { 202 params.required(Options.SUB_CA_CERT_FILE, Options.CA_CERT_FILE); 203 FileUtils.validFileType(params.getString(Options.SUB_CA_CERT_FILE), "cer"); 204 FileUtils.validFileType(params.getString(Options.CA_CERT_FILE), "cer"); 205 } 206 String keyStoreFile = params.getString(Options.KEY_STORE_FILE); 207 FileUtils.validFileType(keyStoreFile, "p12", "jks"); 208 209 if (params.containsKey(Options.ISSUER_KEY_STORE_FILE)) { 210 String issuerKeyStoreFile = params.getString(Options.ISSUER_KEY_STORE_FILE); 211 FileUtils.validFileType(issuerKeyStoreFile, "p12", "jks"); 212 } 213 214 String outFile = params.getString(Options.OUT_FILE); 215 if (!StringUtils.isEmpty(outFile)) { 216 FileUtils.validFileType(outFile, "cer", "pem"); 217 } 218 } 219 runAppCert(Options params, ServiceApi api)220 private static boolean runAppCert(Options params, ServiceApi api) { 221 checkEndCertArguments(params); 222 return api.generateAppCert(params); 223 } 224 runCa(Options params, ServiceApi api)225 private static boolean runCa(Options params, ServiceApi api) { 226 params.required(Options.KEY_ALIAS, Options.KEY_ALG, Options.KEY_SIZE, Options.SUBJECT, 227 Options.SIGN_ALG, Options.KEY_STORE_FILE); 228 String keyAlg = params.getString(Options.KEY_ALG); 229 CmdUtil.judgeAlgType(keyAlg); 230 String size = params.getString(Options.KEY_SIZE); 231 CmdUtil.judgeSize(size, keyAlg); 232 String signAlg = params.getString(Options.SIGN_ALG); 233 CmdUtil.judgeSignAlgType(signAlg); 234 FileUtils.validFileType(params.getString(Options.KEY_STORE_FILE), "p12", "jks"); 235 params.put(Options.KEY_SIZE, CmdUtil.convertAlgSize(size)); 236 return api.generateCA(params); 237 } 238 runCert(Options params, ServiceApi api)239 private static boolean runCert(Options params, ServiceApi api) { 240 params.required(Options.KEY_ALIAS, Options.ISSUER, Options.ISSUER_KEY_ALIAS, Options.SUBJECT, 241 Options.KEY_USAGE, Options.SIGN_ALG, Options.KEY_STORE_FILE); 242 String keyUsage = params.getString(Options.KEY_USAGE); 243 CmdUtil.verifyType(keyUsage, Options.KEY_USAGE_SCOPE); 244 String extKeyUsage = params.getString(Options.EXT_KEY_USAGE); 245 CmdUtil.verifyType(extKeyUsage, Options.EXT_KEY_USAGE_SCOPE); 246 String signAlg = params.getString(Options.SIGN_ALG); 247 CmdUtil.judgeSignAlgType(signAlg); 248 FileUtils.validFileType(params.getString(Options.KEY_STORE_FILE), "p12", "jks"); 249 if (params.containsKey(Options.ISSUER_KEY_STORE_FILE)) { 250 String issuerKeyStoreFile = params.getString(Options.ISSUER_KEY_STORE_FILE); 251 FileUtils.validFileType(issuerKeyStoreFile, "p12", "jks"); 252 } 253 return api.generateCert(params); 254 } 255 runCsr(Options params, ServiceApi api)256 private static boolean runCsr(Options params, ServiceApi api) { 257 params.required(Options.KEY_ALIAS, Options.SUBJECT, Options.SIGN_ALG, Options.KEY_STORE_FILE); 258 String signAlg = params.getString(Options.SIGN_ALG); 259 CmdUtil.judgeSignAlgType(signAlg); 260 FileUtils.validFileType(params.getString(Options.KEY_STORE_FILE), "p12", "jks"); 261 if (!StringUtils.isEmpty(params.getString(Options.OUT_FILE))) { 262 FileUtils.validFileType(params.getString(Options.OUT_FILE), "csr"); 263 } 264 265 return api.generateCsr(params); 266 } 267 runKeypair(Options params, ServiceApi api)268 private static boolean runKeypair(Options params, ServiceApi api) { 269 params.required(Options.KEY_ALIAS, Options.KEY_ALG, Options.KEY_SIZE, Options.KEY_STORE_FILE); 270 String keyAlg = params.getString(Options.KEY_ALG); 271 CmdUtil.judgeAlgType(keyAlg); 272 String size = params.getString(Options.KEY_SIZE); 273 CmdUtil.judgeSize(size, keyAlg); 274 params.put(Options.KEY_SIZE, CmdUtil.convertAlgSize(size)); 275 FileUtils.validFileType(params.getString(Options.KEY_STORE_FILE), "p12", "jks"); 276 277 return api.generateKeyStore(params); 278 } 279 runProfileCert(Options params, ServiceApi api)280 private static boolean runProfileCert(Options params, ServiceApi api) { 281 checkEndCertArguments(params); 282 return api.generateProfileCert(params); 283 } 284 runSignApp(Options params, ServiceApi api)285 private static boolean runSignApp(Options params, ServiceApi api) { 286 params.required(Options.MODE, Options.IN_FILE, Options.OUT_FILE, Options.SIGN_ALG); 287 String mode = params.getString(Options.MODE); 288 if (!LOCAL_SIGN.equalsIgnoreCase(mode) 289 && !REMOTE_SIGN.equalsIgnoreCase(mode) 290 && !"remoteResign".equalsIgnoreCase(mode)) { 291 CustomException.throwException(ERROR.COMMAND_ERROR, "mode params is incorrect"); 292 } 293 294 if (LOCAL_SIGN.equalsIgnoreCase(mode)) { 295 params.required(Options.KEY_STORE_FILE, Options.KEY_ALIAS, Options.APP_CERT_FILE); 296 FileUtils.validFileType(params.getString(Options.KEY_STORE_FILE), "p12", "jks"); 297 } 298 checkProfile(params); 299 String inForm = params.getString(Options.IN_FORM, "zip"); 300 if (!StringUtils.isEmpty(inForm) && !containsIgnoreCase(inForm)) { 301 CustomException.throwException(ERROR.NOT_SUPPORT_ERROR, "inForm params is incorrect"); 302 } 303 String signAlg = params.getString(Options.SIGN_ALG); 304 CmdUtil.judgeEndSignAlgType(signAlg); 305 306 return api.signHap(params); 307 } 308 checkProfile(Options params)309 private static void checkProfile(Options params) { 310 String inForm = params.getString(Options.IN_FORM); 311 String profileFile = params.getString(Options.PROFILE_FILE); 312 String profileSigned = params.getString(Options.PROFILE_SIGNED, SIGNED); 313 314 if ("elf".equalsIgnoreCase(inForm) && StringUtils.isEmpty(profileFile)) { 315 return; 316 } 317 if (!SIGNED.equals(profileSigned) && !NOT_SIGNED.equals(profileSigned)) { 318 CustomException.throwException(ERROR.NOT_SUPPORT_ERROR, "profileSigned params is incorrect"); 319 } 320 if (SIGNED.equals(profileSigned)) { 321 FileUtils.validFileType(profileFile, "p7b"); 322 } else { 323 FileUtils.validFileType(profileFile, "json"); 324 } 325 } 326 runSignProfile(Options params, ServiceApi api)327 private static boolean runSignProfile(Options params, ServiceApi api) { 328 params.required(Options.MODE, Options.SIGN_ALG, Options.OUT_FILE, Options.IN_FILE); 329 String mode = params.getString(Options.MODE); 330 if (!LOCAL_SIGN.equalsIgnoreCase(mode) && !REMOTE_SIGN.equalsIgnoreCase(mode)) { 331 CustomException.throwException(ERROR.COMMAND_ERROR, "mode params is incorrect"); 332 } 333 if (LOCAL_SIGN.equalsIgnoreCase(mode)) { 334 params.required(Options.KEY_STORE_FILE, Options.KEY_ALIAS, Options.PROFILE_CERT_FILE); 335 FileUtils.validFileType(params.getString(Options.KEY_STORE_FILE), "p12", "jks"); 336 } 337 338 String signAlg = params.getString(Options.SIGN_ALG); 339 CmdUtil.judgeEndSignAlgType(signAlg); 340 String outFile = params.getString(Options.OUT_FILE); 341 FileUtils.validFileType(outFile, "p7b"); 342 343 return api.signProfile(params); 344 } 345 runVerifyApp(Options params, ServiceApi api)346 private static boolean runVerifyApp(Options params, ServiceApi api) { 347 params.required(Options.IN_FILE, Options.OUT_CERT_CHAIN, 348 Options.OUT_PROFILE); 349 String inForm = params.getString(Options.IN_FORM, "zip"); 350 if (!containsIgnoreCase(inForm)) { 351 CustomException.throwException(ERROR.NOT_SUPPORT_ERROR, "inForm params must is " + informList); 352 } 353 FileUtils.validFileType(params.getString(Options.OUT_CERT_CHAIN), "cer"); 354 FileUtils.validFileType(params.getString(Options.OUT_PROFILE), "p7b"); 355 return api.verifyHap(params); 356 } 357 runVerifyProfile(Options params, ServiceApi api)358 private static boolean runVerifyProfile(Options params, ServiceApi api) { 359 params.required(Options.IN_FILE); 360 FileUtils.validFileType(params.getString(Options.IN_FILE), "p7b"); 361 String outFile = params.getString(Options.OUT_FILE); 362 if (!StringUtils.isEmpty(outFile)) { 363 FileUtils.validFileType(outFile, "json"); 364 } 365 366 return api.verifyProfile(params); 367 } 368 369 /** 370 * Software version. 371 */ version()372 public static void version() { 373 LOGGER.info(VERSION); 374 } 375 376 /** 377 * Print help to console. 378 */ help()379 public static void help() { 380 HelpDocument.printHelp(LOGGER); 381 } 382 containsIgnoreCase(String inForm)383 private static boolean containsIgnoreCase(String inForm) { 384 for (String s : informList) { 385 if (s.equalsIgnoreCase(inForm)) { 386 return true; 387 } 388 } 389 return false; 390 } 391 392 /** 393 * sign App 394 * 395 * @param signAppParameters verifyProfileParameters 396 * @return RetMsg 397 */ signApp(SignAppParameters signAppParameters)398 public static RetMsg signApp(SignAppParameters signAppParameters) { 399 try { 400 if (signAppParameters == null) { 401 throw new ParamException("params is null"); 402 } 403 Options options = signAppParameters.toOptions(); 404 ServiceApi api = new SignToolServiceImpl(); 405 if (runSignApp(options, api)) { 406 return new RetMsg(ERROR.SUCCESS_CODE, "sign app success"); 407 } 408 return new RetMsg(ERROR.SIGN_ERROR, "sign app failed"); 409 } catch (CustomException e) { 410 return new RetMsg(e.getError(), e.getMessage()); 411 } catch (ParamException e) { 412 return new RetMsg(ERROR.COMMAND_PARAM_ERROR, "paramException : " + e.getMessage()); 413 } catch (Exception e) { 414 return new RetMsg(ERROR.UNKNOWN_ERROR, "unknownException : " + e.getMessage()); 415 } 416 } 417 418 /** 419 * verify App 420 * 421 * @param verifyAppParameters verifyProfileParameters 422 * @return RetMsg 423 */ verifyApp(VerifyAppParameters verifyAppParameters)424 public static RetMsg verifyApp(VerifyAppParameters verifyAppParameters) { 425 try { 426 if (verifyAppParameters == null) { 427 throw new ParamException("params is null"); 428 } 429 Options options = verifyAppParameters.toOptions(); 430 ServiceApi api = new SignToolServiceImpl(); 431 if (runVerifyApp(options, api)) { 432 return new RetMsg(ERROR.SUCCESS_CODE, "verify app success"); 433 } 434 return new RetMsg(ERROR.VERIFY_ERROR, "verify app failed"); 435 } catch (CustomException e) { 436 return new RetMsg(e.getError(), e.getMessage()); 437 } catch (ParamException e) { 438 return new RetMsg(ERROR.COMMAND_PARAM_ERROR, "paramException : " + e.getMessage()); 439 } catch (Exception e) { 440 return new RetMsg(ERROR.UNKNOWN_ERROR, "unknownException : " + e.getMessage()); 441 } 442 } 443 444 /** 445 * sign Profile 446 * 447 * @param signProfileParameters verifyProfileParameters 448 * @return RetMsg 449 */ signProfile(SignProfileParameters signProfileParameters)450 public static RetMsg signProfile(SignProfileParameters signProfileParameters) { 451 try { 452 if (signProfileParameters == null) { 453 throw new ParamException("params is null"); 454 } 455 Options options = signProfileParameters.toOptions(); 456 ServiceApi api = new SignToolServiceImpl(); 457 if (runSignProfile(options, api)) { 458 return new RetMsg(ERROR.SUCCESS_CODE, "sign profile success"); 459 } 460 return new RetMsg(ERROR.SIGN_ERROR, "sign profile failed"); 461 } catch (CustomException e) { 462 return new RetMsg(e.getError(), e.getMessage()); 463 } catch (ParamException e) { 464 return new RetMsg(ERROR.COMMAND_PARAM_ERROR, "paramException : " + e.getMessage()); 465 } catch (Exception e) { 466 return new RetMsg(ERROR.UNKNOWN_ERROR, "unknownException : " + e.getMessage()); 467 } 468 } 469 470 /** 471 * verify Profile 472 * 473 * @param verifyProfileParameters verifyProfileParameters 474 * @return RetMsg 475 */ verifyProfile(VerifyProfileParameters verifyProfileParameters)476 public static RetMsg verifyProfile(VerifyProfileParameters verifyProfileParameters) { 477 try { 478 if (verifyProfileParameters == null) { 479 throw new ParamException("params is null"); 480 } 481 Options options = verifyProfileParameters.toOptions(); 482 ServiceApi api = new SignToolServiceImpl(); 483 if (runVerifyProfile(options, api)) { 484 return new RetMsg(ERROR.SUCCESS_CODE, "verify profile success"); 485 } 486 return new RetMsg(ERROR.VERIFY_ERROR, "verify profile failed"); 487 } catch (CustomException e) { 488 return new RetMsg(e.getError(), e.getMessage()); 489 } catch (ParamException e) { 490 return new RetMsg(ERROR.COMMAND_PARAM_ERROR, "paramException : " + e.getMessage()); 491 } catch (Exception e) { 492 return new RetMsg(ERROR.UNKNOWN_ERROR, "unknownException : " + e.getMessage()); 493 } 494 } 495 } 496