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