• 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.SignToolErrMsg;
22 import com.ohos.hapsigntool.error.VerifyCertificateChainException;
23 import com.ohos.hapsigntool.utils.KeyPairTools;
24 import com.ohos.hapsigntool.utils.KeyStoreHelper;
25 import com.ohos.hapsigntool.utils.CertUtils;
26 import com.ohos.hapsigntool.utils.FileUtils;
27 import com.ohos.hapsigntool.utils.LogUtils;
28 import com.ohos.hapsigntool.utils.StringUtils;
29 import com.ohos.hapsigntool.utils.ValidateUtils;
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 LogUtils LOGGER = new LogUtils(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,
190                 SignToolErrMsg.PARAM_VALUE_EMPTY.toString("KeyAlias"));
191         KeyPair keyPair = null;
192         if (keyStoreHelper.hasAlias(alias)) {
193             keyPair = keyStoreHelper.loadKeyPair(alias, keyPwd);
194         } else {
195             if (autoCreate) {
196                 options.required(Options.KEY_ALG, Options.KEY_SIZE);
197                 keyPair = KeyPairTools.generateKeyPair(options.getString(Options.KEY_ALG),
198                         options.getInt(Options.KEY_SIZE));
199                 keyStoreHelper.store(alias, keyPwd, keyPair, null);
200             }
201         }
202         ValidateUtils.throwIfNotMatches(keyPair != null, ERROR.PARAM_NOT_EXIST_ERROR,
203                 SignToolErrMsg.KEY_ALIAS_NOT_FOUND.toString(alias, 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, SignToolErrMsg.CERT_CHAIN_FORMAT_FAILED.toString(certPath));
222         return certificates;
223     }
224 
225     /**
226      * Get the cert file of sub ca.
227      *
228      * @return the cert file of sub ca.
229      */
getSubCaCertFile()230     public X509Certificate getSubCaCertFile() {
231         String certPath = options.getString(Options.SUB_CA_CERT_FILE);
232         return getCertsFromFile(certPath, Options.SUB_CA_CERT_FILE).get(0);
233     }
234 
235     /**
236      * Get the cert file of root ca.
237      *
238      * @return the cert file of root ca.
239      */
getCaCertFile()240     public X509Certificate getCaCertFile() {
241         String certPath = options.getString(Options.CA_CERT_FILE);
242         return getCertsFromFile(certPath, Options.CA_CERT_FILE).get(0);
243     }
244 
245     /**
246      * Check whether the form is cert chain.
247      *
248      * @return result indicating whether the form is cert chain.
249      */
isOutFormChain()250     public boolean isOutFormChain() {
251         String outForm = options.getString(Options.OUT_FORM, "certChain");
252         return "certChain".equals(outForm);
253     }
254 
255     /**
256      * Get certificates from file.
257      *
258      * @param certPath the path of cert
259      * @param logTitle log title
260      * @return certificates
261      */
getCertsFromFile(String certPath, String logTitle)262     public List<X509Certificate> getCertsFromFile(String certPath, String logTitle) {
263         ValidateUtils.throwIfNotMatches(!StringUtils.isEmpty(certPath), ERROR.PARAM_NOT_EXIST_ERROR,
264                 SignToolErrMsg.FILE_NOT_EXIST.toString(logTitle));
265 
266         File certFile = new File(certPath);
267         ValidateUtils.throwIfNotMatches(certFile.exists(), ERROR.FILE_NOT_FOUND,
268                 SignToolErrMsg.FILE_NOT_EXIST.toString(certPath));
269         List<X509Certificate> certificates = null;
270         try {
271             certificates = CertUtils.generateCertificates(FileUtils.readFile(certFile));
272         } catch (IOException | CertificateException | VerifyCertificateChainException exception) {
273             LOGGER.debug(exception.getMessage(), exception);
274             CustomException.throwException(ERROR.ACCESS_ERROR, SignToolErrMsg.CERT_FORMAT_FAILED
275                     .toString(exception.getMessage()));
276         }
277         ValidateUtils.throwIfNotMatches(certificates != null && !certificates.isEmpty(), ERROR.READ_FILE_ERROR,
278                 SignToolErrMsg.CERT_FORMAT_FAILED.toString("can not found certificates in file " + certPath));
279         return certificates;
280     }
281 
282     /**
283      * Get signature algorithm.
284      *
285      * @return signature algorithm.
286      */
getSignAlg()287     public String getSignAlg() {
288         return options.getString(Options.SIGN_ALG);
289     }
290 
291     /**
292      * Check whether the key usage is critical.
293      *
294      * @return result indicating whether the key usage is critical.
295      */
isKeyUsageCritical()296     public boolean isKeyUsageCritical() {
297         return options.getBoolean(Options.KEY_USAGE_CRITICAL, true);
298     }
299 
300     /**
301      * Check whether the external key usage is critical.
302      *
303      * @return result indicating whether the external key usage is critical.
304      */
isExtKeyUsageCritical()305     public boolean isExtKeyUsageCritical() {
306         return options.getBoolean(Options.EXT_KEY_USAGE_CRITICAL, true);
307     }
308 
309     /**
310      * Check whether the basic constraints is ca.
311      *
312      * @return result indicating whether the basic constraints is ca.
313      */
isBasicConstraintsCa()314     public boolean isBasicConstraintsCa() {
315         return options.getBoolean(Options.BASIC_CONSTRAINTS_CA, false);
316     }
317 
318     /**
319      * Check whether the basic constraints is critical.
320      *
321      * @return result indicating whether the basic constraints is critical.
322      */
isBasicConstraintsCritical()323     public boolean isBasicConstraintsCritical() {
324         return options.getBoolean(Options.BASIC_CONSTRAINTS_CRITICAL, false);
325     }
326 
327     /**
328      * Get the path length of basic constraints.
329      *
330      * @return the path length of basic constraints.
331      */
getBasicConstraintsPathLen()332     public int getBasicConstraintsPathLen() {
333         return options.getInt(Options.BASIC_CONSTRAINTS_PATH_LEN);
334     }
335 
336     /**
337      * Get the external key usage.
338      *
339      * @return KeyPurposeId[] of ExtKeyUsage.
340      */
getExtKeyUsage()341     public KeyPurposeId[] getExtKeyUsage() {
342         return CertUtils.parseExtKeyUsage(options.getString(Options.EXT_KEY_USAGE));
343     }
344 
345     /**
346      * Get the key usage.
347      *
348      * @return the key usage.
349      */
getKeyUsage()350     public KeyUsage getKeyUsage() {
351         return new KeyUsage(CertUtils.parseKeyUsage(options.getString(Options.KEY_USAGE)));
352     }
353 
354     /**
355      * Get the subject of cert.
356      *
357      * @return the subject of cert.
358      */
getSubject()359     public X500Name getSubject() {
360         String subject = options.getString(Options.SUBJECT);
361         return CertUtils.buildDN(subject);
362     }
363 
364     /**
365      * Get the subject of issuer.
366      *
367      * @return the subject of issuer.
368      */
getIssuer()369     public X500Name getIssuer() {
370         String issuer = options.getString(Options.ISSUER, options.getString(Options.SUBJECT));
371         return CertUtils.buildDN(issuer);
372     }
373 
374     /**
375      * Get the output file.
376      *
377      * @return the string of output file.
378      */
getOutFile()379     public String getOutFile() {
380         return options.getString(Options.OUT_FILE);
381     }
382 
383     /**
384      * Get the input file.
385      *
386      * @return the string of input file.
387      */
getInFile()388     public String getInFile() {
389         String file = options.getString(Options.IN_FILE);
390         ValidateUtils.throwIfNotMatches(new File(file).exists(),
391                 ERROR.FILE_NOT_FOUND, SignToolErrMsg.FILE_NOT_EXIST.toString(file));
392         return file;
393     }
394 
395     /**
396      * Check if it is a remote signature.
397      *
398      * @return result indicating whether the signer is a remote signer.
399      */
isRemoteSigner()400     public boolean isRemoteSigner() {
401         String mode = options.getString(Options.MODE, "localSign");
402         return "remoteSign".equalsIgnoreCase(mode);
403     }
404 
405     /**
406      * Reset the password to ensure security.
407      */
releasePwd()408     public void releasePwd() {
409         resetChars(options.getChars(Options.KEY_STORE_RIGHTS));
410         resetChars(options.getChars(Options.KEY_RIGHTS));
411         resetChars(options.getChars(Options.ISSUER_KEY_RIGHTS));
412         resetChars(options.getChars(Options.ISSUER_KEY_STORE_RIGHTS));
413     }
414 
resetChars(char[] chars)415     private void resetChars(char[] chars) {
416         if (chars == null) {
417             return;
418         }
419         Arrays.fill(chars, (char) 0);
420     }
421 }
422