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