• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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