• 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.api;
17 
18 import com.ohos.hapsigntool.api.model.Options;
19 import com.ohos.hapsigntool.error.CustomException;
20 import com.ohos.hapsigntool.error.ERROR;
21 import com.ohos.hapsigntool.hap.exception.VerifyCertificateChainException;
22 import com.ohos.hapsigntool.key.KeyPairTools;
23 import com.ohos.hapsigntool.keystore.KeyStoreHelper;
24 import com.ohos.hapsigntool.utils.CertUtils;
25 import com.ohos.hapsigntool.utils.FileUtils;
26 import com.ohos.hapsigntool.utils.StringUtils;
27 import com.ohos.hapsigntool.utils.ValidateUtils;
28 import org.apache.logging.log4j.LogManager;
29 import org.apache.logging.log4j.Logger;
30 import org.bouncycastle.asn1.x500.X500Name;
31 import org.bouncycastle.asn1.x509.KeyPurposeId;
32 import org.bouncycastle.asn1.x509.KeyUsage;
33 
34 import java.io.File;
35 import java.io.IOException;
36 import java.security.KeyPair;
37 import java.security.cert.CertificateException;
38 import java.security.cert.X509Certificate;
39 import java.util.Arrays;
40 import java.util.List;
41 
42 /**
43  * Localization adapter.
44  *
45  * @since 2021/12/28
46  */
47 public class LocalizationAdapter {
48     /**
49      * Check cert chain size
50      */
51     private static final int MIN_CERT_CHAIN_SIZE = 2;
52     private static final int MAX_CERT_CHAIN_SIZE = 3;
53 
54     /**
55      * Logger
56      */
57     private static final Logger logger = LogManager.getLogger(LocalizationAdapter.class);
58 
59     /**
60      * Params
61      */
62     private final Options options;
63 
64     /**
65      * Operation of keystore
66      */
67     private KeyStoreHelper keyStoreHelper;
68 
69     /**
70      * Judge whether IssuerKeyStoreFile exists
71      */
72     private  boolean isIssuerKeyStoreFile = false ;
73 
74     /**
75      * Constructor of LocalizationAdapter.
76      *
77      * @param options options
78      */
LocalizationAdapter(Options options)79     public LocalizationAdapter(Options options) {
80         this.options = options;
81     }
82 
83     /**
84      * Set keyStoreHelper
85      * @param keyStoreHelper keyStoreHelper
86      */
setKeyStoreHelper(KeyStoreHelper keyStoreHelper)87     public void setKeyStoreHelper(KeyStoreHelper keyStoreHelper) {
88         this.keyStoreHelper = keyStoreHelper;
89     }
90 
91     /**
92      * Set issuerKeyStoreFile
93      * @param issuerKeyStoreFile issuerKeyStoreFile
94      */
setIssuerKeyStoreFile(boolean issuerKeyStoreFile)95     public void setIssuerKeyStoreFile(boolean issuerKeyStoreFile) {
96         this.isIssuerKeyStoreFile = issuerKeyStoreFile;
97     }
98 
99     /**
100      * Get options.
101      *
102      * @return options
103      */
getOptions()104     public Options getOptions() {
105         return options;
106     }
107 
initKeyStore()108     private void initKeyStore() {
109         // Avoid duplicated initialization
110         if (keyStoreHelper != null) {
111             return;
112         }
113 
114         String keyStore ;
115         if (this.isIssuerKeyStoreFile){
116             keyStore = options.getString(Options.ISSUER_KEY_STORE_FILE, "");
117             keyStoreHelper = new KeyStoreHelper(keyStore, options.getChars(Options.ISSUER_KEY_STORE_RIGHTS));
118         }else {
119             keyStore =  options.getString(Options.KEY_STORE_FILE, "");
120             keyStoreHelper = new KeyStoreHelper(keyStore, options.getChars(Options.KEY_STORE_RIGHTS));
121         }
122         this.setIssuerKeyStoreFile(false);
123     }
124 
125     /**
126      * Get KeyPair through key alias and password.
127      *
128      * @param autoCreate autoCreate
129      * @return keyPair
130      */
getAliasKey(boolean autoCreate)131     public KeyPair getAliasKey(boolean autoCreate) {
132         return getKeyPair(options.getString(Options.KEY_ALIAS),
133                 options.getChars(Options.KEY_RIGHTS), autoCreate);
134     }
135 
136     /**
137      * Get the alias of issuer key.
138      *
139      * @return param of issuer alias key .
140      */
getIssuerAliasKey()141     public KeyPair getIssuerAliasKey() {
142         return getKeyPair(options.getString(Options.ISSUER_KEY_ALIAS),
143                 options.getChars(Options.ISSUER_KEY_RIGHTS), false);
144     }
145 
146     /**
147      * Check whether the keystore has alias or not.
148      *
149      * @param alias alias of key
150      * @return true or false
151      */
hasAlias(String alias)152     public boolean hasAlias(String alias) {
153         if (keyStoreHelper == null) {
154             initKeyStore();
155         }
156         return keyStoreHelper.hasAlias(alias);
157     }
158 
159     /**
160      * Error if not exist.
161      *
162      * @param alias alias
163      */
errorIfNotExist(String alias)164     public void errorIfNotExist(String alias) {
165         if (keyStoreHelper == null) {
166             initKeyStore();
167         }
168         keyStoreHelper.errorIfNotExist(alias);
169     }
170 
171     /**
172      * Error on exist.
173      *
174      * @param alias alias
175      */
errorOnExist(String alias)176     public void errorOnExist(String alias) {
177         if (keyStoreHelper == null) {
178             initKeyStore();
179         }
180         keyStoreHelper.errorOnExist(alias);
181     }
182 
getKeyPair(String alias, char[] keyPwd, boolean autoCreate)183     private KeyPair getKeyPair(String alias, char[] keyPwd, boolean autoCreate) {
184         if (keyStoreHelper == null) {
185             initKeyStore();
186         }
187         ValidateUtils.throwIfNotMatches(!StringUtils.isEmpty(alias), ERROR.ACCESS_ERROR, "Alias could not be empty");
188         KeyPair keyPair = null;
189         if (keyStoreHelper.hasAlias(alias)) {
190             keyPair = keyStoreHelper.loadKeyPair(alias, keyPwd);
191         } else {
192             if (autoCreate) {
193                 options.required(Options.KEY_ALG, Options.KEY_SIZE);
194                 keyPair = KeyPairTools.generateKeyPair(options.getString(Options.KEY_ALG),
195                         options.getInt(Options.KEY_SIZE));
196                 keyStoreHelper.store(alias, keyPwd, keyPair, null);
197             }
198         }
199         ValidateUtils.throwIfNotMatches(keyPair != null, ERROR.PARAM_NOT_EXIST_ERROR,
200                 String.format("%s: '%s' is not exist in %s", Options.KEY_ALIAS, alias,
201                         keyStoreHelper.getKeyStorePath()));
202         return keyPair;
203     }
204 
205     /**
206      * Get profile cert.
207      *
208      * @return profile cert.
209      */
getSignCertChain()210     public List<X509Certificate> getSignCertChain() {
211         String certPath = options.getString(Options.PROFILE_CERT_FILE);
212         if (StringUtils.isEmpty(certPath)) {
213             certPath = options.getString(Options.APP_CERT_FILE);
214         }
215         List<X509Certificate> certificates = getCertsFromFile(certPath, Options.PROFILE_CERT_FILE);
216 
217         ValidateUtils.throwIfNotMatches(
218                 certificates.size() >= MIN_CERT_CHAIN_SIZE && certificates.size() <= MAX_CERT_CHAIN_SIZE,
219                 ERROR.NOT_SUPPORT_ERROR, String.format("Profile cert '%s' must a cert chain", certPath)
220         );
221         return certificates;
222     }
223 
224     /**
225      * Get the cert file of sub ca.
226      *
227      * @return the cert file of sub ca.
228      */
getSubCaCertFile()229     public X509Certificate getSubCaCertFile() {
230         String certPath = options.getString(Options.SUB_CA_CERT_FILE);
231         return getCertsFromFile(certPath, Options.SUB_CA_CERT_FILE).get(0);
232     }
233 
234     /**
235      * Get the cert file of root ca.
236      *
237      * @return the cert file of root ca.
238      */
getCaCertFile()239     public X509Certificate getCaCertFile() {
240         String certPath = options.getString(Options.CA_CERT_FILE);
241         return getCertsFromFile(certPath, Options.CA_CERT_FILE).get(0);
242     }
243 
244     /**
245      * Check whether the form is cert chain.
246      *
247      * @return result indicating whether the form is cert chain.
248      */
isOutFormChain()249     public boolean isOutFormChain() {
250         String outForm = options.getString(Options.OUT_FORM, "certChain");
251         return "certChain".equals(outForm);
252     }
253 
254     /**
255      * Get certificates from file.
256      *
257      * @param certPath the path of cert
258      * @param logTitle log title
259      * @return certificates
260      */
getCertsFromFile(String certPath, String logTitle)261     public List<X509Certificate> getCertsFromFile(String certPath, String logTitle) {
262         ValidateUtils.throwIfNotMatches(!StringUtils.isEmpty(certPath), ERROR.PARAM_NOT_EXIST_ERROR,
263                 String.format("Params '%s' not exist", logTitle));
264 
265         File certFile = new File(certPath);
266         ValidateUtils.throwIfNotMatches(certFile.exists(), ERROR.FILE_NOT_FOUND,
267                 String.format("%s: '%s' not exist", logTitle, certPath));
268         List<X509Certificate> certificates = null;
269         try {
270             certificates = CertUtils.generateCertificates(FileUtils.readFile(certFile));
271         } catch (IOException | CertificateException | VerifyCertificateChainException exception) {
272             logger.debug(exception.getMessage(), exception);
273             CustomException.throwException(ERROR.ACCESS_ERROR, exception.getMessage());
274         }
275         ValidateUtils.throwIfNotMatches(certificates != null && certificates.size() > 0, ERROR.READ_FILE_ERROR,
276                 String.format("Read fail from %s, bot found certificates", certPath));
277         return certificates;
278     }
279 
280     /**
281      * Get signature algorithm.
282      *
283      * @return signature algorithm.
284      */
getSignAlg()285     public String getSignAlg() {
286         return options.getString(Options.SIGN_ALG);
287     }
288 
289     /**
290      * Check whether the key usage is critical.
291      *
292      * @return result indicating whether the key usage is critical.
293      */
isKeyUsageCritical()294     public boolean isKeyUsageCritical() {
295         return options.getBoolean(Options.KEY_USAGE_CRITICAL, true);
296     }
297 
298     /**
299      * Check whether the external key usage is critical.
300      *
301      * @return result indicating whether the external key usage is critical.
302      */
isExtKeyUsageCritical()303     public boolean isExtKeyUsageCritical() {
304         return options.getBoolean(Options.EXT_KEY_USAGE_CRITICAL, true);
305     }
306 
307     /**
308      * Check whether the basic constraints is ca.
309      *
310      * @return result indicating whether the basic constraints is ca.
311      */
isBasicConstraintsCa()312     public boolean isBasicConstraintsCa() {
313         return options.getBoolean(Options.BASIC_CONSTRAINTS_CA, false);
314     }
315 
316     /**
317      * Check whether the basic constraints is critical.
318      *
319      * @return result indicating whether the basic constraints is critical.
320      */
isBasicConstraintsCritical()321     public boolean isBasicConstraintsCritical() {
322         return options.getBoolean(Options.BASIC_CONSTRAINTS_CRITICAL, false);
323     }
324 
325     /**
326      * Get the path length of basic constraints.
327      *
328      * @return the path length of basic constraints.
329      */
getBasicConstraintsPathLen()330     public int getBasicConstraintsPathLen() {
331         return options.getInt(Options.BASIC_CONSTRAINTS_PATH_LEN);
332     }
333 
334     /**
335      * Get the external key usage.
336      *
337      * @return KeyPurposeId[] of ExtKeyUsage.
338      */
getExtKeyUsage()339     public KeyPurposeId[] getExtKeyUsage() {
340         return CertUtils.parseExtKeyUsage(options.getString(Options.EXT_KEY_USAGE));
341     }
342 
343     /**
344      * Get the key usage.
345      *
346      * @return the key usage.
347      */
getKeyUsage()348     public KeyUsage getKeyUsage() {
349         return new KeyUsage(CertUtils.parseKeyUsage(options.getString(Options.KEY_USAGE)));
350     }
351 
352     /**
353      * Get the subject of cert.
354      *
355      * @return the subject of cert.
356      */
getSubject()357     public X500Name getSubject() {
358         String subject = options.getString(Options.SUBJECT);
359         return CertUtils.buildDN(subject);
360     }
361 
362     /**
363      * Get the subject of issuer.
364      *
365      * @return the subject of issuer.
366      */
getIssuer()367     public X500Name getIssuer() {
368         String issuer = options.getString(Options.ISSUER, options.getString(Options.SUBJECT));
369         return CertUtils.buildDN(issuer);
370     }
371 
372     /**
373      * Get the output file.
374      *
375      * @return the string of output file.
376      */
getOutFile()377     public String getOutFile() {
378         return options.getString(Options.OUT_FILE);
379     }
380 
381     /**
382      * Get the input file.
383      *
384      * @return the string of input file.
385      */
getInFile()386     public String getInFile() {
387         String file = options.getString(Options.IN_FILE);
388         ValidateUtils.throwIfNotMatches(new File(file).exists(), ERROR.FILE_NOT_FOUND,
389                 String.format("Required %s: '%s' not exist", Options.IN_FILE, file));
390         return file;
391     }
392 
393     /**
394      * Check if it is a remote signature.
395      *
396      * @return result indicating whether the signer is a remote signer.
397      */
isRemoteSigner()398     public boolean isRemoteSigner() {
399         String mode = options.getString(Options.MODE, "localSign");
400         return "remoteSign".equalsIgnoreCase(mode);
401     }
402 
403     /**
404      * Reset the password to ensure security.
405      */
releasePwd()406     public void releasePwd() {
407         resetChars(options.getChars(Options.KEY_STORE_RIGHTS));
408         resetChars(options.getChars(Options.KEY_RIGHTS));
409         resetChars(options.getChars(Options.ISSUER_KEY_RIGHTS));
410         resetChars(options.getChars(Options.ISSUER_KEY_STORE_RIGHTS));
411     }
412 
resetChars(char[] chars)413     private void resetChars(char[] chars) {
414         if (chars == null) {
415             return;
416         }
417         Arrays.fill(chars, (char) 0);
418     }
419 }
420