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.hapsigntool.api.ServiceApi; 19 import com.ohos.hapsigntool.api.SignToolServiceImpl; 20 import com.ohos.hapsigntool.entity.Options; 21 import com.ohos.hapsigntool.error.CustomException; 22 import com.ohos.hapsigntool.error.ERROR; 23 import com.ohos.hapsigntool.utils.FileUtils; 24 import com.ohos.hapsigntool.utils.StringUtils; 25 import com.ohos.hapsigntoolcmd.CmdUtil; 26 import com.ohos.hapsigntoolcmd.CmdUtil.Method; 27 import com.ohos.hapsigntoolcmd.HelpDocument; 28 import com.ohos.hapsigntoolcmd.Params; 29 30 import org.apache.logging.log4j.LogManager; 31 import org.apache.logging.log4j.Logger; 32 33 import java.util.ArrayList; 34 import java.util.List; 35 36 /** 37 * HapSignTool. 38 * 39 * @since 2021/12/28 40 */ 41 public final class HapSignTool { 42 /** 43 * Add log info. 44 */ 45 private static final Logger LOGGER = LogManager.getLogger(HapSignTool.class); 46 47 /** 48 * Tool version. 49 */ 50 private static final String VERSION = "1.0.0"; 51 52 /** 53 * Local sign. 54 */ 55 private static final String LOCAL_SIGN = "localSign"; 56 57 /** 58 * Remote sign. 59 */ 60 private static final String REMOTE_SIGN = "remoteSign"; 61 62 /** 63 * Signed. 64 */ 65 private static final String SIGNED = "1"; 66 67 /** 68 * No signed. 69 */ 70 private static final String NOT_SIGNED = "0"; 71 72 private static List<String> informList = new ArrayList<>(); 73 74 static { 75 informList.add("bin"); 76 informList.add("elf"); 77 informList.add("zip"); 78 } 79 HapSignTool()80 private HapSignTool() { 81 } 82 83 /** 84 * Main entry. 85 * 86 * @param args arguments 87 */ main(String[] args)88 public static void main(String[] args) { 89 try { 90 boolean isSuccess = processCmd(args); 91 if (!isSuccess) { 92 System.exit(1); 93 } 94 } catch (CustomException exception) { 95 LOGGER.debug(exception.getMessage(), exception); 96 LOGGER.error(exception.getMessage()); 97 System.exit(1); 98 } 99 } 100 101 /** 102 * Process command. 103 * 104 * @param args arguments 105 * @return command processing result 106 * @throws CustomException custom exception for command execution failure 107 */ processCmd(String[] args)108 public static boolean processCmd(String[] args) throws CustomException { 109 if (args.length == 0 || StringUtils.isEmpty(args[0])) { 110 help(); 111 } else if ("-h".equals(args[0]) || "-help".equals(args[0])) { 112 help(); 113 } else if ("-v".equals(args[0]) || "-version".equals(args[0])) { 114 version(); 115 } else { 116 ServiceApi api = new SignToolServiceImpl(); 117 Params params = CmdUtil.convert2Params(args); 118 LOGGER.debug(params.toString()); 119 LOGGER.info("Start {}", params.getMethod()); 120 boolean isSuccess = dispatchParams(params, api); 121 if (isSuccess) { 122 LOGGER.info(String.format("%s %s", params.getMethod(), "success")); 123 } else { 124 LOGGER.info(String.format("%s %s", params.getMethod(), "failed")); 125 } 126 return isSuccess; 127 } 128 return true; 129 } 130 callGenerators(Params params, ServiceApi api)131 private static boolean callGenerators(Params params, ServiceApi api) { 132 boolean isSuccess = false; 133 switch (params.getMethod()) { 134 case Method.GENERATE_APP_CERT: 135 isSuccess = runAppCert(params.getOptions(), api); 136 break; 137 case Method.GENERATE_CA: 138 isSuccess = runCa(params.getOptions(), api); 139 break; 140 case Method.GENERATE_CERT: 141 isSuccess = runCert(params.getOptions(), api); 142 break; 143 case Method.GENERATE_CSR: 144 isSuccess = runCsr(params.getOptions(), api); 145 break; 146 case Method.GENERATE_KEYPAIR: 147 isSuccess = runKeypair(params.getOptions(), api); 148 break; 149 case Method.GENERATE_PROFILE_CERT: 150 isSuccess = runProfileCert(params.getOptions(), api); 151 break; 152 default: 153 CustomException.throwException(ERROR.COMMAND_ERROR, "Unsupported cmd"); 154 break; 155 } 156 return isSuccess; 157 } 158 dispatchParams(Params params, ServiceApi api)159 private static boolean dispatchParams(Params params, ServiceApi api) { 160 boolean isSuccess; 161 switch (params.getMethod()) { 162 case Method.SIGN_APP: 163 isSuccess = runSignApp(params.getOptions(), api); 164 break; 165 case Method.SIGN_PROFILE: 166 isSuccess = runSignProfile(params.getOptions(), api); 167 break; 168 case Method.VERIFY_APP: 169 isSuccess = runVerifyApp(params.getOptions(), api); 170 break; 171 case Method.VERIFY_PROFILE: 172 isSuccess = runVerifyProfile(params.getOptions(), api); 173 break; 174 default: 175 isSuccess = callGenerators(params, api); 176 break; 177 } 178 return isSuccess; 179 } 180 checkEndCertArguments(Options params)181 private static void checkEndCertArguments(Options params) { 182 params.required(Options.KEY_ALIAS, Options.ISSUER, Options.ISSUER_KEY_ALIAS, Options.SUBJECT, 183 Options.SIGN_ALG, Options.KEY_STORE_FILE); 184 String signAlg = params.getString(Options.SIGN_ALG); 185 CmdUtil.judgeEndSignAlgType(signAlg); 186 String outForm = params.getString(Options.OUT_FORM); 187 if (!StringUtils.isEmpty(outForm)) { 188 CmdUtil.verifyType(outForm, Options.OUT_FORM_SCOPE); 189 } 190 if (StringUtils.isEmpty(outForm) || "certChain".equals(outForm)) { 191 params.required(Options.SUB_CA_CERT_FILE, Options.CA_CERT_FILE); 192 FileUtils.validFileType(params.getString(Options.SUB_CA_CERT_FILE), "cer"); 193 FileUtils.validFileType(params.getString(Options.CA_CERT_FILE), "cer"); 194 } 195 String keyStoreFile = params.getString(Options.KEY_STORE_FILE); 196 FileUtils.validFileType(keyStoreFile, "p12", "jks"); 197 198 if (params.containsKey(Options.ISSUER_KEY_STORE_FILE)) { 199 String issuerKeyStoreFile = params.getString(Options.ISSUER_KEY_STORE_FILE); 200 FileUtils.validFileType(issuerKeyStoreFile, "p12", "jks"); 201 } 202 203 String outFile = params.getString(Options.OUT_FILE); 204 if (!StringUtils.isEmpty(outFile)) { 205 FileUtils.validFileType(outFile, "cer", "pem"); 206 } 207 } 208 runAppCert(Options params, ServiceApi api)209 private static boolean runAppCert(Options params, ServiceApi api) { 210 checkEndCertArguments(params); 211 return api.generateAppCert(params); 212 } 213 runCa(Options params, ServiceApi api)214 private static boolean runCa(Options params, ServiceApi api) { 215 params.required(Options.KEY_ALIAS, Options.KEY_ALG, Options.KEY_SIZE, Options.SUBJECT, 216 Options.SIGN_ALG, Options.KEY_STORE_FILE); 217 String keyAlg = params.getString(Options.KEY_ALG); 218 CmdUtil.judgeAlgType(keyAlg); 219 String size = params.getString(Options.KEY_SIZE); 220 CmdUtil.judgeSize(size, keyAlg); 221 String signAlg = params.getString(Options.SIGN_ALG); 222 CmdUtil.judgeSignAlgType(signAlg); 223 FileUtils.validFileType(params.getString(Options.KEY_STORE_FILE), "p12", "jks"); 224 params.put(Options.KEY_SIZE, CmdUtil.convertAlgSize(size)); 225 return api.generateCA(params); 226 } 227 runCert(Options params, ServiceApi api)228 private static boolean runCert(Options params, ServiceApi api) { 229 params.required(Options.KEY_ALIAS, Options.ISSUER, Options.ISSUER_KEY_ALIAS, Options.SUBJECT, 230 Options.KEY_USAGE, Options.SIGN_ALG, Options.KEY_STORE_FILE); 231 String keyUsage = params.getString(Options.KEY_USAGE); 232 CmdUtil.verifyType(keyUsage, Options.KEY_USAGE_SCOPE); 233 String extKeyUsage = params.getString(Options.EXT_KEY_USAGE); 234 CmdUtil.verifyType(extKeyUsage, Options.EXT_KEY_USAGE_SCOPE); 235 String signAlg = params.getString(Options.SIGN_ALG); 236 CmdUtil.judgeSignAlgType(signAlg); 237 FileUtils.validFileType(params.getString(Options.KEY_STORE_FILE), "p12", "jks"); 238 if (params.containsKey(Options.ISSUER_KEY_STORE_FILE)) { 239 String issuerKeyStoreFile = params.getString(Options.ISSUER_KEY_STORE_FILE); 240 FileUtils.validFileType(issuerKeyStoreFile, "p12", "jks"); 241 } 242 return api.generateCert(params); 243 } 244 runCsr(Options params, ServiceApi api)245 private static boolean runCsr(Options params, ServiceApi api) { 246 params.required(Options.KEY_ALIAS, Options.SUBJECT, Options.SIGN_ALG, Options.KEY_STORE_FILE); 247 String signAlg = params.getString(Options.SIGN_ALG); 248 CmdUtil.judgeSignAlgType(signAlg); 249 FileUtils.validFileType(params.getString(Options.KEY_STORE_FILE), "p12", "jks"); 250 if (!StringUtils.isEmpty(params.getString(Options.OUT_FILE))) { 251 FileUtils.validFileType(params.getString(Options.OUT_FILE), "csr"); 252 } 253 254 return api.generateCsr(params); 255 } 256 runKeypair(Options params, ServiceApi api)257 private static boolean runKeypair(Options params, ServiceApi api) { 258 params.required(Options.KEY_ALIAS, Options.KEY_ALG, Options.KEY_SIZE, Options.KEY_STORE_FILE); 259 String keyAlg = params.getString(Options.KEY_ALG); 260 CmdUtil.judgeAlgType(keyAlg); 261 String size = params.getString(Options.KEY_SIZE); 262 CmdUtil.judgeSize(size, keyAlg); 263 params.put(Options.KEY_SIZE, CmdUtil.convertAlgSize(size)); 264 FileUtils.validFileType(params.getString(Options.KEY_STORE_FILE), "p12", "jks"); 265 266 return api.generateKeyStore(params); 267 } 268 runProfileCert(Options params, ServiceApi api)269 private static boolean runProfileCert(Options params, ServiceApi api) { 270 checkEndCertArguments(params); 271 return api.generateProfileCert(params); 272 } 273 runSignApp(Options params, ServiceApi api)274 private static boolean runSignApp(Options params, ServiceApi api) { 275 params.required(Options.MODE, Options.IN_FILE, Options.OUT_FILE, Options.SIGN_ALG); 276 String mode = params.getString(Options.MODE); 277 if (!LOCAL_SIGN.equalsIgnoreCase(mode) 278 && !REMOTE_SIGN.equalsIgnoreCase(mode) 279 && !"remoteResign".equalsIgnoreCase(mode)) { 280 CustomException.throwException(ERROR.COMMAND_ERROR, "mode params is incorrect"); 281 } 282 283 if (LOCAL_SIGN.equalsIgnoreCase(mode)) { 284 params.required(Options.KEY_STORE_FILE, Options.KEY_ALIAS, Options.APP_CERT_FILE); 285 FileUtils.validFileType(params.getString(Options.KEY_STORE_FILE), "p12", "jks"); 286 } 287 checkProfile(params); 288 String inForm = params.getString(Options.IN_FORM, "zip"); 289 if (!StringUtils.isEmpty(inForm) && !containsIgnoreCase(inForm)) { 290 CustomException.throwException(ERROR.NOT_SUPPORT_ERROR, "inForm params is incorrect"); 291 } 292 String signAlg = params.getString(Options.SIGN_ALG); 293 CmdUtil.judgeEndSignAlgType(signAlg); 294 295 return api.signHap(params); 296 } 297 checkProfile(Options params)298 private static void checkProfile(Options params) { 299 String inForm = params.getString(Options.IN_FORM); 300 String profileFile = params.getString(Options.PROFILE_FILE); 301 String profileSigned = params.getString(Options.PROFILE_SIGNED, SIGNED); 302 303 if ("elf".equalsIgnoreCase(inForm) && StringUtils.isEmpty(profileFile)) { 304 return; 305 } 306 if (!SIGNED.equals(profileSigned) && !NOT_SIGNED.equals(profileSigned)) { 307 CustomException.throwException(ERROR.NOT_SUPPORT_ERROR, "profileSigned params is incorrect"); 308 } 309 if (SIGNED.equals(profileSigned)) { 310 FileUtils.validFileType(profileFile, "p7b"); 311 } else { 312 FileUtils.validFileType(profileFile, "json"); 313 } 314 } 315 runSignProfile(Options params, ServiceApi api)316 private static boolean runSignProfile(Options params, ServiceApi api) { 317 params.required(Options.MODE, Options.SIGN_ALG, Options.OUT_FILE, Options.IN_FILE); 318 String mode = params.getString(Options.MODE); 319 if (!LOCAL_SIGN.equalsIgnoreCase(mode) && !REMOTE_SIGN.equalsIgnoreCase(mode)) { 320 CustomException.throwException(ERROR.COMMAND_ERROR, "mode params is incorrect"); 321 } 322 if (LOCAL_SIGN.equalsIgnoreCase(mode)) { 323 params.required(Options.KEY_STORE_FILE, Options.KEY_ALIAS, Options.PROFILE_CERT_FILE); 324 FileUtils.validFileType(params.getString(Options.KEY_STORE_FILE), "p12", "jks"); 325 } 326 327 String signAlg = params.getString(Options.SIGN_ALG); 328 CmdUtil.judgeEndSignAlgType(signAlg); 329 String outFile = params.getString(Options.OUT_FILE); 330 FileUtils.validFileType(outFile, "p7b"); 331 332 return api.signProfile(params); 333 } 334 runVerifyApp(Options params, ServiceApi api)335 private static boolean runVerifyApp(Options params, ServiceApi api) { 336 params.required(Options.IN_FILE, Options.OUT_CERT_CHAIN, 337 Options.OUT_PROFILE); 338 String inForm = params.getString(Options.IN_FORM, "zip"); 339 if (!containsIgnoreCase(inForm)) { 340 CustomException.throwException(ERROR.NOT_SUPPORT_ERROR, "inForm params must is " + informList); 341 } 342 FileUtils.validFileType(params.getString(Options.OUT_CERT_CHAIN), "cer"); 343 FileUtils.validFileType(params.getString(Options.OUT_PROFILE), "p7b"); 344 return api.verifyHap(params); 345 } 346 runVerifyProfile(Options params, ServiceApi api)347 private static boolean runVerifyProfile(Options params, ServiceApi api) { 348 params.required(Options.IN_FILE); 349 FileUtils.validFileType(params.getString(Options.IN_FILE), "p7b"); 350 String outFile = params.getString(Options.OUT_FILE); 351 if (!StringUtils.isEmpty(outFile)) { 352 FileUtils.validFileType(outFile, "json"); 353 } 354 355 return api.verifyProfile(params); 356 } 357 358 /** 359 * Software version. 360 */ version()361 public static void version() { 362 LOGGER.info(VERSION); 363 } 364 365 /** 366 * Print help to console. 367 */ help()368 public static void help() { 369 HelpDocument.printHelp(LOGGER); 370 } 371 containsIgnoreCase(String inForm)372 private static boolean containsIgnoreCase(String inForm) { 373 for (String s : informList) { 374 if (s.equalsIgnoreCase(inForm)) { 375 return true; 376 } 377 } 378 return false; 379 } 380 } 381