• 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.SignToolErrMsg;
24 import com.ohos.hapsigntool.error.VerifyException;
25 import com.ohos.hapsigntool.hap.provider.LocalJKSSignProvider;
26 import com.ohos.hapsigntool.hap.provider.RemoteSignProvider;
27 import com.ohos.hapsigntool.hap.provider.SignProvider;
28 import com.ohos.hapsigntool.hap.verify.VerifyElf;
29 import com.ohos.hapsigntool.hap.verify.VerifyHap;
30 import com.ohos.hapsigntool.profile.ProfileSignTool;
31 import com.ohos.hapsigntool.profile.VerifyHelper;
32 import com.ohos.hapsigntool.profile.model.Provision;
33 import com.ohos.hapsigntool.profile.model.VerificationResult;
34 import com.ohos.hapsigntool.utils.CertUtils;
35 import com.ohos.hapsigntool.utils.FileUtils;
36 import com.ohos.hapsigntool.entity.ParamConstants;
37 import com.ohos.hapsigntool.utils.LogUtils;
38 import com.ohos.hapsigntool.utils.StringUtils;
39 
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 LogUtils LOGGER = new LogUtils(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, SignToolErrMsg.GENERATE_CA_FAILED
156                         .toString(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, SignToolErrMsg.GENERATE_CA_FAILED
163                             .toString(Options.KEY_STORE_RIGHTS, Options.ISSUER_KEY_STORE_RIGHTS));
164                 }
165             }
166             rootKey = subKey;
167         } else {
168             if (options.containsKey(Options.ISSUER_KEY_STORE_FILE)) {
169                 FileUtils.validFileType(options.getString(Options.ISSUER_KEY_STORE_FILE), "p12", "jks");
170                 adapter.setKeyStoreHelper(null);
171                 adapter.setIssuerKeyStoreFile(true);
172             }
173             rootKey = adapter.getIssuerAliasKey();
174         }
175         adapter.releasePwd();
176 
177         byte[] csr = CertTools.generateCsr(subKey, adapter.getSignAlg(), adapter.getSubject());
178         X509Certificate cert;
179         if (isEmpty) {
180             cert = CertTools.generateRootCaCert(rootKey, csr, adapter);
181         } else {
182             cert = CertTools.generateSubCert(rootKey, csr, adapter);
183         }
184         return outputCert(cert, adapter.getOutFile());
185     }
186 
187     /**
188      * Generate app cert.
189      *
190      * @param options options
191      * @return Generate or not
192      */
193     @Override
generateAppCert(Options options)194     public boolean generateAppCert(Options options) {
195         LocalizationAdapter adapter = new LocalizationAdapter(options);
196         KeyPair keyPair = adapter.getAliasKey(false);
197         if (options.containsKey(Options.ISSUER_KEY_STORE_FILE)) {
198             adapter.setKeyStoreHelper(null);
199             adapter.setIssuerKeyStoreFile(true);
200         }
201         KeyPair issueKeyPair = adapter.getIssuerAliasKey();
202         adapter.releasePwd();
203 
204         byte[] csr = CertTools.generateCsr(keyPair, adapter.getSignAlg(), adapter.getSubject());
205         X509Certificate cert = CertTools.generateEndCert(issueKeyPair, csr, adapter, APP_SIGNING_CAPABILITY);
206         return getOutputCert(adapter, cert);
207     }
208 
209     /**
210      * Generate profile cert.
211      *
212      * @param options options
213      * @return Generate or not
214      */
215     @Override
generateProfileCert(Options options)216     public boolean generateProfileCert(Options options) {
217         LocalizationAdapter adapter = new LocalizationAdapter(options);
218         KeyPair keyPair = adapter.getAliasKey(false);
219         if (options.containsKey(Options.ISSUER_KEY_STORE_FILE)) {
220             adapter.setKeyStoreHelper(null);
221             adapter.setIssuerKeyStoreFile(true);
222         }
223         KeyPair issueKeyPair = adapter.getIssuerAliasKey();
224         adapter.releasePwd();
225 
226         byte[] csr = CertTools.generateCsr(keyPair, adapter.getSignAlg(), adapter.getSubject());
227         X509Certificate cert = CertTools.generateEndCert(issueKeyPair, csr, adapter, PROFILE_SIGNING_CAPABILITY);
228         return getOutputCert(adapter, cert);
229     }
230 
getOutputCert(LocalizationAdapter adapter, X509Certificate cert)231     private boolean getOutputCert(LocalizationAdapter adapter, X509Certificate cert) {
232         if (adapter.isOutFormChain()) {
233             List<X509Certificate> certificates = new ArrayList<>();
234             certificates.add(cert);
235             certificates.add(adapter.getSubCaCertFile());
236             certificates.add(adapter.getCaCertFile());
237             return outputCertChain(certificates, adapter.getOutFile());
238         } else {
239             return outputCert(cert, adapter.getOutFile());
240         }
241     }
242 
243     /**
244      * Sign for profile.
245      *
246      * @param options options
247      * @return Sign or not
248      */
249     @Override
signProfile(Options options)250     public boolean signProfile(Options options) {
251         boolean isSuccess;
252         try {
253             LocalizationAdapter adapter = new LocalizationAdapter(options);
254             byte[] provisionContent = getProvisionContent(new File(adapter.getInFile()));
255             byte[] p7b = ProfileSignTool.generateP7b(adapter, provisionContent);
256             FileUtils.write(p7b, new File(adapter.getOutFile()));
257             isSuccess = true;
258         } catch (IOException exception) {
259             LOGGER.debug(exception.getMessage(), exception);
260             LOGGER.error(exception.getMessage());
261             isSuccess = false;
262         }
263         return isSuccess;
264     }
265 
266     /**
267      * Verify profile.
268      *
269      * @param options options
270      * @return Verify or not
271      */
272     @Override
verifyProfile(Options options)273     public boolean verifyProfile(Options options) {
274         boolean isSign;
275         try {
276             LocalizationAdapter adapter = new LocalizationAdapter(options);
277             VerifyHelper verifyHelper = new VerifyHelper();
278             byte[] p7b = FileUtils.readFile(new File(adapter.getInFile()));
279             VerificationResult verificationResult = verifyHelper.verify(p7b);
280             isSign = verificationResult.isVerifiedPassed();
281             if (!isSign) {
282                 LOGGER.error(verificationResult.getMessage());
283             }
284             outputString(FileUtils.GSON_PRETTY_PRINT.toJson(verificationResult), adapter.getOutFile());
285         } catch (IOException exception) {
286             LOGGER.debug(exception.getMessage(), exception);
287             LOGGER.error(exception.getMessage());
288             isSign = false;
289         } catch (VerifyException e) {
290             CustomException.throwException(ERROR.VERIFY_ERROR, SignToolErrMsg.VERIFY_PROFILE_FAILED
291                     .toString(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, SignToolErrMsg.FILE_WRITE_FAILED
354                         .toString(exception.getMessage()));
355             }
356         }
357     }
358 
359     /**
360      * Output certificate.
361      *
362      * @param certificate certificate
363      * @param file        file
364      * @return Whether to output certificate
365      */
outputCert(X509Certificate certificate, String file)366     public boolean outputCert(X509Certificate certificate, String file) {
367         try {
368             if (StringUtils.isEmpty(file)) {
369                 LOGGER.info(CertUtils.generateCertificateInCer(certificate));
370             } else {
371                 FileUtils.write(CertUtils.generateCertificateInCer(certificate).getBytes(StandardCharsets.UTF_8),
372                         new File(file));
373             }
374             return true;
375         } catch (CertificateException | IOException exception) {
376             LOGGER.debug(exception.getMessage(), exception);
377             CustomException.throwException(ERROR.WRITE_FILE_ERROR, SignToolErrMsg.FILE_WRITE_FAILED
378                     .toString(exception.getMessage()));
379             return false;
380         }
381     }
382 
383     /**
384      * Output certificate in certificates list.
385      *
386      * @param certificates certificates
387      * @param file         file
388      * @return Whether to output certificate in certificates list
389      */
outputCertChain(List<X509Certificate> certificates, String file)390     public boolean outputCertChain(List<X509Certificate> certificates, String file) {
391         try {
392             StringBuilder stringBuilder = new StringBuilder();
393             for (X509Certificate cert : certificates) {
394                 stringBuilder.append(CertUtils.generateCertificateInCer(cert));
395             }
396             if (StringUtils.isEmpty(file)) {
397                 LOGGER.info(stringBuilder.toString());
398             } else {
399                 FileUtils.write(stringBuilder.toString().getBytes(StandardCharsets.UTF_8), new File(file));
400             }
401             return true;
402         } catch (CertificateException | IOException exception) {
403             LOGGER.debug(exception.getMessage(), exception);
404             CustomException.throwException(ERROR.WRITE_FILE_ERROR, SignToolErrMsg.FILE_WRITE_FAILED
405                     .toString(exception.getMessage()));
406             return false;
407         }
408     }
409 
410     /**
411      * Get provision content.
412      *
413      * @param input input provision profile
414      * @return file data
415      * @throws IOException IOException
416      */
getProvisionContent(File input)417     public static byte[] getProvisionContent(File input) throws IOException {
418         byte[] bytes = FileUtils.readFile(input);
419         String json = JsonParser.parseString(new String(bytes, StandardCharsets.UTF_8)).toString();
420         Provision provision = FileUtils.GSON.fromJson(new String(bytes, StandardCharsets.UTF_8), Provision.class);
421         Provision.enforceValid(provision);
422         return json.getBytes(StandardCharsets.UTF_8);
423     }
424 }
425