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.ServiceApi; 19 import com.ohos.hapsigntool.error.CustomException; 20 import com.ohos.hapsigntool.error.ERROR; 21 import com.ohos.hapsigntool.error.SignToolErrMsg; 22 import com.ohos.hapsigntool.utils.CertUtils; 23 24 import com.ohos.hapsigntool.utils.LogUtils; 25 import org.bouncycastle.asn1.ASN1ObjectIdentifier; 26 import org.bouncycastle.asn1.x500.X500Name; 27 import org.bouncycastle.asn1.x509.BasicConstraints; 28 import org.bouncycastle.asn1.x509.ExtendedKeyUsage; 29 import org.bouncycastle.asn1.x509.Extension; 30 import org.bouncycastle.asn1.x509.KeyPurposeId; 31 import org.bouncycastle.asn1.x509.KeyUsage; 32 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 33 import org.bouncycastle.cert.CertIOException; 34 import org.bouncycastle.cert.X509v3CertificateBuilder; 35 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; 36 import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; 37 import org.bouncycastle.jce.provider.BouncyCastleProvider; 38 import org.bouncycastle.operator.ContentSigner; 39 import org.bouncycastle.pkcs.PKCS10CertificationRequest; 40 41 import java.io.IOException; 42 import java.security.InvalidKeyException; 43 import java.security.KeyPair; 44 import java.security.NoSuchAlgorithmException; 45 import java.security.NoSuchProviderException; 46 import java.security.SignatureException; 47 import java.security.cert.CertificateException; 48 import java.security.cert.X509Certificate; 49 import java.time.LocalDateTime; 50 import java.time.ZoneId; 51 import java.util.Date; 52 53 /** 54 * Builder pattern to build certification. 55 * 56 * @since 2021/12/28 57 */ 58 public class CertBuilder { 59 /** 60 * Logger. 61 */ 62 private static final LogUtils LOGGER = new LogUtils(ServiceApi.class); 63 64 /** 65 * issuer keyPair. 66 */ 67 private final KeyPair keyPair; 68 69 /** 70 * CertificateBuilder. 71 */ 72 private final X509v3CertificateBuilder x509v3CertificateBuilder; 73 74 /** 75 * CertBuilder. 76 * 77 * @param keyPair keyPair 78 * @param issuer issuer 79 * @param csr csr 80 * @param certExpire certExpire 81 */ CertBuilder(KeyPair keyPair, X500Name issuer, byte[] csr, long certExpire)82 public CertBuilder(KeyPair keyPair, X500Name issuer, byte[] csr, long certExpire) { 83 this.keyPair = keyPair; 84 LocalDateTime notBefore = LocalDateTime.now(); 85 LocalDateTime notAfter = notBefore.plusDays(certExpire); 86 87 PKCS10CertificationRequest request = null; 88 try { 89 request = new PKCS10CertificationRequest(csr); 90 } catch (IOException exception) { 91 LOGGER.debug(exception.getMessage(), exception); 92 CustomException.throwException(ERROR.IO_CSR_ERROR, SignToolErrMsg.IO_CSR_ERROR 93 .toString(exception.getMessage())); 94 } 95 x509v3CertificateBuilder = new X509v3CertificateBuilder( 96 issuer, CertUtils.randomSerial(), Date.from(notBefore.atZone(ZoneId.systemDefault()).toInstant()), 97 Date.from(notAfter.atZone(ZoneId.systemDefault()).toInstant()), 98 request.getSubject(), request.getSubjectPublicKeyInfo()); 99 try { 100 JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); 101 x509v3CertificateBuilder.addExtension(Extension.subjectKeyIdentifier, false, 102 extUtils.createSubjectKeyIdentifier(request.getSubjectPublicKeyInfo())); 103 } catch (NoSuchAlgorithmException exception) { 104 LOGGER.debug(exception.getMessage(), exception); 105 CustomException.throwException(ERROR.NOT_SUPPORT_ERROR, SignToolErrMsg.NO_SUCH_SIGNATURE 106 .toString(exception.getMessage())); 107 } catch (CertIOException exception) { 108 LOGGER.debug(exception.getMessage(), exception); 109 CustomException.throwException(ERROR.IO_CERT_ERROR, SignToolErrMsg.CERT_IO_FAILED 110 .toString(exception.getMessage())); 111 } 112 } 113 114 /** 115 * Add authorityKeyIdentifier for certificate builder. 116 * 117 * @param certLevel certLevel 118 * @return CertBuilder 119 */ withAuthorityKeyIdentifier(CertLevel certLevel)120 public CertBuilder withAuthorityKeyIdentifier(CertLevel certLevel) { 121 try { 122 JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); 123 if (certLevel == CertLevel.SUB_CA) { 124 x509v3CertificateBuilder.addExtension(Extension.authorityKeyIdentifier, false, 125 extUtils.createAuthorityKeyIdentifier(SubjectPublicKeyInfo 126 .getInstance(keyPair.getPublic().getEncoded()))); 127 } 128 } catch (NoSuchAlgorithmException exception) { 129 LOGGER.debug(exception.getMessage(), exception); 130 CustomException.throwException(ERROR.NOT_SUPPORT_ERROR, SignToolErrMsg.NO_SUCH_SIGNATURE 131 .toString(exception.getMessage())); 132 } catch (CertIOException exception) { 133 LOGGER.debug(exception.getMessage(), exception); 134 CustomException.throwException(ERROR.IO_CERT_ERROR, SignToolErrMsg.CERT_IO_FAILED 135 .toString(exception.getMessage())); 136 } 137 return this; 138 } 139 140 /** 141 * Add basicConstraints for certificate builder. 142 * 143 * @param certLevel certLevel 144 * @param basicConstraintsCritical basicConstraintsCritical 145 * @param basicConstraintsCa basicConstraintsCa 146 * @param basicConstraintsPathLen basicConstraintsPathLen 147 * @return CertBuilder 148 * @throws CertIOException CertIOException 149 */ withBasicConstraints(CertLevel certLevel, boolean basicConstraintsCritical, boolean basicConstraintsCa, Integer basicConstraintsPathLen)150 public CertBuilder withBasicConstraints(CertLevel certLevel, boolean basicConstraintsCritical, 151 boolean basicConstraintsCa, Integer basicConstraintsPathLen) 152 throws CertIOException { 153 BasicConstraints basicConstraints; 154 if (certLevel == CertLevel.END_ENTITY) { 155 basicConstraints = new BasicConstraints(basicConstraintsCritical); 156 } else { 157 if (basicConstraintsPathLen == null) { 158 basicConstraints = new BasicConstraints(basicConstraintsCa); 159 } else { 160 basicConstraints = new BasicConstraints(basicConstraintsPathLen); 161 } 162 } 163 x509v3CertificateBuilder.addExtension(Extension.basicConstraints, basicConstraintsCritical, basicConstraints); 164 return this; 165 } 166 167 /** 168 * Add keyUsages for certificate builder. 169 * 170 * @param keyUsage keyUsage 171 * @param keyUsageCritical keyUsageCritical 172 * @return CertBuilder 173 * @throws CertIOException CertIOException 174 */ withKeyUsages(KeyUsage keyUsage, boolean keyUsageCritical)175 public CertBuilder withKeyUsages(KeyUsage keyUsage, boolean keyUsageCritical) throws CertIOException { 176 x509v3CertificateBuilder.addExtension(Extension.keyUsage, keyUsageCritical, keyUsage); 177 return this; 178 } 179 180 /** 181 * Add extKeyUsages for certificate builder. 182 * 183 * @param extKeyUsages extKeyUsages 184 * @param extKeyUsageCritical extKeyUsageCritical 185 * @return CertBuilder 186 * @throws CertIOException CertIOException 187 */ withExtKeyUsages(KeyPurposeId[] extKeyUsages, boolean extKeyUsageCritical)188 public CertBuilder withExtKeyUsages(KeyPurposeId[] extKeyUsages, boolean extKeyUsageCritical) 189 throws CertIOException { 190 if (extKeyUsages != null) { 191 ExtendedKeyUsage extendedKeyUsage = new ExtendedKeyUsage(extKeyUsages); 192 x509v3CertificateBuilder.addExtension(Extension.extendedKeyUsage, extKeyUsageCritical, extendedKeyUsage); 193 } 194 return this; 195 } 196 197 /** 198 * Add signingCapabilty for certificate builder. 199 * 200 * @param signingCapabiltyBytes signingCapabiltyBytes 201 * @return CertBuilder 202 * @throws CertIOException CertIOException 203 */ withSigningCapabilty(byte[] signingCapabiltyBytes)204 public CertBuilder withSigningCapabilty(byte[] signingCapabiltyBytes) throws CertIOException { 205 ASN1ObjectIdentifier signingCapabiltyIdentifier = (new ASN1ObjectIdentifier("1.3.6.1.4.1.2011.2.376.1.3")) 206 .intern(); 207 x509v3CertificateBuilder.addExtension(signingCapabiltyIdentifier, false, signingCapabiltyBytes); 208 return this; 209 } 210 211 /** 212 * build X509Certificate. 213 * 214 * @param signAlgorithm signAlgorithm to sign 215 * @return X509Certificate 216 */ build(String signAlgorithm)217 public X509Certificate build(String signAlgorithm) { 218 ContentSigner contentSigner = CertUtils.createFixedContentSigner(keyPair.getPrivate(), signAlgorithm); 219 X509Certificate cert = null; 220 try { 221 cert = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME) 222 .getCertificate(x509v3CertificateBuilder.build(contentSigner)); 223 cert.verify(keyPair.getPublic()); 224 } catch (InvalidKeyException exception) { 225 LOGGER.debug(exception.getMessage(), exception); 226 CustomException.throwException(ERROR.KEY_ERROR, SignToolErrMsg.INVALID_KEY 227 .toString(exception.getMessage())); 228 } catch (SignatureException exception) { 229 LOGGER.debug(exception.getMessage(), exception); 230 CustomException.throwException(ERROR.SIGN_ERROR, SignToolErrMsg.SIGNATURE_FAILED 231 .toString(exception.getMessage())); 232 } catch (CertificateException exception) { 233 LOGGER.debug(exception.getMessage(), exception); 234 CustomException.throwException(ERROR.CERTIFICATE_ERROR, SignToolErrMsg.CERT_IO_FAILED 235 .toString(exception.getMessage())); 236 } catch (NoSuchAlgorithmException | NoSuchProviderException exception) { 237 LOGGER.debug(exception.getMessage(), exception); 238 CustomException.throwException(ERROR.NOT_SUPPORT_ERROR, SignToolErrMsg.NO_SUCH_SIGNATURE 239 .toString(exception.getMessage())); 240 } 241 return cert; 242 } 243 } 244