• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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.hap.provider;
17 
18 import com.google.gson.JsonElement;
19 import com.google.gson.JsonObject;
20 import com.google.gson.JsonParseException;
21 import com.google.gson.JsonParser;
22 import com.ohos.hapsigntool.api.model.Options;
23 import com.ohos.hapsigntool.error.CustomException;
24 import com.ohos.hapsigntool.hap.config.SignerConfig;
25 import com.ohos.hapsigntool.hap.entity.SigningBlock;
26 import com.ohos.hapsigntool.hap.exception.InvalidParamsException;
27 import com.ohos.hapsigntool.hap.exception.MissingParamsException;
28 import com.ohos.hapsigntool.hap.exception.ProfileException;
29 import com.ohos.hapsigntool.hap.exception.SignatureException;
30 import com.ohos.hapsigntool.hap.exception.VerifyCertificateChainException;
31 import com.ohos.hapsigntool.hap.exception.HapFormatException;
32 import com.ohos.hapsigntool.hap.sign.SignBin;
33 import com.ohos.hapsigntool.hap.sign.SignHap;
34 import com.ohos.hapsigntool.hap.sign.SignatureAlgorithm;
35 import com.ohos.hapsigntool.hap.verify.VerifyUtils;
36 import com.ohos.hapsigntool.utils.CertificateUtils;
37 import com.ohos.hapsigntool.utils.DigestUtils;
38 import com.ohos.hapsigntool.utils.EscapeCharacter;
39 import com.ohos.hapsigntool.utils.FileUtils;
40 import com.ohos.hapsigntool.utils.HapUtils;
41 import com.ohos.hapsigntool.utils.ParamConstants;
42 import com.ohos.hapsigntool.utils.ParamProcessUtil;
43 import com.ohos.hapsigntool.utils.StringUtils;
44 import com.ohos.hapsigntool.zip.ByteBufferZipDataInput;
45 import com.ohos.hapsigntool.zip.RandomAccessFileZipDataInput;
46 import com.ohos.hapsigntool.zip.RandomAccessFileZipDataOutput;
47 import com.ohos.hapsigntool.zip.ZipDataInput;
48 import com.ohos.hapsigntool.zip.ZipDataOutput;
49 import com.ohos.hapsigntool.zip.ZipFileInfo;
50 import com.ohos.hapsigntool.zip.ZipUtils;
51 
52 import org.apache.logging.log4j.LogManager;
53 import org.apache.logging.log4j.Logger;
54 import org.bouncycastle.cms.CMSException;
55 import org.bouncycastle.cms.CMSSignedData;
56 import org.bouncycastle.jce.provider.BouncyCastleProvider;
57 
58 import java.io.File;
59 import java.io.FileOutputStream;
60 import java.io.IOException;
61 import java.io.RandomAccessFile;
62 import java.nio.ByteBuffer;
63 import java.nio.charset.StandardCharsets;
64 import java.nio.file.Files;
65 import java.nio.file.StandardCopyOption;
66 import java.security.InvalidKeyException;
67 import java.security.Security;
68 import java.security.cert.CertificateException;
69 import java.security.cert.X509CRL;
70 import java.security.cert.X509Certificate;
71 import java.util.ArrayList;
72 import java.util.Collections;
73 import java.util.HashMap;
74 import java.util.List;
75 import java.util.Map;
76 import java.util.Set;
77 import java.util.TimeZone;
78 import java.util.Optional;
79 import java.util.jar.JarFile;
80 import java.util.jar.JarOutputStream;
81 
82 /**
83  * Sign provider super class
84  *
85  * @since 2021-12-14
86  */
87 public abstract class SignProvider {
88     private static final Logger LOGGER = LogManager.getLogger(SignProvider.class);
89     private static final List<String> VALID_SIGN_ALG_NAME = new ArrayList<String>();
90     private static final List<String> PARAMETERS_NEED_ESCAPE = new ArrayList<String>();
91     private static final long TIMESTAMP = 1230768000000L;
92     private static final int COMPRESSION_MODE = 9;
93 
94     static {
95         VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA256_ECDSA);
96         VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA384_ECDSA);
97         VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA512_ECDSA);
98         VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA256_RSA_PSS);
99         VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA384_RSA_PSS);
100         VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA512_RSA_PSS);
101         VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA256_RSA_MGF1);
102         VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA384_RSA_MGF1);
103         VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA512_RSA_MGF1);
104     }
105 
106     static {
107         PARAMETERS_NEED_ESCAPE.add(ParamConstants.PARAM_REMOTE_CODE);
108         PARAMETERS_NEED_ESCAPE.add(ParamConstants.PARAM_LOCAL_JKS_KEYSTORE_CODE);
109         PARAMETERS_NEED_ESCAPE.add(ParamConstants.PARAM_LOCAL_JKS_KEYALIAS_CODE);
110     }
111 
112     /**
113      * list of hap signature optional blocks
114      */
115     protected List<SigningBlock> optionalBlocks = new ArrayList<SigningBlock>();
116 
117     /**
118      * parameters only used in signing
119      */
120     protected Map<String, String> signParams = new HashMap<String, String>();
121 
122     /**
123      * Read data of optional blocks from file user inputted.
124      *
125      * @throws InvalidParamsException Exception occurs when the input is invalid.
126      */
loadOptionalBlocks()127     protected void loadOptionalBlocks() throws InvalidParamsException {
128         String property = signParams.get(ParamConstants.PARAM_BASIC_PROPERTY);
129         loadOptionalBlock(property, HapUtils.HAP_PROPERTY_BLOCK_ID);
130 
131         String profile = signParams.get(ParamConstants.PARAM_BASIC_PROFILE);
132         loadOptionalBlock(profile, HapUtils.HAP_PROFILE_BLOCK_ID);
133 
134         String proofOfRotation = signParams.get(ParamConstants.PARAM_BASIC_PROOF);
135         loadOptionalBlock(proofOfRotation, HapUtils.HAP_PROOF_OF_ROTATION_BLOCK_ID);
136     }
137 
loadOptionalBlock(String file, int type)138     private void loadOptionalBlock(String file, int type) throws InvalidParamsException {
139         if (!checkStringIsNotNullAndEmity(file)) {
140             return;
141         }
142         if (!checkFile(file)) {
143             LOGGER.error("check file failed");
144             throw new InvalidParamsException("Invalid file: " + file + ", filetype: " + type);
145         }
146         try {
147             byte[] optionalBlockBytes = HapUtils.readFileToByte(file);
148             if (optionalBlockBytes == null || optionalBlockBytes.length <= 0) {
149                 LOGGER.warn("Optional block is null!");
150                 return;
151             }
152             optionalBlocks.add(new SigningBlock(type, optionalBlockBytes));
153         } catch (IOException e) {
154             LOGGER.error("read file error", e);
155             throw new InvalidParamsException("Invalid file: " + file + " is not readable. filetype: " + type);
156         }
157     }
158 
159     /**
160      * check if the input path is a file
161      *
162      * @param filePath input file path
163      * @return true, if path is a file and can be read
164      */
checkFile(String filePath)165     private boolean checkFile(String filePath) {
166         if (!(checkStringIsNotNullAndEmity(filePath))) {
167             LOGGER.error("fileName is null");
168             return false;
169         }
170         File file = new File(filePath);
171         if (!file.canRead() || !file.isFile()) {
172             LOGGER.error(filePath + " not exist or can not read!");
173             return false;
174         }
175         return true;
176     }
177 
checkStringIsNotNullAndEmity(String str)178     private boolean checkStringIsNotNullAndEmity(String str) {
179         return !(str == null || "".equals(str));
180     }
181 
182     /**
183      * Get certificate chain used to sign.
184      *
185      * @return list of x509 certificates.
186      */
getPublicCerts()187     private List<X509Certificate> getPublicCerts() {
188         String publicCertsFile = signParams.get(ParamConstants.PARAM_LOCAL_PUBLIC_CERT);
189         if (StringUtils.isEmpty(publicCertsFile)) {
190             return Collections.emptyList();
191         }
192         return getCertificateChainFromFile(publicCertsFile);
193     }
194 
195     /**
196      * get certificate revocation list used to sign
197      *
198      * @return certificate revocation list
199      */
getCrl()200     public Optional<X509CRL> getCrl() {
201         return Optional.empty();
202     }
203 
204     /**
205      * Create SignerConfig by certificate chain and certificate revocation list.
206      *
207      * @param certificates certificate chain
208      * @param crl certificate revocation list
209      * @param options options
210      * @return Object of SignerConfig
211      * @throws InvalidKeyException on error when the key is invalid.
212      */
createSignerConfigs(List<X509Certificate> certificates, Optional<X509CRL> crl, Options options)213     public SignerConfig createSignerConfigs(List<X509Certificate> certificates, Optional<X509CRL> crl, Options options)
214             throws InvalidKeyException {
215         SignerConfig signerConfig = new SignerConfig();
216         signerConfig.fillParameters(this.signParams);
217         signerConfig.setCertificates(certificates);
218         signerConfig.setOptions(options);
219 
220         List<SignatureAlgorithm> signatureAlgorithms = new ArrayList<SignatureAlgorithm>();
221         signatureAlgorithms.add(
222             ParamProcessUtil.getSignatureAlgorithm(this.signParams.get(ParamConstants.PARAM_BASIC_SIGANTURE_ALG)));
223         signerConfig.setSignatureAlgorithms(signatureAlgorithms);
224 
225         if (!crl.equals(Optional.empty())) {
226             signerConfig.setX509CRLs(Collections.singletonList(crl.get()));
227         }
228         return signerConfig;
229     }
230 
231     /**
232      * sign bin file
233      *
234      * @param options parameters used to sign bin file
235      * @return true, if sign successfully.
236      */
signBin(Options options)237     public boolean signBin(Options options) {
238         Security.addProvider(new BouncyCastleProvider());
239         List<X509Certificate> publicCert = null;
240         SignerConfig signerConfig;
241         try {
242             publicCert = getX509Certificates(options);
243 
244             // Get x509 CRL
245             Optional<X509CRL> crl = getCrl();
246 
247             // Create signer configs, which contains public cert and crl info.
248             signerConfig = createSignerConfigs(publicCert, crl, options);
249         } catch (InvalidKeyException | InvalidParamsException | MissingParamsException | ProfileException e) {
250             LOGGER.error("create signer configs failed.", e);
251             printErrorLogWithoutStack(e);
252             return false;
253         }
254 
255         /* 6. make signed file into output file. */
256         if (!SignBin.sign(signerConfig, signParams)) {
257             LOGGER.error("hap-sign-tool: error: Sign bin internal failed.");
258             return false;
259         }
260         LOGGER.info("Sign success");
261         return true;
262     }
263 
264     /**
265      * sign hap file
266      *
267      * @param options parameters used to sign hap file
268      * @return true, if sign successfully
269      */
sign(Options options)270     public boolean sign(Options options) {
271         Security.addProvider(new BouncyCastleProvider());
272         List<X509Certificate> publicCerts = null;
273         File output = null;
274         File tmpOutput = null;
275         boolean isRet = false;
276         boolean isPathOverlap = false;
277         try {
278             publicCerts = getX509Certificates(options);
279             checkCompatibleVersion();
280             File input = new File(signParams.get(ParamConstants.PARAM_BASIC_INPUT_FILE));
281             output = new File(signParams.get(ParamConstants.PARAM_BASIC_OUTPUT_FILE));
282             if (input.getCanonicalPath().equals(output.getCanonicalPath())) {
283                 tmpOutput = File.createTempFile("signedHap", ".hap");
284                 isPathOverlap = true;
285             } else {
286                 tmpOutput = output;
287             }
288             // copy file and Alignment
289             int alignment = Integer.parseInt(signParams.get(ParamConstants.PARAM_BASIC_ALIGNMENT));
290             copyFileAndAlignment(input, tmpOutput, alignment);
291             // generate sign block and output signedHap
292             try (RandomAccessFile outputHap = new RandomAccessFile(tmpOutput, "rw")) {
293                 ZipDataInput outputHapIn = new RandomAccessFileZipDataInput(outputHap);
294                 ZipFileInfo zipInfo = ZipUtils.findZipInfo(outputHapIn);
295                 long centralDirectoryOffset = zipInfo.getCentralDirectoryOffset();
296                 ZipDataInput beforeCentralDir = outputHapIn.slice(0, centralDirectoryOffset);
297                 ByteBuffer centralDirBuffer =
298                     outputHapIn.createByteBuffer(centralDirectoryOffset, zipInfo.getCentralDirectorySize());
299                 ZipDataInput centralDirectory = new ByteBufferZipDataInput(centralDirBuffer);
300 
301                 ByteBuffer eocdBuffer = zipInfo.getEocd();
302                 ZipDataInput eocd = new ByteBufferZipDataInput(eocdBuffer);
303 
304                 Optional<X509CRL> crl = getCrl();
305                 SignerConfig signerConfig = createSignerConfigs(publicCerts, crl, options);
306                 signerConfig.setCompatibleVersion(Integer.parseInt(
307                         signParams.get(ParamConstants.PARAM_BASIC_COMPATIBLE_VERSION)));
308                 ZipDataInput[] contents = {beforeCentralDir, centralDirectory, eocd};
309                 byte[] signingBlock = SignHap.sign(contents, signerConfig, optionalBlocks);
310                 long newCentralDirectoryOffset = centralDirectoryOffset + signingBlock.length;
311                 ZipUtils.setCentralDirectoryOffset(eocdBuffer, newCentralDirectoryOffset);
312                 LOGGER.info("Generate signing block success, begin write it to output file");
313 
314                 outputSignedFile(outputHap, centralDirectoryOffset, signingBlock, centralDirectory, eocdBuffer);
315                 isRet = true;
316             }
317         } catch (IOException | InvalidKeyException | HapFormatException | MissingParamsException
318             | InvalidParamsException | ProfileException | NumberFormatException | CustomException e) {
319             printErrorLogWithoutStack(e);
320         } catch (SignatureException e) {
321             printErrorLog(e);
322         }
323         return doAfterSign(isRet, isPathOverlap, tmpOutput, output);
324     }
325 
326     /**
327      * Load certificate chain from input parameters
328      *
329      * @param options parameters used to sign hap file
330      * @return list of type x509certificate
331      * @throws MissingParamsException Exception occurs when the required parameters are not entered.
332      * @throws InvalidParamsException Exception occurs when the required parameters are invalid.
333      * @throws ProfileException Exception occurs when profile is invalid.
334      */
getX509Certificates(Options options)335     private List<X509Certificate> getX509Certificates(Options options) throws MissingParamsException,
336             InvalidParamsException, ProfileException {
337         List<X509Certificate> publicCerts;
338         // 1. check the parameters
339         checkParams(options);
340         // 2. get x509 verify certificate
341         publicCerts = getPublicCerts();
342         // 3. load optionalBlocks
343         loadOptionalBlocks();
344         checkProfileValid(publicCerts);
345         return publicCerts;
346     }
347 
outputSignedFile(RandomAccessFile outputHap, long centralDirectoryOffset, byte[] signingBlock, ZipDataInput centralDirectory, ByteBuffer eocdBuffer)348     private void outputSignedFile(RandomAccessFile outputHap, long centralDirectoryOffset,
349         byte[] signingBlock, ZipDataInput centralDirectory, ByteBuffer eocdBuffer) throws IOException {
350         ZipDataOutput outputHapOut = new RandomAccessFileZipDataOutput(outputHap, centralDirectoryOffset);
351         outputHapOut.write(signingBlock, 0, signingBlock.length);
352         centralDirectory.copyTo(0, centralDirectory.size(), outputHapOut);
353         outputHapOut.write(eocdBuffer);
354     }
355 
doAfterSign(boolean isSuccess, boolean pathOverlap, File tmpOutput, File output)356     private boolean doAfterSign(boolean isSuccess, boolean pathOverlap, File tmpOutput, File output) {
357         boolean isRet = isSuccess;
358         if (isRet && pathOverlap) {
359             try {
360                 Files.move(tmpOutput.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING);
361             } catch (IOException e) {
362                 printErrorLog(e);
363                 isRet = false;
364             }
365         }
366 
367         if (isRet) {
368             LOGGER.info("Sign Hap success!");
369         } else {
370             FileUtils.deleteFile(tmpOutput);
371         }
372         return isRet;
373     }
374 
printErrorLog(Exception exception)375     private void printErrorLog(Exception exception) {
376         if (exception != null) {
377             LOGGER.error("hap-sign-tool: error: {}", exception.getMessage(), exception);
378         }
379     }
380 
printErrorLogWithoutStack(Exception exception)381     private void printErrorLogWithoutStack(Exception exception) {
382         if (exception != null) {
383             LOGGER.error("hap-sign-tool: error: {}", exception.getMessage());
384         }
385     }
386 
387     /**
388      * Copy file and alignment
389      *
390      * @param input file input
391      * @param tmpOutput file tmpOutput
392      * @param alignment alignment
393      * @throws IOException  io error
394      */
copyFileAndAlignment(File input, File tmpOutput, int alignment)395     private void copyFileAndAlignment(File input, File tmpOutput, int alignment) throws IOException {
396         try (JarFile inputJar = new JarFile(input, false);
397             FileOutputStream outputFile = new FileOutputStream(tmpOutput);
398             JarOutputStream outputJar = new JarOutputStream(outputFile)) {
399             long timestamp = TIMESTAMP;
400             timestamp -= TimeZone.getDefault().getOffset(timestamp);
401             outputJar.setLevel(COMPRESSION_MODE);
402             List<String> entryNames = SignHap.getEntryNamesFromHap(inputJar);
403             SignHap.copyFiles(entryNames, inputJar, outputJar, timestamp, alignment);
404         }
405     }
406 
407     /**
408      * check signature algorithm
409      *
410      * @throws InvalidParamsException Exception occurs when the inputted sign algorithm is invalid.
411      */
checkSignatureAlg()412     private void checkSignatureAlg() throws InvalidParamsException {
413         String signAlg = signParams.get(ParamConstants.PARAM_BASIC_SIGANTURE_ALG).trim();
414         for (String validAlg : VALID_SIGN_ALG_NAME) {
415             if (validAlg.equalsIgnoreCase(signAlg)) {
416                 return;
417             }
418         }
419         LOGGER.error("Unsupported signature algorithm :" + signAlg);
420         throw new InvalidParamsException("Invalid parameter: Sign Alg");
421     }
422 
423     /**
424      * check alignment
425      */
checkSignAlignment()426     protected void checkSignAlignment() {
427         if (!signParams.containsKey(ParamConstants.PARAM_BASIC_ALIGNMENT)) {
428             signParams.put(ParamConstants.PARAM_BASIC_ALIGNMENT, ParamConstants.ALIGNMENT);
429         }
430     }
431 
432     /**
433      * Get CN value of developer certificate from profile.
434      *
435      * @param buildInfoObject json obect of buildInfo in profile.
436      * @return Object of development-certificate.
437      */
getDevelopmentCertificate(JsonObject buildInfoObject)438     private X509Certificate getDevelopmentCertificate(JsonObject buildInfoObject) {
439         final String developmentCertElememt = "development-certificate";
440         String developmentCertificate = buildInfoObject.get(developmentCertElememt).getAsString();
441         return DigestUtils.decodeBase64ToX509Certifate(developmentCertificate);
442     }
443 
444     /**
445      * Get CN value of release certificate from profile.
446      *
447      * @param buildInfoObject json obect of buildInfo in profile.
448      * @return Object of distribution-certificate.
449      */
getReleaseCertificate(JsonObject buildInfoObject)450     private X509Certificate getReleaseCertificate(JsonObject buildInfoObject) {
451         final String distributeCertElememt = "distribution-certificate";
452         String distributeCertificate = buildInfoObject.get(distributeCertElememt).getAsString();
453         return DigestUtils.decodeBase64ToX509Certifate(distributeCertificate);
454     }
455 
getCertificateCN(X509Certificate cert)456     private String getCertificateCN(X509Certificate cert) {
457         if (cert == null) {
458             return "";
459         }
460         String valueOfDN = cert.getSubjectDN().toString();
461         valueOfDN = valueOfDN.replace("\"", "");
462         String[] arrayDN = valueOfDN.split(",");
463         for (String element : arrayDN) {
464             if (element.trim().startsWith("CN=")) {
465                 return element.split("=")[1];
466             }
467         }
468         return "";
469     }
470 
findProfileFromOptionalBlocks()471     private byte[] findProfileFromOptionalBlocks() {
472         byte[] profile = new byte[0];
473         for (SigningBlock optionalBlock : optionalBlocks) {
474             if (optionalBlock.getType() == HapUtils.HAP_PROFILE_BLOCK_ID) {
475                 profile = optionalBlock.getValue();
476             }
477         }
478         return profile;
479     }
480 
481     /**
482      * Check profile is valid. A valid profile must include type and
483      * certificate which has a non-empty value of DN.
484      *
485      * @param inputCerts certificates inputted by user.
486      * @throws ProfileException Exception occurs when profile is invalid.
487      */
checkProfileValid(List<X509Certificate> inputCerts)488     private void checkProfileValid(List<X509Certificate> inputCerts) throws ProfileException {
489         try {
490             byte[] profile = findProfileFromOptionalBlocks();
491             boolean isProfileWithoutSign = ParamConstants.ProfileSignFlag.UNSIGNED_PROFILE.getSignFlag().equals(
492                     signParams.get(ParamConstants.PARAM_BASIC_PROFILE_SIGNED));
493             String content;
494             if (!isProfileWithoutSign) {
495                 CMSSignedData cmsSignedData = new CMSSignedData(profile);
496                 boolean isVerify = VerifyUtils.verifyCmsSignedData(cmsSignedData);
497                 if (!isVerify) {
498                     throw new ProfileException("Verify profile pkcs7 failed! Profile is invalid.");
499                 }
500                 Object contentObj = cmsSignedData.getSignedContent().getContent();
501                 if (!(contentObj instanceof byte[])) {
502                     throw new ProfileException("Check profile failed, signed profile content is not byte array!");
503                 }
504                 content = new String((byte[]) contentObj, StandardCharsets.UTF_8);
505             } else {
506                 content = new String(profile, StandardCharsets.UTF_8);
507             }
508             JsonElement parser = JsonParser.parseString(content);
509             JsonObject profileJson = parser.getAsJsonObject();
510             checkProfileInfo(profileJson, inputCerts);
511         } catch (CMSException e) {
512             throw new ProfileException("Verify profile pkcs7 failed! Profile is invalid.", e);
513         } catch (JsonParseException e) {
514             throw new ProfileException("Invalid parameter: profile content is not a JSON.", e);
515         }
516     }
517 
checkProfileInfo(JsonObject profileJson, List<X509Certificate> inputCerts)518     private void checkProfileInfo(JsonObject profileJson, List<X509Certificate> inputCerts) throws ProfileException {
519         String profileTypeKey = "type";
520         String profileType = profileJson.get(profileTypeKey).getAsString();
521         if (profileType == null || profileType.length() == 0) {
522             throw new ProfileException("Get profile type error!");
523         }
524         String buildInfoMember = "bundle-info";
525         JsonObject buildInfoObject = profileJson.getAsJsonObject(buildInfoMember);
526         X509Certificate certInProfile;
527         if (profileType.equalsIgnoreCase("release")) {
528             certInProfile = getReleaseCertificate(buildInfoObject);
529         } else if (profileType.equalsIgnoreCase("debug")) {
530             certInProfile = getDevelopmentCertificate(buildInfoObject);
531         } else {
532             throw new ProfileException("Unsupported profile type!");
533         }
534         if (!inputCerts.isEmpty() && !checkInputCertMatchWithProfile(inputCerts.get(0), certInProfile)) {
535                 throw new ProfileException("input certificates do not match with profile!");
536         }
537         String cn = getCertificateCN(certInProfile);
538         LOGGER.info("certificate in profile: {}", cn);
539         if (cn.isEmpty()) {
540             throw new ProfileException("Common name of certificate is empty!");
541         }
542     }
543 
544     /**
545      * check whether certificate inputted by user is matched with the certificate in profile.
546      *
547      * @param inputCert certificates inputted by user.
548      * @param certInProfile the certificate in profile.
549      * @return true, if it is match.
550      */
checkInputCertMatchWithProfile(X509Certificate inputCert, X509Certificate certInProfile)551     protected boolean checkInputCertMatchWithProfile(X509Certificate inputCert, X509Certificate certInProfile) {
552         return true;
553     }
554 
555     /**
556      * Check input parameters is valid. And put valid parameters into signParams.
557      *
558      * @param options parameters inputted by user.
559      * @throws MissingParamsException Exception occurs when the required parameters are not entered.
560      * @throws InvalidParamsException Exception occurs when the required parameters are invalid.
561      */
checkParams(Options options)562     public void checkParams(Options options) throws MissingParamsException, InvalidParamsException {
563         String[] paramFileds = {
564             ParamConstants.PARAM_BASIC_ALIGNMENT,
565             ParamConstants.PARAM_BASIC_SIGANTURE_ALG,
566             ParamConstants.PARAM_BASIC_INPUT_FILE,
567             ParamConstants.PARAM_BASIC_OUTPUT_FILE,
568             ParamConstants.PARAM_BASIC_PRIVATE_KEY,
569             ParamConstants.PARAM_BASIC_PROFILE,
570             ParamConstants.PARAM_BASIC_PROOF,
571             ParamConstants.PARAM_BASIC_PROPERTY,
572             ParamConstants.PARAM_REMOTE_SERVER,
573             ParamConstants.PARAM_BASIC_PROFILE_SIGNED,
574             ParamConstants.PARAM_LOCAL_PUBLIC_CERT,
575             ParamConstants.PARAM_BASIC_COMPATIBLE_VERSION
576         };
577         Set<String> paramSet = ParamProcessUtil.initParamField(paramFileds);
578 
579         for (String paramKey : options.keySet()) {
580             if (paramSet.contains(paramKey)) {
581                 signParams.put(paramKey, getParamValue(paramKey, options.getString(paramKey)));
582             }
583         }
584         if (!signParams.containsKey(ParamConstants.PARAM_BASIC_PROFILE_SIGNED)) {
585             signParams.put(ParamConstants.PARAM_BASIC_PROFILE_SIGNED, "1");
586         }
587         checkSignatureAlg();
588         checkSignAlignment();
589     }
590 
591     /**
592      * Check compatible version, if param do not have compatible version default 9.
593      *
594      * @throws InvalidParamsException invalid param
595      * @throws MissingParamsException missing param
596      */
checkCompatibleVersion()597     protected void checkCompatibleVersion() throws InvalidParamsException, MissingParamsException {
598         if (!signParams.containsKey(ParamConstants.PARAM_BASIC_COMPATIBLE_VERSION)) {
599             signParams.put(ParamConstants.PARAM_BASIC_COMPATIBLE_VERSION, "9");
600             return;
601         }
602         String compatibleApiVersionVal = signParams.get(ParamConstants.PARAM_BASIC_COMPATIBLE_VERSION);
603         try {
604             int compatibleApiVersion = Integer.parseInt(compatibleApiVersionVal);
605         } catch (NumberFormatException e) {
606             throw new InvalidParamsException("Invalid parameter: " + ParamConstants.PARAM_BASIC_COMPATIBLE_VERSION);
607         }
608     }
609 
610     /**
611      * Get parameters from inputted strings. This function unescape some escaped parameters and return it.
612      *
613      * @param paramName the name of parameter
614      * @param paramValue the value of parameter
615      * @return parameter value in the correct form.
616      */
getParamValue(String paramName, String paramValue)617     protected String getParamValue(String paramName, String paramValue) {
618         for (String name : PARAMETERS_NEED_ESCAPE) {
619             if (name.equals(paramName)) {
620                 return EscapeCharacter.unescape(paramValue);
621             }
622         }
623         return paramValue;
624     }
625 
getCertificateChainFromFile(String certChianFile)626     private List<X509Certificate> getCertificateChainFromFile(String certChianFile) {
627         try {
628             return CertificateUtils.getCertListFromFile(certChianFile);
629         } catch (CertificateException e) {
630             LOGGER.error("File content is not certificates! " + e.getMessage());
631         } catch (IOException e) {
632             LOGGER.error("Certificate file exception: " + e.getMessage());
633         } catch (VerifyCertificateChainException e) {
634             LOGGER.error(e.getMessage());
635         }
636         return Collections.emptyList();
637     }
638 }
639