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.cert; 17 18 import com.ohos.hapsigntool.api.LocalizationAdapter; 19 import com.ohos.hapsigntool.api.ServiceApi; 20 import com.ohos.hapsigntool.api.model.Options; 21 import com.ohos.hapsigntool.error.CustomException; 22 import com.ohos.hapsigntool.error.ERROR; 23 import com.ohos.hapsigntool.utils.ValidateUtils; 24 import org.apache.logging.log4j.LogManager; 25 import org.apache.logging.log4j.Logger; 26 import org.bouncycastle.asn1.x500.X500Name; 27 import org.bouncycastle.asn1.x509.KeyPurposeId; 28 import org.bouncycastle.asn1.x509.KeyUsage; 29 import org.bouncycastle.jce.provider.BouncyCastleProvider; 30 import org.bouncycastle.operator.ContentSigner; 31 import org.bouncycastle.operator.OperatorCreationException; 32 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; 33 import org.bouncycastle.pkcs.PKCS10CertificationRequest; 34 import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; 35 36 import java.io.IOException; 37 import java.security.KeyPair; 38 import java.security.PrivateKey; 39 import java.security.cert.X509Certificate; 40 import java.security.interfaces.ECPrivateKey; 41 import java.security.interfaces.RSAPrivateKey; 42 import java.util.regex.Matcher; 43 import java.util.regex.Pattern; 44 45 /** 46 * CertTools. 47 * 48 * @since 2021/12/28 49 */ 50 public final class CertTools { 51 /** 52 * Ten years, in days. 53 */ 54 private static final int TEN_YEAR_DAY = 3650; 55 56 /** 57 * Three years, in dats. 58 */ 59 private static final int THREE_YEAR_DAY = 1095; 60 61 /** 62 * Empty csr array. 63 */ 64 private static final byte[] NO_CSR = {}; 65 66 /** 67 * ECC. 68 */ 69 private static final String ECC = "ECDSA"; 70 71 /** 72 * Compile String. 73 */ 74 private static final Pattern SIGN_ALGORITHM_PATTERN = Pattern.compile("^SHA([0-9]{3})with([A-Z]{1,5})$"); 75 76 /** 77 * Logger. 78 */ 79 private static final Logger LOGGER = LogManager.getLogger(ServiceApi.class); 80 CertTools()81 private CertTools() { 82 } 83 84 /** 85 * Generate root ca certificate. 86 * 87 * @param keyPair keyPair 88 * @param csr csr 89 * @param adapter adapter 90 * @return X509Certificate 91 */ generateRootCaCert(KeyPair keyPair, byte[] csr, LocalizationAdapter adapter)92 public static X509Certificate generateRootCaCert(KeyPair keyPair, byte[] csr, LocalizationAdapter adapter) { 93 try { 94 return new CertBuilder(keyPair, adapter.getIssuer(), csr, 95 adapter.getOptions().getInt(Options.VALIDITY, TEN_YEAR_DAY)) 96 .withAuthorityKeyIdentifier(CertLevel.ROOT_CA) 97 .withBasicConstraints(CertLevel.ROOT_CA, true, true, 98 adapter.getBasicConstraintsPathLen()) 99 .withKeyUsages(new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign), true) 100 .withExtKeyUsages(null, false) 101 .build(adapter.getSignAlg()); 102 } catch (IOException exception) { 103 LOGGER.debug(exception.getMessage(), exception); 104 CustomException.throwException(ERROR.IO_CERT_ERROR, exception.getMessage()); 105 } 106 return null; 107 } 108 109 /** 110 * Generate sub ca certificate. 111 * 112 * @param keyPair keyPair 113 * @param csr csr 114 * @param adapter parameter 115 * @return X509Certificate 116 */ generateSubCert(KeyPair keyPair, byte[] csr, LocalizationAdapter adapter)117 public static X509Certificate generateSubCert(KeyPair keyPair, byte[] csr, LocalizationAdapter adapter) { 118 try { 119 return new CertBuilder(keyPair, adapter.getIssuer(), csr, 120 adapter.getOptions().getInt(Options.VALIDITY, TEN_YEAR_DAY)) 121 .withAuthorityKeyIdentifier(CertLevel.SUB_CA) 122 .withBasicConstraints(CertLevel.SUB_CA, true, true, 123 adapter.getBasicConstraintsPathLen()) 124 .withKeyUsages(new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign), true) 125 .build(adapter.getSignAlg()); 126 } catch (IOException exception) { 127 LOGGER.debug(exception.getMessage(), exception); 128 CustomException.throwException(ERROR.IO_CERT_ERROR, exception.getMessage()); 129 } 130 return null; 131 } 132 133 /** 134 * Generate certificate. 135 * 136 * @param keyPair keyPair 137 * @param csr csr 138 * @param adapter parameter 139 * @return X509Certificate 140 */ generateCert(KeyPair keyPair, byte[] csr, LocalizationAdapter adapter)141 public static X509Certificate generateCert(KeyPair keyPair, byte[] csr, LocalizationAdapter adapter) { 142 try { 143 return new CertBuilder(keyPair, adapter.getIssuer(), csr, 144 adapter.getOptions().getInt(Options.VALIDITY, THREE_YEAR_DAY)) 145 // Need CertLevel 146 .withAuthorityKeyIdentifier(CertLevel.ROOT_CA) 147 .withBasicConstraints(CertLevel.ROOT_CA, 148 adapter.isBasicConstraintsCritical(), 149 adapter.isBasicConstraintsCa(), 150 adapter.getBasicConstraintsPathLen()) 151 .withKeyUsages(adapter.getKeyUsage(), adapter.isKeyUsageCritical()) 152 .withExtKeyUsages(adapter.getExtKeyUsage(), adapter.isExtKeyUsageCritical()) 153 .build(adapter.getSignAlg()); 154 } catch (IOException exception) { 155 LOGGER.debug(exception.getMessage(), exception); 156 CustomException.throwException(ERROR.IO_CERT_ERROR, exception.getMessage()); 157 } 158 return null; 159 } 160 161 /** 162 * Generate app certificate. 163 * 164 * @param keyPair keyPair 165 * @param csr csr 166 * @param adapter adapter 167 * @return X509Certificate 168 */ generateEndCert(KeyPair keyPair, byte[] csr, LocalizationAdapter adapter, byte[] signingCapabiltyBytes)169 public static X509Certificate generateEndCert(KeyPair keyPair, byte[] csr, LocalizationAdapter adapter, 170 byte[] signingCapabiltyBytes) { 171 try { 172 return new CertBuilder(keyPair, adapter.getIssuer(), csr, 173 adapter.getOptions().getInt(Options.VALIDITY, THREE_YEAR_DAY)) 174 .withBasicConstraints(CertLevel.END_ENTITY, false, false, 175 null) 176 .withKeyUsages(new KeyUsage(KeyUsage.digitalSignature), true) 177 .withExtKeyUsages(new KeyPurposeId[]{KeyPurposeId.id_kp_codeSigning}, false) 178 .withSigningCapabilty(signingCapabiltyBytes) 179 .build(adapter.getSignAlg()); 180 } catch (IOException exception) { 181 LOGGER.debug(exception.getMessage(), exception); 182 CustomException.throwException(ERROR.IO_CERT_ERROR, exception.getMessage()); 183 } 184 return null; 185 } 186 187 /** 188 * generateCsr. 189 * 190 * @param keyPair Applier keypair 191 * @param signAlgorithm sign algorithm 192 * @param subject Applier subject 193 * @return csr bytes 194 */ generateCsr(KeyPair keyPair, String signAlgorithm, X500Name subject)195 public static byte[] generateCsr(KeyPair keyPair, String signAlgorithm, X500Name subject) { 196 JcaPKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(subject, 197 keyPair.getPublic()); 198 PKCS10CertificationRequest csr = csrBuilder.build(createFixedContentSigner(keyPair.getPrivate(), 199 signAlgorithm)); 200 try { 201 return csr.getEncoded(); 202 } catch (IOException exception) { 203 LOGGER.debug(exception.getMessage(), exception); 204 CustomException.throwException(ERROR.IO_CSR_ERROR, "Not support " + subject); 205 return NO_CSR; 206 } 207 } 208 209 /** 210 * Auto fix algorithm according key type and create content signer. 211 * 212 * @param privateKey Sign key 213 * @param signAlgorithm Sign algorithm 214 * @return ContentSigner 215 */ createFixedContentSigner(PrivateKey privateKey, String signAlgorithm)216 public static ContentSigner createFixedContentSigner(PrivateKey privateKey, String signAlgorithm) { 217 Matcher matcher = SIGN_ALGORITHM_PATTERN.matcher(signAlgorithm); 218 ValidateUtils.throwIfNotMatches(matcher.matches(), ERROR.NOT_SUPPORT_ERROR, "Not Support " + signAlgorithm); 219 // Auto fix signAlgorithm error 220 if (privateKey instanceof ECPrivateKey && signAlgorithm.contains("RSA")) { 221 signAlgorithm = signAlgorithm.replace("RSA", ECC); 222 } else { 223 if (privateKey instanceof RSAPrivateKey && signAlgorithm.contains(ECC)) { 224 signAlgorithm = signAlgorithm.replace(ECC, "RSA"); 225 } 226 } 227 228 JcaContentSignerBuilder jcaContentSignerBuilder = new JcaContentSignerBuilder(signAlgorithm); 229 jcaContentSignerBuilder.setProvider(BouncyCastleProvider.PROVIDER_NAME); 230 try { 231 return jcaContentSignerBuilder.build(privateKey); 232 } catch (OperatorCreationException exception) { 233 LOGGER.debug(exception.getMessage(), exception); 234 CustomException.throwException(ERROR.OPERATOR_CREATION_ERROR, exception.getMessage()); 235 } 236 return null; 237 } 238 239 } 240