• 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.api;
17 
18 import com.google.gson.JsonParser;
19 import com.ohos.hapsigntool.entity.Options;
20 import com.ohos.hapsigntool.adapter.LocalizationAdapter;
21 import com.ohos.hapsigntool.error.CustomException;
22 import com.ohos.hapsigntool.error.ERROR;
23 import com.ohos.hapsigntool.error.VerifyException;
24 import com.ohos.hapsigntool.hap.provider.LocalJKSSignProvider;
25 import com.ohos.hapsigntool.hap.provider.RemoteSignProvider;
26 import com.ohos.hapsigntool.hap.provider.SignProvider;
27 import com.ohos.hapsigntool.hap.verify.VerifyElf;
28 import com.ohos.hapsigntool.hap.verify.VerifyHap;
29 import com.ohos.hapsigntool.profile.ProfileSignTool;
30 import com.ohos.hapsigntool.profile.VerifyHelper;
31 import com.ohos.hapsigntool.profile.model.Provision;
32 import com.ohos.hapsigntool.profile.model.VerificationResult;
33 import com.ohos.hapsigntool.utils.CertUtils;
34 import com.ohos.hapsigntool.utils.FileUtils;
35 import com.ohos.hapsigntool.entity.ParamConstants;
36 import com.ohos.hapsigntool.utils.StringUtils;
37 
38 import org.apache.logging.log4j.LogManager;
39 import org.apache.logging.log4j.Logger;
40 import org.bouncycastle.jce.provider.BouncyCastleProvider;
41 
42 import java.io.File;
43 import java.io.IOException;
44 import java.nio.charset.StandardCharsets;
45 import java.security.KeyPair;
46 import java.security.Security;
47 import java.security.cert.CertificateException;
48 import java.security.cert.X509Certificate;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.List;
52 
53 /**
54  * Main entry of lib.
55  *
56  * @since 2021/12/28
57  */
58 public class SignToolServiceImpl implements ServiceApi {
59     /**
60      * App signing Capabilty Bytes.
61      */
62     private static final byte[] APP_SIGNING_CAPABILITY = {0x30, 0x06, 0x02, 0x01, 0x01, 0x0A, 0x01, 0x00};
63 
64     /**
65      * Profile signing Capabilty Bytes.
66      */
67     private static final byte[] PROFILE_SIGNING_CAPABILITY = {0x30, 0x06, 0x02, 0x01, 0x01, 0x0A, 0x01, 0x01};
68 
69     /**
70      * Logger.
71      */
72     private static final Logger logger = LogManager.getLogger(ServiceApi.class);
73 
74     static {
Security.addProvider(new BouncyCastleProvider())75         Security.addProvider(new BouncyCastleProvider());
76     }
77 
78     /**
79      * Generate keyStore.
80      *
81      * @param options options
82      * @return Generate or not
83      */
84     @Override
generateKeyStore(Options options)85     public boolean generateKeyStore(Options options) {
86         LocalizationAdapter adapter = new LocalizationAdapter(options);
87         adapter.errorOnExist(options.getString(Options.KEY_ALIAS));
88         KeyPair keyPair = adapter.getAliasKey(true);
89         adapter.releasePwd();
90         return keyPair != null;
91     }
92 
93     /**
94      * Generate csr.
95      *
96      * @param options options
97      * @return Generate or not
98      */
99     @Override
generateCsr(Options options)100     public boolean generateCsr(Options options) {
101         LocalizationAdapter adapter = new LocalizationAdapter(options);
102         KeyPair keyPair = adapter.getAliasKey(false);
103         adapter.releasePwd();
104         byte[] csr = CertTools.generateCsr(keyPair, adapter.getSignAlg(), adapter.getSubject());
105         if (csr == null) {
106             return false;
107         }
108         String csrContent = CertUtils.toCsrTemplate(csr);
109         outputString(csrContent, adapter.getOutFile());
110         return true;
111     }
112 
113     /**
114      * Generate cert.
115      *
116      * @param options options
117      * @return Generate or not
118      */
119     @Override
generateCert(Options options)120     public boolean generateCert(Options options) {
121         LocalizationAdapter adapter = new LocalizationAdapter(options);
122         adapter.errorIfNotExist(adapter.getOptions().getString(Options.KEY_ALIAS));
123         String issuerAlias = adapter.getOptions().getString(Options.ISSUER_KEY_ALIAS);
124         adapter.errorIfNotExist(issuerAlias);
125 
126         KeyPair subjectKeyPair = adapter.getAliasKey(false);
127         if (options.containsKey(Options.ISSUER_KEY_STORE_FILE)) {
128             adapter.setKeyStoreHelper(null);
129             adapter.setIssuerKeyStoreFile(true);
130         }
131         KeyPair rootKeyPair = adapter.getIssuerAliasKey();
132         adapter.releasePwd();
133         byte[] csr = CertTools.generateCsr(subjectKeyPair, adapter.getSignAlg(), adapter.getSubject());
134 
135         X509Certificate cert = CertTools.generateCert(rootKeyPair, csr, adapter);
136         return outputCert(cert, adapter.getOutFile());
137     }
138 
139     /**
140      * Generate CA.
141      *
142      * @param options options
143      * @return Generate or not
144      */
145     @Override
generateCA(Options options)146     public boolean generateCA(Options options) {
147         LocalizationAdapter adapter = new LocalizationAdapter(options);
148         boolean isEmpty = StringUtils.isEmpty(options.getString(Options.ISSUER_KEY_ALIAS));
149         KeyPair subKey = adapter.getAliasKey(true);
150         KeyPair rootKey;
151         String ksFile = options.getString(Options.KEY_STORE_FILE);
152         String iksFile = options.getString(Options.ISSUER_KEY_STORE_FILE);
153         if (isEmpty) {
154             if (!StringUtils.isEmpty(iksFile) && !ksFile.equals(iksFile)) {
155                 CustomException.throwException(ERROR.WRITE_FILE_ERROR,
156                         String.format("Parameter '%s' and parameter '%s' are inconsistent", ksFile, iksFile));
157             }
158             if (options.containsKey(Options.ISSUER_KEY_STORE_RIGHTS)) {
159                 boolean isEqual = Arrays.equals(options.getChars(Options.KEY_STORE_RIGHTS),
160                         options.getChars(Options.ISSUER_KEY_STORE_RIGHTS));
161                 if (!isEqual) {
162                     CustomException.throwException(ERROR.WRITE_FILE_ERROR,
163                             String.format("Parameter '%s' and parameter '%s' are inconsistent",
164                                     Options.KEY_STORE_RIGHTS, Options.ISSUER_KEY_STORE_RIGHTS));
165                 }
166             }
167             rootKey = subKey;
168         } else {
169             if (options.containsKey(Options.ISSUER_KEY_STORE_FILE)) {
170                 FileUtils.validFileType(options.getString(Options.ISSUER_KEY_STORE_FILE), "p12", "jks");
171                 adapter.setKeyStoreHelper(null);
172                 adapter.setIssuerKeyStoreFile(true);
173             }
174             rootKey = adapter.getIssuerAliasKey();
175         }
176         adapter.releasePwd();
177 
178         byte[] csr = CertTools.generateCsr(subKey, adapter.getSignAlg(), adapter.getSubject());
179         X509Certificate cert;
180         if (isEmpty) {
181             cert = CertTools.generateRootCaCert(rootKey, csr, adapter);
182         } else {
183             cert = CertTools.generateSubCert(rootKey, csr, adapter);
184         }
185         return outputCert(cert, adapter.getOutFile());
186     }
187 
188     /**
189      * Generate app cert.
190      *
191      * @param options options
192      * @return Generate or not
193      */
194     @Override
generateAppCert(Options options)195     public boolean generateAppCert(Options options) {
196         LocalizationAdapter adapter = new LocalizationAdapter(options);
197         KeyPair keyPair = adapter.getAliasKey(false);
198         if (options.containsKey(Options.ISSUER_KEY_STORE_FILE)) {
199             adapter.setKeyStoreHelper(null);
200             adapter.setIssuerKeyStoreFile(true);
201         }
202         KeyPair issueKeyPair = adapter.getIssuerAliasKey();
203         adapter.releasePwd();
204 
205         byte[] csr = CertTools.generateCsr(keyPair, adapter.getSignAlg(), adapter.getSubject());
206         X509Certificate cert = CertTools.generateEndCert(issueKeyPair, csr, adapter, APP_SIGNING_CAPABILITY);
207         return getOutputCert(adapter, cert);
208     }
209 
210     /**
211      * Generate profile cert.
212      *
213      * @param options options
214      * @return Generate or not
215      */
216     @Override
generateProfileCert(Options options)217     public boolean generateProfileCert(Options options) {
218         LocalizationAdapter adapter = new LocalizationAdapter(options);
219         KeyPair keyPair = adapter.getAliasKey(false);
220         if (options.containsKey(Options.ISSUER_KEY_STORE_FILE)) {
221             adapter.setKeyStoreHelper(null);
222             adapter.setIssuerKeyStoreFile(true);
223         }
224         KeyPair issueKeyPair = adapter.getIssuerAliasKey();
225         adapter.releasePwd();
226 
227         byte[] csr = CertTools.generateCsr(keyPair, adapter.getSignAlg(), adapter.getSubject());
228         X509Certificate cert = CertTools.generateEndCert(issueKeyPair, csr, adapter, PROFILE_SIGNING_CAPABILITY);
229         return getOutputCert(adapter, cert);
230     }
231 
getOutputCert(LocalizationAdapter adapter, X509Certificate cert)232     private boolean getOutputCert(LocalizationAdapter adapter, X509Certificate cert) {
233         if (adapter.isOutFormChain()) {
234             List<X509Certificate> certificates = new ArrayList<>();
235             certificates.add(cert);
236             certificates.add(adapter.getSubCaCertFile());
237             certificates.add(adapter.getCaCertFile());
238             return outputCertChain(certificates, adapter.getOutFile());
239         } else {
240             return outputCert(cert, adapter.getOutFile());
241         }
242     }
243 
244     /**
245      * Sign for profile.
246      *
247      * @param options options
248      * @return Sign or not
249      */
250     @Override
signProfile(Options options)251     public boolean signProfile(Options options) {
252         boolean isSuccess;
253         try {
254             LocalizationAdapter adapter = new LocalizationAdapter(options);
255             byte[] provisionContent = getProvisionContent(new File(adapter.getInFile()));
256             byte[] p7b = ProfileSignTool.generateP7b(adapter, provisionContent);
257             FileUtils.write(p7b, new File(adapter.getOutFile()));
258             isSuccess = true;
259         } catch (IOException exception) {
260             logger.debug(exception.getMessage(), exception);
261             logger.error(exception.getMessage());
262             isSuccess = false;
263         }
264         return isSuccess;
265     }
266 
267     /**
268      * Verify profile.
269      *
270      * @param options options
271      * @return Verify or not
272      */
273     @Override
verifyProfile(Options options)274     public boolean verifyProfile(Options options) {
275         boolean isSign;
276         try {
277             LocalizationAdapter adapter = new LocalizationAdapter(options);
278             VerifyHelper verifyHelper = new VerifyHelper();
279             byte[] p7b = FileUtils.readFile(new File(adapter.getInFile()));
280             VerificationResult verificationResult = verifyHelper.verify(p7b);
281             isSign = verificationResult.isVerifiedPassed();
282             if (!isSign) {
283                 logger.error(verificationResult.getMessage());
284             }
285             outputString(FileUtils.GSON_PRETTY_PRINT.toJson(verificationResult), adapter.getOutFile());
286         } catch (IOException exception) {
287             logger.debug(exception.getMessage(), exception);
288             logger.error(exception.getMessage());
289             isSign = false;
290         } catch (VerifyException e) {
291             CustomException.throwException(ERROR.VERIFY_ERROR, "Verify Profile Failed! " + e.getMessage());
292             isSign = false;
293         }
294         return isSign;
295     }
296 
297     /**
298      * Sign for hap.
299      *
300      * @param options options
301      * @return Sign or not
302      */
303     @Override
signHap(Options options)304     public boolean signHap(Options options) {
305         String mode = options.getString(Options.MODE);
306         // sign online or locally
307         SignProvider signProvider;
308         if ("localSign".equalsIgnoreCase(mode)) {
309             signProvider = new LocalJKSSignProvider();
310         } else if ("remoteSign".equalsIgnoreCase(mode)) {
311             signProvider = new RemoteSignProvider();
312         } else {
313             logger.info("Resign mode. But not implement yet");
314             return false;
315         }
316 
317         // The type of file is bin or hap
318         String inForm = options.getString(Options.IN_FORM, "zip");
319         if ("zip".equalsIgnoreCase(inForm)) {
320             return signProvider.sign(options);
321         } else if ("elf".equalsIgnoreCase(inForm)) {
322             return signProvider.signElf(options);
323         } else {
324             return signProvider.signBin(options);
325         }
326     }
327 
328     @Override
verifyHap(Options options)329     public boolean verifyHap(Options options) {
330         if ("zip".equals(options.getOrDefault(ParamConstants.PARAM_IN_FORM, "zip"))) {
331             VerifyHap hapVerify = new VerifyHap();
332             return hapVerify.verify(options);
333         } else {
334             VerifyElf verifyElf = new VerifyElf();
335             return verifyElf.verify(options);
336         }
337     }
338 
339     /**
340      * Output string, save into file or print.
341      *
342      * @param content String Content
343      * @param file    file
344      */
outputString(String content, String file)345     public void outputString(String content, String file) {
346         if (StringUtils.isEmpty(file)) {
347             logger.info(content);
348         } else {
349             try {
350                 FileUtils.write(content.getBytes(StandardCharsets.UTF_8), new File(file));
351             } catch (IOException exception) {
352                 logger.debug(exception.getMessage(), exception);
353                 CustomException.throwException(ERROR.WRITE_FILE_ERROR, exception.getMessage());
354             }
355         }
356     }
357 
358     /**
359      * Output certificate.
360      *
361      * @param certificate certificate
362      * @param file        file
363      * @return Whether to output certificate
364      */
outputCert(X509Certificate certificate, String file)365     public boolean outputCert(X509Certificate certificate, String file) {
366         try {
367             if (StringUtils.isEmpty(file)) {
368                 logger.info(CertUtils.generateCertificateInCer(certificate));
369             } else {
370                 FileUtils.write(CertUtils.generateCertificateInCer(certificate).getBytes(StandardCharsets.UTF_8),
371                         new File(file));
372             }
373             return true;
374         } catch (CertificateException | IOException exception) {
375             logger.debug(exception.getMessage(), exception);
376             CustomException.throwException(ERROR.WRITE_FILE_ERROR, exception.getMessage());
377             return false;
378         }
379     }
380 
381     /**
382      * Output certificate in certificates list.
383      *
384      * @param certificates certificates
385      * @param file         file
386      * @return Whether to output certificate in certificates list
387      */
outputCertChain(List<X509Certificate> certificates, String file)388     public boolean outputCertChain(List<X509Certificate> certificates, String file) {
389         try {
390             StringBuilder stringBuilder = new StringBuilder();
391             for (X509Certificate cert : certificates) {
392                 stringBuilder.append(CertUtils.generateCertificateInCer(cert));
393             }
394             if (StringUtils.isEmpty(file)) {
395                 logger.info(stringBuilder.toString());
396             } else {
397                 FileUtils.write(stringBuilder.toString().getBytes(StandardCharsets.UTF_8), new File(file));
398             }
399             return true;
400         } catch (CertificateException | IOException exception) {
401             logger.debug(exception.getMessage(), exception);
402             CustomException.throwException(ERROR.WRITE_FILE_ERROR, exception.getMessage());
403             return false;
404         }
405     }
406 
407     /**
408      * Get provision content.
409      *
410      * @param input input provision profile
411      * @return file data
412      * @throws IOException IOException
413      */
getProvisionContent(File input)414     public static byte[] getProvisionContent(File input) throws IOException {
415         byte[] bytes = FileUtils.readFile(input);
416         String json = JsonParser.parseString(new String(bytes, StandardCharsets.UTF_8)).toString();
417         Provision provision = FileUtils.GSON.fromJson(new String(bytes, StandardCharsets.UTF_8), Provision.class);
418         Provision.enforceValid(provision);
419         return json.getBytes(StandardCharsets.UTF_8);
420     }
421 }
422