• 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.ohos.hapsigntool.api.model.Options;
19 import com.ohos.hapsigntool.cert.CertTools;
20 import com.ohos.hapsigntool.error.CustomException;
21 import com.ohos.hapsigntool.error.ERROR;
22 import com.ohos.hapsigntool.error.VerifyException;
23 import com.ohos.hapsigntool.hap.provider.LocalJKSSignProvider;
24 import com.ohos.hapsigntool.hap.provider.RemoteSignProvider;
25 import com.ohos.hapsigntool.hap.provider.SignProvider;
26 import com.ohos.hapsigntool.hap.verify.VerifyElf;
27 import com.ohos.hapsigntool.hap.verify.VerifyHap;
28 import com.ohos.hapsigntool.profile.ProfileSignTool;
29 import com.ohos.hapsigntool.profile.VerifyHelper;
30 import com.ohos.hapsigntool.profile.model.VerificationResult;
31 import com.ohos.hapsigntool.utils.CertUtils;
32 import com.ohos.hapsigntool.utils.FileUtils;
33 import com.ohos.hapsigntool.utils.ParamConstants;
34 import com.ohos.hapsigntool.utils.ProfileUtils;
35 import com.ohos.hapsigntool.utils.StringUtils;
36 
37 import org.apache.logging.log4j.LogManager;
38 import org.apache.logging.log4j.Logger;
39 import org.bouncycastle.jce.provider.BouncyCastleProvider;
40 
41 import java.io.File;
42 import java.io.IOException;
43 import java.nio.charset.StandardCharsets;
44 import java.security.KeyPair;
45 import java.security.Security;
46 import java.security.cert.CertificateException;
47 import java.security.cert.X509Certificate;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.List;
51 
52 /**
53  * Main entry of lib.
54  *
55  * @since 2021/12/28
56  */
57 public class SignToolServiceImpl implements ServiceApi {
58     /**
59      * App signing Capabilty Bytes.
60      */
61     private static final byte[] APP_SIGNING_CAPABILITY = {0x30, 0x06, 0x02, 0x01, 0x01, 0x0A, 0x01, 0x00};
62 
63     /**
64      * Profile signing Capabilty Bytes.
65      */
66     private static final byte[] PROFILE_SIGNING_CAPABILITY = {0x30, 0x06, 0x02, 0x01, 0x01, 0x0A, 0x01, 0x01};
67 
68     /**
69      * Logger.
70      */
71     private static final Logger logger = LogManager.getLogger(ServiceApi.class);
72 
73     static {
Security.addProvider(new BouncyCastleProvider())74         Security.addProvider(new BouncyCastleProvider());
75     }
76 
77     /**
78      * Generate keyStore.
79      *
80      * @param options options
81      * @return Generate or not
82      */
83     @Override
generateKeyStore(Options options)84     public boolean generateKeyStore(Options options) {
85         LocalizationAdapter adapter = new LocalizationAdapter(options);
86         adapter.errorOnExist(options.getString(Options.KEY_ALIAS));
87         KeyPair keyPair = adapter.getAliasKey(true);
88         adapter.releasePwd();
89         return keyPair != null;
90     }
91 
92     /**
93      * Generate csr.
94      *
95      * @param options options
96      * @return Generate or not
97      */
98     @Override
generateCsr(Options options)99     public boolean generateCsr(Options options) {
100         LocalizationAdapter adapter = new LocalizationAdapter(options);
101         KeyPair keyPair = adapter.getAliasKey(false);
102         adapter.releasePwd();
103         byte[] csr = CertTools.generateCsr(keyPair, adapter.getSignAlg(), adapter.getSubject());
104         if (csr == null) {
105             return false;
106         }
107         String csrContent = CertUtils.toCsrTemplate(csr);
108         outputString(csrContent, adapter.getOutFile());
109         return true;
110     }
111 
112     /**
113      * Generate cert.
114      *
115      * @param options options
116      * @return Generate or not
117      */
118     @Override
generateCert(Options options)119     public boolean generateCert(Options options) {
120         LocalizationAdapter adapter = new LocalizationAdapter(options);
121         adapter.errorIfNotExist(adapter.getOptions().getString(Options.KEY_ALIAS));
122         String issuerAlias = adapter.getOptions().getString(Options.ISSUER_KEY_ALIAS);
123         adapter.errorIfNotExist(issuerAlias);
124 
125         KeyPair subjectKeyPair = adapter.getAliasKey(false);
126         if (options.containsKey(Options.ISSUER_KEY_STORE_FILE)) {
127             adapter.setKeyStoreHelper(null);
128             adapter.setIssuerKeyStoreFile(true);
129         }
130         KeyPair rootKeyPair = adapter.getIssuerAliasKey();
131         adapter.releasePwd();
132         byte[] csr = CertTools.generateCsr(subjectKeyPair, adapter.getSignAlg(), adapter.getSubject());
133 
134         X509Certificate cert = CertTools.generateCert(rootKeyPair, csr, adapter);
135         return outputCert(cert, adapter.getOutFile());
136     }
137 
138     /**
139      * Generate CA.
140      *
141      * @param options options
142      * @return Generate or not
143      */
144     @Override
generateCA(Options options)145     public boolean generateCA(Options options) {
146         LocalizationAdapter adapter = new LocalizationAdapter(options);
147         boolean isEmpty = StringUtils.isEmpty(options.getString(Options.ISSUER_KEY_ALIAS));
148         KeyPair subKey = adapter.getAliasKey(true);
149         KeyPair rootKey;
150         String ksFile = options.getString(Options.KEY_STORE_FILE);
151         String iksFile = options.getString(Options.ISSUER_KEY_STORE_FILE);
152         if (isEmpty) {
153             if (!StringUtils.isEmpty(iksFile) && !ksFile.equals(iksFile)) {
154                 CustomException.throwException(ERROR.WRITE_FILE_ERROR,
155                         String.format("Parameter '%s' and parameter '%s' are inconsistent", ksFile, iksFile));
156             }
157             if (options.containsKey(Options.ISSUER_KEY_STORE_RIGHTS)) {
158                 boolean isEqual = Arrays.equals(options.getChars(Options.KEY_STORE_RIGHTS),
159                         options.getChars(Options.ISSUER_KEY_STORE_RIGHTS));
160                 if (!isEqual) {
161                     CustomException.throwException(ERROR.WRITE_FILE_ERROR,
162                             String.format("Parameter '%s' and parameter '%s' are inconsistent",
163                                     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 = ProfileUtils.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, "Verify Profile Failed! " + e.getMessage());
291             isSign = false;
292         }
293         return isSign;
294     }
295 
296     /**
297      * Sign for hap.
298      *
299      * @param options options
300      * @return Sign or not
301      */
302     @Override
signHap(Options options)303     public boolean signHap(Options options) {
304         String mode = options.getString(Options.MODE);
305         // sign online or locally
306         SignProvider signProvider;
307         if ("localSign".equalsIgnoreCase(mode)) {
308             signProvider = new LocalJKSSignProvider();
309         } else if ("remoteSign".equalsIgnoreCase(mode)) {
310             signProvider = new RemoteSignProvider();
311         } else {
312             logger.info("Resign mode. But not implement yet");
313             return false;
314         }
315 
316         // The type of file is bin or hap
317         String inForm = options.getString(Options.IN_FORM, "zip");
318         if ("zip".equalsIgnoreCase(inForm)) {
319             return signProvider.sign(options);
320         } else if ("elf".equalsIgnoreCase(inForm)) {
321             return signProvider.signElf(options);
322         } else {
323             return signProvider.signBin(options);
324         }
325     }
326 
327     @Override
verifyHap(Options options)328     public boolean verifyHap(Options options) {
329         if ("zip".equals(options.getOrDefault(ParamConstants.PARAM_IN_FORM, "zip"))) {
330             VerifyHap hapVerify = new VerifyHap();
331             return hapVerify.verify(options);
332         } else {
333             VerifyElf verifyElf = new VerifyElf();
334             return verifyElf.verify(options);
335         }
336     }
337 
338     /**
339      * Output string, save into file or print.
340      *
341      * @param content String Content
342      * @param file    file
343      */
outputString(String content, String file)344     public void outputString(String content, String file) {
345         if (StringUtils.isEmpty(file)) {
346             logger.info(content);
347         } else {
348             try {
349                 FileUtils.write(content.getBytes(StandardCharsets.UTF_8), new File(file));
350             } catch (IOException exception) {
351                 logger.debug(exception.getMessage(), exception);
352                 CustomException.throwException(ERROR.WRITE_FILE_ERROR, exception.getMessage());
353             }
354         }
355     }
356 
357     /**
358      * Output certificate.
359      *
360      * @param certificate certificate
361      * @param file        file
362      * @return Whether to output certificate
363      */
outputCert(X509Certificate certificate, String file)364     public boolean outputCert(X509Certificate certificate, String file) {
365         try {
366             if (StringUtils.isEmpty(file)) {
367                 logger.info(CertUtils.generateCertificateInCer(certificate));
368             } else {
369                 FileUtils.write(CertUtils.generateCertificateInCer(certificate).getBytes(StandardCharsets.UTF_8),
370                         new File(file));
371             }
372             return true;
373         } catch (CertificateException | IOException exception) {
374             logger.debug(exception.getMessage(), exception);
375             CustomException.throwException(ERROR.WRITE_FILE_ERROR, exception.getMessage());
376             return false;
377         }
378     }
379 
380     /**
381      * Output certificate in certificates list.
382      *
383      * @param certificates certificates
384      * @param file         file
385      * @return Whether to output certificate in certificates list
386      */
outputCertChain(List<X509Certificate> certificates, String file)387     public boolean outputCertChain(List<X509Certificate> certificates, String file) {
388         try {
389             StringBuilder stringBuilder = new StringBuilder();
390             for (X509Certificate cert : certificates) {
391                 stringBuilder.append(CertUtils.generateCertificateInCer(cert));
392             }
393             if (StringUtils.isEmpty(file)) {
394                 logger.info(stringBuilder.toString());
395             } else {
396                 FileUtils.write(stringBuilder.toString().getBytes(StandardCharsets.UTF_8), new File(file));
397             }
398             return true;
399         } catch (CertificateException | IOException exception) {
400             logger.debug(exception.getMessage(), exception);
401             CustomException.throwException(ERROR.WRITE_FILE_ERROR, exception.getMessage());
402             return false;
403         }
404     }
405 }
406