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.NOT_SUPPORT_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 | CertIOException exception) { 102 logger.debug(exception.getMessage(), exception); 103 CustomException.throwException(ERROR.NOT_SUPPORT_ERROR, exception.getMessage()); 104 } 105 } 106 107 /** 108 * Add authorityKeyIdentifier for certificate builder. 109 * 110 * @param certLevel certLevel 111 * @return CertBuilder 112 */ withAuthorityKeyIdentifier(CertLevel certLevel)113 public CertBuilder withAuthorityKeyIdentifier(CertLevel certLevel) { 114 try { 115 JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); 116 if (certLevel == CertLevel.SUB_CA) { 117 x509v3CertificateBuilder.addExtension(Extension.authorityKeyIdentifier, false, 118 extUtils.createAuthorityKeyIdentifier(SubjectPublicKeyInfo 119 .getInstance(keyPair.getPublic().getEncoded()))); 120 } 121 } catch (NoSuchAlgorithmException | CertIOException exception) { 122 logger.debug(exception.getMessage(), exception); 123 CustomException.throwException(ERROR.NOT_SUPPORT_ERROR, exception.getMessage()); 124 } 125 return this; 126 } 127 128 /** 129 * Add basicConstraints for certificate builder. 130 * 131 * @param certLevel certLevel 132 * @param basicConstraintsCritical basicConstraintsCritical 133 * @param basicConstraintsCa basicConstraintsCa 134 * @param basicConstraintsPathLen basicConstraintsPathLen 135 * @return CertBuilder 136 * @throws CertIOException CertIOException 137 */ withBasicConstraints(CertLevel certLevel, boolean basicConstraintsCritical, boolean basicConstraintsCa, Integer basicConstraintsPathLen)138 public CertBuilder withBasicConstraints(CertLevel certLevel, boolean basicConstraintsCritical, 139 boolean basicConstraintsCa, Integer basicConstraintsPathLen) 140 throws CertIOException { 141 BasicConstraints basicConstraints; 142 if (certLevel == CertLevel.END_ENTITY) { 143 basicConstraints = new BasicConstraints(basicConstraintsCritical); 144 } else { 145 if (basicConstraintsPathLen == null) { 146 basicConstraints = new BasicConstraints(basicConstraintsCa); 147 } else { 148 basicConstraints = new BasicConstraints(basicConstraintsPathLen); 149 } 150 } 151 x509v3CertificateBuilder.addExtension(Extension.basicConstraints, basicConstraintsCritical, basicConstraints); 152 return this; 153 } 154 155 /** 156 * Add keyUsages for certificate builder. 157 * 158 * @param keyUsage keyUsage 159 * @param keyUsageCritical keyUsageCritical 160 * @return CertBuilder 161 * @throws CertIOException CertIOException 162 */ withKeyUsages(KeyUsage keyUsage, boolean keyUsageCritical)163 public CertBuilder withKeyUsages(KeyUsage keyUsage, boolean keyUsageCritical) throws CertIOException { 164 x509v3CertificateBuilder.addExtension(Extension.keyUsage, keyUsageCritical, keyUsage); 165 return this; 166 } 167 168 /** 169 * Add extKeyUsages for certificate builder. 170 * 171 * @param extKeyUsages extKeyUsages 172 * @param extKeyUsageCritical extKeyUsageCritical 173 * @return CertBuilder 174 * @throws CertIOException CertIOException 175 */ withExtKeyUsages(KeyPurposeId[] extKeyUsages, boolean extKeyUsageCritical)176 public CertBuilder withExtKeyUsages(KeyPurposeId[] extKeyUsages, boolean extKeyUsageCritical) 177 throws CertIOException { 178 if (extKeyUsages != null) { 179 ExtendedKeyUsage extendedKeyUsage = new ExtendedKeyUsage(extKeyUsages); 180 x509v3CertificateBuilder.addExtension(Extension.extendedKeyUsage, extKeyUsageCritical, extendedKeyUsage); 181 } 182 return this; 183 } 184 185 /** 186 * Add signingCapabilty for certificate builder. 187 * 188 * @param signingCapabiltyBytes signingCapabiltyBytes 189 * @return CertBuilder 190 * @throws CertIOException CertIOException 191 */ withSigningCapabilty(byte[] signingCapabiltyBytes)192 public CertBuilder withSigningCapabilty(byte[] signingCapabiltyBytes) throws CertIOException { 193 ASN1ObjectIdentifier signingCapabiltyIdentifier = (new ASN1ObjectIdentifier("1.3.6.1.4.1.2011.2.376.1.3")) 194 .intern(); 195 x509v3CertificateBuilder.addExtension(signingCapabiltyIdentifier, false, signingCapabiltyBytes); 196 return this; 197 } 198 199 /** 200 * build X509Certificate. 201 * 202 * @param signAlgorithm signAlgorithm to sign 203 * @return X509Certificate 204 */ build(String signAlgorithm)205 public X509Certificate build(String signAlgorithm) { 206 ContentSigner contentSigner = CertTools.createFixedContentSigner(keyPair.getPrivate(), signAlgorithm); 207 X509Certificate cert = null; 208 try { 209 cert = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME) 210 .getCertificate(x509v3CertificateBuilder.build(contentSigner)); 211 cert.verify(keyPair.getPublic()); 212 } catch (CertificateException | NoSuchAlgorithmException | SignatureException 213 | InvalidKeyException | NoSuchProviderException exception) { 214 logger.debug(exception.getMessage(), exception); 215 CustomException.throwException(ERROR.NOT_SUPPORT_ERROR, exception.getMessage()); 216 } 217 return cert; 218 } 219 } 220