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