• 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.utils;
17 
18 import com.ohos.hapsigntool.error.CustomException;
19 import com.ohos.hapsigntool.error.ERROR;
20 import com.ohos.hapsigntool.hap.exception.VerifyCertificateChainException;
21 import org.apache.logging.log4j.LogManager;
22 import org.apache.logging.log4j.Logger;
23 import org.bouncycastle.asn1.x500.X500Name;
24 import org.bouncycastle.asn1.x509.KeyPurposeId;
25 import org.bouncycastle.asn1.x509.KeyUsage;
26 
27 import javax.security.auth.x500.X500Principal;
28 import java.io.ByteArrayInputStream;
29 import java.math.BigInteger;
30 import java.nio.charset.StandardCharsets;
31 import java.security.SecureRandom;
32 import java.security.cert.CertificateEncodingException;
33 import java.security.cert.CertificateException;
34 import java.security.cert.CertificateFactory;
35 import java.security.cert.X509Certificate;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.List;
39 
40 /**
41  * Cert Usage Util.
42  *
43  * @since 2021/12/28
44  */
45 public final class CertUtils {
46     /**
47      * Logger.
48      */
49     private static final Logger LOGGER = LogManager.getLogger(CertUtils.class);
50 
51     /**
52      * Max length to print certificate string.
53      */
54     private static final int MAX_LINE_LENGTH = 65;
55     /**
56      * Length of serial security random number.
57      */
58     private static final int RANDOM_SERIAL_LENGTH = 32;
59     /**
60      * number constant.
61      */
62     private static final int SECOND_INDEX = 2;
63 
CertUtils()64     private CertUtils() {
65         // Empty constructor
66     }
67 
68     /**
69      * Parse string to key usage.
70      *
71      * @param keyUsageStr Key usage string
72      * @return Key usage
73      */
parseKeyUsage(String keyUsageStr)74     public static int parseKeyUsage(String keyUsageStr) {
75         int keyUsage = 0;
76         if (keyUsageStr.contains("digitalSignature")) {
77             keyUsage |= KeyUsage.digitalSignature;
78         }
79         if (keyUsageStr.contains("nonRepudiation")) {
80             keyUsage |= KeyUsage.nonRepudiation;
81         }
82         if (keyUsageStr.contains("keyEncipherment")) {
83             keyUsage |= KeyUsage.keyEncipherment;
84         }
85         if (keyUsageStr.contains("dataEncipherment")) {
86             keyUsage |= KeyUsage.dataEncipherment;
87         }
88         if (keyUsageStr.contains("keyAgreement")) {
89             keyUsage |= KeyUsage.keyAgreement;
90         }
91         if (keyUsageStr.contains("certificateSignature")) {
92             keyUsage |= KeyUsage.keyCertSign;
93         }
94         if (keyUsageStr.contains("crlSignature")) {
95             keyUsage |= KeyUsage.cRLSign;
96         }
97         if (keyUsageStr.contains("encipherOnly")) {
98             keyUsage |= KeyUsage.encipherOnly;
99         }
100         if (keyUsageStr.contains("decipherOnly")) {
101             keyUsage |= KeyUsage.decipherOnly;
102         }
103         return keyUsage;
104     }
105 
106     /**
107      * Parse string to KeyPurposeId[]
108      *
109      * @param extKeyUsageStr ext key usage string
110      * @return KeyPurposeId[]
111      */
parseExtKeyUsage(String extKeyUsageStr)112     public static KeyPurposeId[] parseExtKeyUsage(String extKeyUsageStr) {
113         ArrayList<KeyPurposeId> ids = new ArrayList<>();
114         if (extKeyUsageStr.contains("clientAuthentication")) {
115             ids.add(KeyPurposeId.id_kp_clientAuth);
116         }
117         if (extKeyUsageStr.contains("serverAuthentication")) {
118             ids.add(KeyPurposeId.id_kp_serverAuth);
119         }
120         if (extKeyUsageStr.contains("codeSignature")) {
121             ids.add(KeyPurposeId.id_kp_codeSigning);
122         }
123         if (extKeyUsageStr.contains("emailProtection")) {
124             ids.add(KeyPurposeId.id_kp_emailProtection);
125         }
126         if (extKeyUsageStr.contains("smartCardLogin")) {
127             ids.add(KeyPurposeId.id_kp_smartcardlogon);
128         }
129         if (extKeyUsageStr.contains("timestamp")) {
130             ids.add(KeyPurposeId.id_kp_timeStamping);
131         }
132         if (extKeyUsageStr.contains("ocspSignature")) {
133             ids.add(KeyPurposeId.id_kp_OCSPSigning);
134         }
135         return ids.toArray(new KeyPurposeId[]{});
136     }
137 
138     /**
139      * buildDN
140      * @param nameString nameString
141      * @return X500Name
142      */
buildDN(String nameString)143     public static X500Name buildDN(String nameString) {
144         checkDN(nameString);
145         X500Name dn = null;
146         try {
147             dn = new X500Name(nameString);
148         } catch (IllegalArgumentException | IndexOutOfBoundsException exception) {
149             LOGGER.debug(exception.getMessage(), exception);
150             CustomException.throwException(ERROR.COMMAND_ERROR,
151                     String.format("Error params near: %s. Reason: %s", nameString, exception.getMessage()));
152         }
153         return dn;
154     }
155 
156     /**
157      * To verify the format of subject or issuer.
158      * Refer to X500NameStyle.fromString().
159      *
160      * @param nameString subject or issuer
161      */
checkDN(String nameString)162     private static void checkDN(String nameString) {
163         String errorMsg = String.format("Format error, must be \"X=xx,XX=xxx,...\", please check: \"%s\"", nameString);
164         ValidateUtils.throwIfNotMatches(!StringUtils.isEmpty(nameString), ERROR.COMMAND_ERROR, errorMsg);
165         String[] pairs = nameString.split(",");
166         for (String pair : pairs) {
167             ValidateUtils.throwIfNotMatches(!StringUtils.isEmpty(nameString.trim()), ERROR.COMMAND_ERROR, errorMsg);
168             String[] kvPair = pair.split("=");
169             ValidateUtils.throwIfNotMatches(kvPair.length == SECOND_INDEX, ERROR.COMMAND_ERROR, errorMsg);
170             // Key will be checked in X500NameStyle.attrNameToOID
171             ValidateUtils.throwIfNotMatches(!StringUtils.isEmpty(kvPair[1].trim()), ERROR.COMMAND_ERROR, errorMsg);
172         }
173     }
174 
175     /**
176      * Convert byte to CSR String.
177      *
178      * @param csr bytes of CSR
179      * @return String
180      */
toCsrTemplate(byte[] csr)181     public static String toCsrTemplate(byte[] csr) {
182         return "-----BEGIN NEW CERTIFICATE REQUEST-----\n"
183                 + java.util.Base64.getMimeEncoder(MAX_LINE_LENGTH, "\n".getBytes(StandardCharsets.UTF_8))
184                 .encodeToString(csr)
185                 + "\n-----END NEW CERTIFICATE REQUEST-----\n";
186     }
187 
188     /**
189      * Encoding cert to String.
190      *
191      * @param certificate Cert to convert to string
192      * @return Cert templated string
193      * @throws CertificateEncodingException Failed encoding
194      */
generateCertificateInCer(X509Certificate certificate)195     public static String generateCertificateInCer(X509Certificate certificate)
196             throws CertificateEncodingException {
197         return "-----BEGIN CERTIFICATE-----\n"
198                 + java.util.Base64.getMimeEncoder(MAX_LINE_LENGTH, "\n".getBytes(StandardCharsets.UTF_8))
199                 .encodeToString(certificate.getEncoded())
200                 + "\n" + "-----END CERTIFICATE-----" + "\n";
201     }
202 
203     /**
204      * Random serial.
205      *
206      * @return Random big integer
207      */
randomSerial()208     public static BigInteger randomSerial() {
209         return new BigInteger(RANDOM_SERIAL_LENGTH, new SecureRandom());
210     }
211 
212     /**
213      * Convert byte to cert.
214      *
215      * @param cert Byte from cert file
216      * @return Certs
217      * @throws CertificateException Convert failed
218      * @throws VerifyCertificateChainException certificates in file are not certificate chain
219      */
220     @SuppressWarnings("unchecked")
generateCertificates(byte[] cert)221     public static List<X509Certificate> generateCertificates(byte[] cert) throws CertificateException,
222             VerifyCertificateChainException {
223         CertificateFactory factory = CertificateFactory.getInstance("X.509");
224         List<X509Certificate> certificates =
225                 (List<X509Certificate>) factory.generateCertificates(new ByteArrayInputStream(cert));
226         sortCertificateChain(certificates);
227         CertificateUtils.verifyCertChain(certificates);
228         return certificates;
229     }
230 
231     /**
232      * Sort cert chain to sign cert, sub cert, root cert
233      *
234      * @param certificates cert chain
235      */
sortCertificateChain(List<X509Certificate> certificates)236     public static void sortCertificateChain(List<X509Certificate> certificates) {
237         if (certificates != null && certificates.size() > 1) {
238             int size = certificates.size();
239             X500Principal lastSubjectX500Principal = (certificates.get(size - 1)).getSubjectX500Principal();
240             X500Principal beforeIssuerX500Principal = (certificates.get(size - SECOND_INDEX)).getIssuerX500Principal();
241             if (!lastSubjectX500Principal.equals(beforeIssuerX500Principal)) {
242                 Collections.reverse(certificates);
243             }
244         }
245     }
246 }
247